Merge "Adding new team members working on the launcher" am: e5584cb882 am: 8dfa6bd01f am: bb008f6187
Original change: https://android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/1626521
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Idfd0620ef0fac95143e225c2006ea5e51891cc21
diff --git a/Android.bp b/Android.bp
index 50c8556..002f6fe 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,11 +36,11 @@
"androidx.test.runner",
"androidx.test.rules",
"androidx.test.uiautomator_uiautomator",
+ "androidx.preference_preference",
"SystemUISharedLib",
],
srcs: [
"tests/tapl/**/*.java",
- "src/com/android/launcher3/util/SecureSettingsObserver.java",
"src/com/android/launcher3/ResourceUtils.java",
"src/com/android/launcher3/testing/TestProtocol.java",
],
@@ -52,19 +52,37 @@
name: "launcher_log_protos_lite",
srcs: [
"protos/*.proto",
- "proto_overrides/*.proto",
+ "protos_overrides/*.proto",
],
sdk_version: "current",
proto: {
type: "lite",
local_include_dirs:[
"protos",
- "proto_overrides",
+ "protos_overrides",
],
},
static_libs: ["libprotobuf-java-lite"],
}
+java_library_static {
+ name: "launcher_quickstep_log_protos_lite",
+ srcs: [
+ "quickstep/protos_overrides/*.proto",
+ ],
+ sdk_version: "current",
+ proto: {
+ type: "lite",
+ local_include_dirs:[
+ "quickstep/protos_overrides",
+ ],
+ },
+ static_libs: [
+ "libprotobuf-java-lite",
+ "launcher_log_protos_lite"
+ ],
+}
+
java_library {
name: "LauncherPluginLib",
@@ -75,3 +93,93 @@
sdk_version: "current",
min_sdk_version: "28",
}
+
+//
+// Build rule for Launcher3 dependencies lib.
+//
+android_library {
+ name: "Launcher3CommonDepsLib",
+ static_libs: [
+ "androidx.recyclerview_recyclerview",
+ "androidx.dynamicanimation_dynamicanimation",
+ "androidx.preference_preference",
+ "androidx.slice_slice-view",
+ "iconloader_base",
+ "LauncherPluginLib",
+ "launcher_quickstep_log_protos_lite"
+ ],
+ srcs: [
+ "src_build_config/**/*.java",
+ ],
+ resource_dirs: ["res"],
+ optimize: {
+ enabled: false,
+ },
+ sdk_version: "current",
+ min_sdk_version: "26",
+ manifest: "AndroidManifest-common.xml",
+}
+
+//
+// Build rule for Launcher3 app.
+//
+android_app {
+ name: "Launcher3",
+
+ static_libs: [
+ "Launcher3CommonDepsLib",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src_shortcuts_overrides/**/*.java",
+ "src_ui_overrides/**/*.java",
+ "ext_tests/src/**/*.java",
+ ],
+ resource_dirs: [
+ "ext_tests/res",
+ ],
+ optimize: {
+ proguard_flags_files: ["proguard.flags"],
+ // Proguard is disable for testing. Derivarive prjects to keep proguard enabled
+ enabled: false,
+ },
+
+ sdk_version: "current",
+ min_sdk_version: "26",
+ target_sdk_version: "29",
+ privileged: true,
+ system_ext_specific: true,
+
+ overrides: [
+ "Home",
+ "Launcher2",
+ ],
+ required: ["privapp_whitelist_com.android.launcher3"],
+
+ jacoco: {
+ include_filter: ["com.android.launcher3.**"],
+ },
+ additional_manifests: [
+ "AndroidManifest-common.xml",
+ ],
+}
+
+//
+// Launcher Robolectric test target.
+//
+java_library {
+ name: "Launcher3TestCommon",
+ libs: [
+ "Launcher3CommonDepsLib",
+ ],
+ srcs: [
+ "src/**/*.java",
+ "src_shortcuts_overrides/**/*.java",
+ "src_ui_overrides/**/*.java",
+ "ext_tests/src/**/*.java",
+ "tests/src_common/**/*.java",
+ ],
+ target_sdk_version: "29",
+ sdk_version: "current",
+ min_sdk_version: "26",
+}
diff --git a/Android.mk b/Android.mk
index 2c59550..89870a6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -17,83 +17,6 @@
LOCAL_PATH := $(call my-dir)
#
-# Build rule for Launcher3 dependencies lib.
-#
-include $(CLEAR_VARS)
-LOCAL_USE_AAPT2 := true
-LOCAL_AAPT2_ONLY := true
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_STATIC_ANDROID_LIBRARIES := \
- androidx.recyclerview_recyclerview \
- androidx.dynamicanimation_dynamicanimation \
- androidx.preference_preference \
- iconloader_base
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- LauncherPluginLib \
- launcher_log_protos_lite
-
-LOCAL_SRC_FILES := \
- $(call all-proto-files-under, protos) \
- $(call all-proto-files-under, proto_overrides) \
- $(call all-java-files-under, src_build_config) \
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_PROTOC_OPTIMIZE_TYPE := nano
-LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ --proto_path=$(LOCAL_PATH)/proto_overrides/
-LOCAL_PROTO_JAVA_OUTPUT_PARAMS := enum_style=java
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
-LOCAL_MODULE := Launcher3CommonDepsLib
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/NOTICE
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_MANIFEST_FILE := AndroidManifest-common.xml
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-#
-# Build rule for Launcher3 app.
-#
-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_shortcuts_overrides) \
- $(call all-java-files-under, src_ui_overrides) \
- $(call all-java-files-under, ext_tests/src)
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-# Proguard is disable for testing. Derivarive prjects to keep proguard enabled
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
-LOCAL_PACKAGE_NAME := Launcher3
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_SYSTEM_EXT_MODULE := true
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2
-LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
-
-LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
-
-LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
-
-include $(BUILD_PACKAGE)
-
-#
# Build rule for Launcher3 Go app for Android Go devices.
#
include $(CLEAR_VARS)
@@ -111,7 +34,7 @@
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
+LOCAL_MIN_SDK_VERSION := 26
LOCAL_PACKAGE_NAME := Launcher3Go
LOCAL_PRIVILEGED_MODULE := true
LOCAL_SYSTEM_EXT_MODULE := true
@@ -136,9 +59,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUI-statsd \
- SystemUISharedLib \
- launcherprotosnano \
- launcher_log_protos_lite
+ SystemUISharedLib
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -155,12 +76,11 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
- $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
$(call all-java-files-under, src_shortcuts_overrides)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
- $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
+ $(LOCAL_PATH)/quickstep/overview_ui_overrides/res
LOCAL_PROGUARD_ENABLED := disabled
@@ -191,7 +111,7 @@
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
- $(LOCAL_PATH)/quickstep/recents_ui_overrides/res
+ $(LOCAL_PATH)/quickstep/overview_ui_overrides/res
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
@@ -212,9 +132,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
SystemUI-statsd \
- SystemUISharedLib \
- launcherprotosnano \
- launcher_log_protos_lite
+ SystemUISharedLib
ifneq (,$(wildcard frameworks/base))
LOCAL_PRIVATE_PLATFORM_APIS := true
else
@@ -226,13 +144,14 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, quickstep/src) \
- $(call all-java-files-under, quickstep/recents_ui_overrides/src) \
- $(call all-java-files-under, go/src)
+ $(call all-java-files-under, go/src) \
+ $(call all-java-files-under, go/quickstep/src)
LOCAL_RESOURCE_DIR := \
$(LOCAL_PATH)/quickstep/res \
- $(LOCAL_PATH)/quickstep/recents_ui_overrides/res \
- $(LOCAL_PATH)/go/res
+ $(LOCAL_PATH)/go/res \
+ $(LOCAL_PATH)/go/quickstep/res \
+ $(LOCAL_PATH)/go/quickstep/overview_ui_overrides/res
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_PROGUARD_ENABLED := full
@@ -245,7 +164,7 @@
LOCAL_FULL_LIBS_MANIFEST_FILES := \
$(LOCAL_PATH)/go/AndroidManifest.xml \
- $(LOCAL_PATH)/quickstep/AndroidManifest-launcher.xml \
+ $(LOCAL_PATH)/go/AndroidManifest-launcher.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
LOCAL_MANIFEST_FILE := quickstep/AndroidManifest.xml
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 77f2428..4fd2e40 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -29,13 +29,8 @@
at compile time. Note that the components defined in AndroidManifest.xml are also required,
with some minor changed based on the derivative app.
-->
- <permission
- android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="dangerous"
- android:label="@string/permlab_install_shortcut"
- android:description="@string/permdesc_install_shortcut" />
+ <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
@@ -45,9 +40,8 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
-
- <!-- TODO(b/150802536): Enabled only for ENABLE_FIXED_ROTATION_TRANSFORM feature flag -->
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
+ <!-- for rotating surface by arbitrary degree -->
+ <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
<!--
Permissions required for read/write access to the workspace data. These permission name
@@ -82,26 +76,18 @@
android:restoreAnyVersion="true"
android:supportsRtl="true" >
- <!-- Intent received used to install shortcuts from other applications -->
- <receiver
- android:name="com.android.launcher3.InstallShortcutReceiver"
- android:permission="com.android.launcher.permission.INSTALL_SHORTCUT"
- android:enabled="@bool/enable_install_shortcut_api" >
- <intent-filter>
- <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
- </intent-filter>
- </receiver>
-
<!-- Intent received when a session is committed -->
<receiver
- android:name="com.android.launcher3.SessionCommitReceiver" >
+ android:name="com.android.launcher3.SessionCommitReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="android.content.pm.action.SESSION_COMMITTED" />
</intent-filter>
</receiver>
<!-- Intent received used to initialize a restored widget -->
- <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
+ <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver"
+ android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
</intent-filter>
@@ -116,7 +102,7 @@
<service
android:name="com.android.launcher3.notification.NotificationListener"
android:label="@string/notification_dots_service_title"
- android:enabled="@bool/notification_dots_enabled"
+ android:exported="true"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
@@ -130,6 +116,7 @@
android:theme="@style/AppItemActivityTheme"
android:excludeFromRecents="true"
android:autoRemoveFromRecents="true"
+ android:exported="true"
android:label="@string/action_add_to_workspace" >
<intent-filter>
<action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
@@ -165,6 +152,7 @@
android:name="com.android.launcher3.settings.SettingsActivity"
android:label="@string/settings_button_text"
android:theme="@style/HomeSettingsTheme"
+ android:exported="true"
android:autoRemoveFromRecents="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES" />
@@ -187,6 +175,7 @@
android:name="com.android.launcher3.secondarydisplay.SecondaryDisplayLauncher"
android:theme="@style/AppTheme"
android:launchMode="singleTop"
+ android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b031ffb..1fad72d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="26"/>
<!--
Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
Refer comments around specific entries on how to extend individual components.
@@ -49,10 +49,11 @@
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
+ android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/OWNERS b/OWNERS
index 26ba1f0..a9dae6f 100644
--- a/OWNERS
+++ b/OWNERS
@@ -34,6 +34,7 @@
peanutbutter@google.com
xuqiu@google.com
sreyasr@google.com
+thiruram@google.com
per-file FeatureFlags.java, globs = set noparent
per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com
diff --git a/buglist.txt b/buglist.txt
new file mode 100644
index 0000000..53dcc35
--- /dev/null
+++ b/buglist.txt
@@ -0,0 +1,42 @@
+171450807
+170675311
+170338029
+170338170
+160544577
+171171594
+170488559
+171131394
+171131394
+171026321
+170648272
+170752716
+170611866
+170702596
+170487752
+170665892
+168608912
+170636685
+169771796
+141126144
+166614700
+168805872
+170263425
+169221288
+143965596
+169221287
+167259591
+156044202
+169438169
+164926736
+168653219
+169963211
+170121063
+169988381
+169980192
+169221288
+169385783
+168167693
+169796517
+169330678
+168818961
+168608912
diff --git a/buglist_unique.txt b/buglist_unique.txt
new file mode 100644
index 0000000..93dbefb
--- /dev/null
+++ b/buglist_unique.txt
@@ -0,0 +1,39 @@
+141126144
+143965596
+156044202
+160544577
+164926736
+166614700
+167259591
+168167693
+168608912
+168653219
+168805872
+168818961
+169221287
+169221288
+169330678
+169385783
+169438169
+169771796
+169796517
+169963211
+169980192
+169988381
+170121063
+170263425
+170338029
+170338170
+170487752
+170488559
+170611866
+170636685
+170648272
+170665892
+170675311
+170702596
+170752716
+171026321
+171131394
+171171594
+171450807
diff --git a/buglist_with_title.txt b/buglist_with_title.txt
new file mode 100644
index 0000000..aa8b413
--- /dev/null
+++ b/buglist_with_title.txt
@@ -0,0 +1,24 @@
+144170434 twickham P1 FIXED Improve Overview -> Home transition ----
+149934536 twickham P2 FIXED Update gesture nav pullback logic ----
+154951045 peanutbutter P1 FIXED Odd animation occuring at times when swiping to home ----
+154964045 awickham P2 FIXED "Clear all" text is not in the middle of app's window vertically ----
+158701272 twickham P4 FIXED Discontinuities when long-swiping to home ----
+160361464 tracyzhou P2 FIXED Place launcher above the target app in live tile mode ----
+160568387 twickham P2 FIXED Can't get to app switcher by swiping up (motion pause not detected) ----
+160718310 xuqiu P1 FIXED With "Select" overview action selected, App icon is missing in other overview apps after orientation change ----
+160748731 sunnygoyal P2 ASSIGNED Unify prediction model with Launcher model ----
+160759508 twickham P2 FIXED Swipe up cannot back to home screen in overview. ----
+161273376 xuqiu P2 FIXED [Overview Actions] Add logging and helpful messages ----
+161536946 twickham P2 FIXED Haptics don't indicate snap-to in overview, ----
+161685099 winsonc P2 FIXED Screen still stay at the quick settings/notification when I swipe up with 3 finger to check the all apps. ----
+161801331 hyunyoungs P2 FIXED Change AllAppsSearch plugin to support only data fetch ----
+161901771 xuqiu P1 FIXED Overlapping layer of highlights with app layout getting darker when keep rotating the device from "Feedback" viewpoint in split screen ----
+161939759 sunnygoyal P2 FIXED RD1A: Going to overview in landscape mode clips the screen content ----
+162012217 perumaal P2 ASSIGNED Leaked Activity Caused by Gleams ----
+162454040 bookatz P2 ASSIGNED Create multiuser test that checks that opening an app works properly ----
+162480567 sfufa P4 FIXED Enable Item Decorations for search items ----
+162564471 tracyzhou P2 FIXED [Live tile] Handle tapping overview actions in live tile mode ----
+162623012 zakcohen P1 ASSIGNED Enable chips flag ----
+162812884 winsonc P2 ASSIGNED [R]The color have not changed in some page after turning on the dark theme. ----
+162861289 hyunyoungs P2 FIXED Add FocusIndicator support to DEVICE_SEARCH feature in S ----
+162871508 sfufa P2 ASSIGNED Introduce support for Hero app section ----
diff --git a/build.gradle b/build.gradle
index 534ca65..a7eef13 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,8 +81,7 @@
java.srcDirs = ['src', 'src_plugins']
manifest.srcFile 'AndroidManifest-common.xml'
proto {
- srcDir 'protos/'
- srcDir 'proto_overrides/'
+ srcDirs = ['protos/', 'protos_overrides/']
}
}
@@ -150,7 +149,6 @@
implementation "androidx.preference:preference:${ANDROID_X_VERSION}"
implementation project(':IconLoader')
withQuickstepImplementation project(':SharedLibWrapper')
- implementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/libs", include: 'launcher_protos.jar')
// Recents lib dependency
withQuickstepImplementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
@@ -171,18 +169,14 @@
protobuf {
// Configure the protoc executable
protoc {
- artifact = 'com.google.protobuf:protoc:3.0.0'
-
- generateProtoTasks {
- all().each { task ->
- task.builtins {
- remove java
- javanano {
- option "java_package=launcher_log_extension.proto|com.android.launcher3.userevent.nano"
- option "java_package=launcher_log.proto|com.android.launcher3.userevent.nano"
- option "java_package=launcher_dump.proto|com.android.launcher3.model.nano"
- option "enum_style=java"
- }
+ artifact = "com.google.protobuf:protoc:${protocVersion}"
+ }
+ generateProtoTasks {
+ all().each { task ->
+ task.builtins {
+ remove java
+ java {
+ option "lite"
}
}
}
diff --git a/commitlist.txt b/commitlist.txt
new file mode 100644
index 0000000..27b8bac
--- /dev/null
+++ b/commitlist.txt
@@ -0,0 +1,934 @@
+[34mCOMMAND>> git log f99351888c3e5a128559678304fefd647472bc7f..4c3952dc60fc78d3816012a86d7e71747ef34c74[m
+commit 4c3952dc60fc78d3816012a86d7e71747ef34c74
+Merge: cb403d9e5 70e8b1572
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Fri Oct 23 00:27:39 2020 +0000
+
+ Merge "Minor stylistic changes in Workspace.java." into ub-launcher3-master
+
+commit cb403d9e5235c7323bc2fdffe6a264d17bb6d0a6
+Author: Pinyao Ting <pinyaoting@google.com>
+Date: Thu Oct 22 16:07:08 2020 -0700
+
+ flip default value of minimal device feature flag
+
+ Test: manual
+ Change-Id: Iaf46dffb935bdf4b46e7c57d547bdc697250ec56
+
+commit a97557a15eb111616d868120a9f4659f1b451fa2
+Merge: f5ce80b8a 932a327eb
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Thu Oct 22 18:22:56 2020 +0000
+
+ Merge "Consider overscroll adjustment of RecentsView for live tile" into ub-launcher3-master
+
+commit 932a327ebf0587b8324b9fea7d31328b2f6719a8
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Wed Oct 21 23:29:00 2020 -0700
+
+ Consider overscroll adjustment of RecentsView for live tile
+
+ Fixes: 171450807
+ Test: manual
+ Change-Id: I83eebf1f6b61c67f289db51aabe5a971815d0df1
+
+commit f5ce80b8a0a1636fc8159475177a07b281492c88
+Author: Hilary Huo <hhuo@google.com>
+Date: Wed Oct 14 16:35:55 2020 -0700
+
+ [pixel-search] Latency analysis, add logging statement in launcher
+
+ Bug: b/170675311
+ Change-Id: I229ace399085bea1c3f9535eb713edd329dff8bd
+
+commit 31b03941ef3aa17edc08c1b509d4fa23766f2d2c
+Merge: e0a50c9e3 0731273d5
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Wed Oct 21 20:03:57 2020 +0000
+
+ Merge "Track live tile better by considering resistance animation" into ub-launcher3-master
+
+commit 0731273d5409149fca32dfb2ad76eab45f6ea79a
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Wed Oct 21 12:03:40 2020 -0700
+
+ Track live tile better by considering resistance animation
+
+ Fixes: 170338029
+ Test: Manual
+ Change-Id: I66536bae567aa94385d5e0352cec9d46d512927a
+
+commit e0a50c9e3f1d4b9f113d6afae01ff2c4ed452fba
+Merge: d2c27a595 acfac6187
+Author: Alex Chau <alexchau@google.com>
+Date: Wed Oct 21 17:02:57 2020 +0000
+
+ Merge "Use Diplay.getMetrics in DisplayController" into ub-launcher3-master
+
+commit d2c27a595065d43bbea37dd2a512d37080f5233e
+Merge: ff8febabb 8b488ccc2
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Wed Oct 21 07:19:48 2020 +0000
+
+ Merge "[Live Tile] Support launching running task animation" into ub-launcher3-master
+
+commit 8b488ccc2e433a708c8b06f0b6866f2a305e4b0a
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Wed Oct 14 12:13:04 2020 -0700
+
+ [Live Tile] Support launching running task animation
+
+ Fixes: 170338170
+ Test: manual
+ Change-Id: I2526b7cfbacaea7899b8e2ed233f913630071d36
+
+commit 70e8b157219e9090ba5e47fdfa51b2b92e98449d
+Author: Andy Wickham <awickham@google.com>
+Date: Wed Oct 7 23:00:06 2020 -0700
+
+ Minor stylistic changes in Workspace.java.
+
+ Change-Id: Ib07611f27cbc427d11abccd8b74ea144485752f7
+
+commit acfac6187dd9d13d55b566a77a5da867a1813573
+Author: Alex Chau <alexchau@google.com>
+Date: Mon Oct 19 18:00:39 2020 +0100
+
+ Use Diplay.getMetrics in DisplayController
+
+ - This is a workaround of b/163815566, where DisplayMetrics is stale
+ when onDisplayChanged is called.
+ - Instead of relying on stale DisplayConext, get the DisplayMetrics
+ from the Display directly.
+ - Also optimized how DisplayController.Info is created by passing in
+ Display only
+ - Use mDisplayContext.getDisplay directly if availalbe
+
+ Bug: 163815566, 160544577
+ Test: DPI looks correct on device boot
+ Change-Id: I2a7454bb8cf2073ce592e8662781b87fc998444f
+ (cherry picked from commit 177c38243dc3bf245d1f7db3c265dfb56522f441)
+
+commit ff8febabb039a3c27ee068f85119860a048b917c
+Merge: b03d2b416 102746823
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Tue Oct 20 17:46:09 2020 +0000
+
+ Merge "Makes Plugin Settings gear adjust to dark mode." into ub-launcher3-master
+
+commit b03d2b41616d479ba360fa4f97e57722c7f57b8e
+Merge: fb79f5541 caa1e9c39
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Tue Oct 20 15:25:56 2020 +0000
+
+ Merge "Search query method should support multiple consumers" into ub-launcher3-master
+
+commit fb79f5541dcbe587002756bb40a3c632d38cc25a
+Merge: f6b05068d cf0b275a4
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date: Tue Oct 20 13:51:06 2020 +0000
+
+ Merge "Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent." into ub-launcher3-master
+
+commit f6b05068d901d4e989b2e107c06f9c7a6e7b113f
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Tue Oct 20 00:19:29 2020 -0700
+
+ Invert the badging
+
+ Bug: 171171594
+ Change-Id: If84fdc03254105c843e16f39f479505b16e1cd5f
+
+commit caa1e9c39978cb3b467b5ac441eb39b5e883fa2e
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Mon Oct 12 13:56:02 2020 -0700
+
+ Search query method should support multiple consumers
+
+ Bug: 170488559
+ Change-Id: I64bef9523d3c3950c4ca3a4b9ce1d506d1672200
+
+commit 10274682339bb60cb24c50536b4f48f921970f3c
+Author: Andy Wickham <awickham@google.com>
+Date: Mon Oct 19 19:06:52 2020 -0700
+
+ Makes Plugin Settings gear adjust to dark mode.
+
+ It wasn't visible in dark mode before because it was
+ black on black. This makes it adjust automatically.
+
+ Change-Id: I5176cffc01842509ddafc4f30ff5029a0c4b8050
+
+commit 744a0fbeae8efaa942d21c61e25012d86f5ff81e
+Merge: 29c79947e 71f24588c
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 19 22:17:26 2020 +0000
+
+ Merge "Call click event on IME quick select for SearchResultIcon" into ub-launcher3-master
+
+commit 29c79947ecf82f662d02004ba9a7289017fc0783
+Merge: 13a2a010d a68ac3e5d
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 19 18:42:05 2020 +0000
+
+ Merge "Removing condition for CUJ tracing/metrics" into ub-launcher3-master
+
+commit 71f24588c0a66449a0c68bcb360a8c671914ce75
+Author: Samuel Fufa <sfufa@google.com>
+Date: Mon Oct 19 10:19:31 2020 -0700
+
+ Call click event on IME quick select for SearchResultIcon
+
+ Bug: 171131394
+ Change-Id: I8a703e8d0ca10570e3f774510610d3fb4c0eaab8
+
+commit 13a2a010decd87eeaf8932430c692f587d2de165
+Author: Samuel Fufa <sfufa@google.com>
+Date: Sun Oct 18 21:19:57 2020 -0700
+
+ Handle IME event for SearchResultIcon
+
+ Bug: 171131394
+ Test: Manual
+ Change-Id: I2ed1c61053c78aaecc3324418229d69634a72ae4
+
+commit 1f79eeda76246534697e92740defc7f73c3c8d14
+Author: Samuel Fufa <sfufa@google.com>
+Date: Fri Oct 16 02:01:31 2020 -0700
+
+ Remove hardcoded itemTypes from SearchTarget
+
+ - Introduces componentName and userHandle members to SearchTarget
+ - SearchTargetEvent now has searchTarget member
+ - Builder pattern for SearchTarget and SearchTargetEvent
+ - Search backend should add headers manually instead of launcher inferring sections
+
+ Bug: 171026321
+ Test: Manual
+ Change-Id: I28e0455e82b925277a17703b9aa061c8f9f15262
+
+commit a68ac3e5dd23095cea7c872c0ff1c5042d1695ba
+Author: vadimt <vadimt@google.com>
+Date: Fri Oct 16 10:48:28 2020 -0700
+
+ Removing condition for CUJ tracing/metrics
+
+ Is doesn't reflect whether jank monitors is collecting metrics,
+ which will eventually be always true anyways.
+
+ Change-Id: Iaebdc838ed2b2cebd32c8c48d7e45bdd93f76fb4
+
+commit 9228ff53c2fb26850b7bd92d86214a6aaebb11d3
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Mon Oct 12 13:43:51 2020 -0700
+
+ Trimming activity and task label
+
+ Bug: 170648272
+ Change-Id: Icd099acee65305e0aa0f98a2a301a0df8a27cf07
+
+commit 7a09177e500a53205f9969bb6cbd4251d54e8fde
+Merge: 37ed5ead3 314761a80
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Thu Oct 15 22:36:14 2020 +0000
+
+ Merge "Setup SearchResultIcon for single cell results" into ub-launcher3-master
+
+commit 37ed5ead391df5747003b2d3a345be0347362f19
+Merge: d5bbe6809 702ed2788
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Thu Oct 15 22:06:12 2020 +0000
+
+ Merge "Fix the issue where shortcuts are removed in minimal device mode" into ub-launcher3-master
+
+commit 314761a80819a6e64a136161f51eebb0f0528c4d
+Author: Samuel Fufa <sfufa@google.com>
+Date: Wed Oct 14 10:15:07 2020 -0700
+
+ Setup SearchResultIcon for single cell results
+
+ SearchResultIcon will be able to render apps, shortcuts and remote actions. It can also handle its own focused state drawing.
+
+ Screenshot: https://screenshot.googleplex.com/C3KgjJtLQTBPgaf
+
+ Bug: 170752716
+ Test: Manual
+ Change-Id: I460a9c128ea3f5814784e342c5d5fa5b7e310882
+
+commit 702ed2788678ac744c768aad6a6302e7cf91a26b
+Author: Pinyao Ting <pinyaoting@google.com>
+Date: Wed Oct 14 11:17:04 2020 -0700
+
+ Fix the issue where shortcuts are removed in minimal device mode
+
+ When loading the workspace, Launcher pins/unpins shortcuts in comply
+ with the loaded workspace. Since minimal device mode creates a mostly
+ empty workspace, existing shortcuts are getting unpinned as a result.
+
+ To mitigate the issue this CL compares the db name and only invoke
+ sanitizeData when it matches the one defined in InvariantDeviceProfile.
+
+ Bug: 170611866
+ Test: manual
+ 1. add some deep shortcut in workspace (e.g. long tap on chrome, drag
+ "incognito tab" to workspace)
+ 2. opt-in to sunshine fishfood (g/sunshine-teamfood)
+ 3. enable bedtime mode with minimal device in Settings -> Digital
+ Wellbeing -> Show Your Data -> Bedtime mode -> Customize -> minimal
+ device
+ 4. toggle bedtime mode, wait for apps in minimal device to show, then
+ toggle off bedtime mode
+ 5. verify the deep shortcut still exist
+
+ Change-Id: Ie18216ecb288e7481aa2404c4cb3ea418aee85cb
+
+commit cf0b275a48d3c9f91a346f7fc24b9604f6dde25a
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date: Tue Oct 6 09:33:40 2020 -0400
+
+ Add the ability to specify a list of tutorial steps in the gesture sandbox tutorial intent.
+
+ Added tutorial_steps string array in the intent to allow specifying an ordered list of tutorial steps.
+
+ Change-Id: Ic42a65598a74a64f8441a22f58c6cd988a5762e3
+
+commit d5bbe6809dcc056fbfc307909b171651f0fb3044
+Author: Samuel Fufa <sfufa@google.com>
+Date: Wed Oct 14 15:39:38 2020 -0700
+
+ Rename shrotcut container to deep-shrotcuts
+
+ Change-Id: If94f0dfa447235f3b1a652f7b6c749695b42d97c
+
+commit 26c1105fa04c2bcc156051e51df90a6a253349bb
+Author: Samuel Fufa <sfufa@google.com>
+Date: Tue Oct 13 01:12:03 2020 -0700
+
+ [search api part 1] Setup centralized SearchEventTracker
+
+ - Rename AdapterItemWIthPayload to SearchAdapterItem, PayloadResultHandler to SearchTargetHandler
+ - Setup SliceViewWrapper for self contained slices
+
+ Bug: 170702596
+ Change-Id: I0baf984ec8123c95011abcc17372f8d055e98ad7
+
+commit 057f2d0d7df67e3680e479ac76b48b30d8bcf884
+Merge: 4bb65ff51 9a6145efb
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Tue Oct 13 01:57:47 2020 +0000
+
+ Merge "Introduce shortcut container for hotseat event reporting" into ub-launcher3-master
+
+commit 4bb65ff516c6d9a429971ab7e04780792d5cb751
+Merge: 69740e62b 2afcab804
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Tue Oct 13 00:07:36 2020 +0000
+
+ Merge "Search UI clean up" into ub-launcher3-master
+
+commit 2afcab804b638ff3b9da5bad40c8f70bdcaae78d
+Author: Samuel Fufa <sfufa@google.com>
+Date: Mon Oct 12 15:38:14 2020 -0700
+
+ Search UI clean up
+
+ - Resolve spacing issue when work profile is installed
+ - Cache play icons and use icon shape
+ - Only draw focus indicator for the first result
+
+ Bug: 170487752
+ Bug: 170665892
+ Change-Id: I864d2e796786637132e127ef9b418c0a76c74d6e
+
+commit 69740e62be3800fc918648009645f7a8e52cb73d
+Merge: 2d7bfc878 979da64d8
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 12 20:53:29 2020 +0000
+
+ Merge "Add app start source info of apps launched from launcher" into ub-launcher3-master
+
+commit 2d7bfc8782e9ed01178672aeb09ba2a6a07f4f4c
+Author: Jon Miranda <jonmiranda@google.com>
+Date: Mon Oct 12 12:09:22 2020 -0700
+
+ Fix shadowRadius not being used in swipe up animation.
+
+ Bug: 168608912
+ Change-Id: I08f7bb057237e5061d5f1fc29afb488b204ee385
+
+commit a433fe1fb34715efb38ed094f39da49fce8cd51e
+Merge: 2de606fe7 0471b9836
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Mon Oct 12 18:08:35 2020 +0000
+
+ Merge "Using FrameCallbacks instead of windowCallbacks for surface removal" into ub-launcher3-master
+
+commit 9a6145efb85f2bbdaccc07166a55e22c15fe27db
+Author: Samuel Fufa <sfufa@google.com>
+Date: Mon Oct 12 09:33:00 2020 -0700
+
+ Introduce shortcut container for hotseat event reporting
+
+ Bug: 170636685
+ Test: Manual
+ Change-Id: I5abeb17976bbafdc8cc74fb8b9a586d544c682fc
+
+commit 2de606fe731573c081fd2d6ba166e21ea6aa2e9c
+Author: Yogisha Dixit <ydixit@google.com>
+Date: Mon Oct 12 15:36:07 2020 +0100
+
+ Delete the minimal database to force refresh.
+
+ Bug: 169771796
+ Test: manual
+ Change-Id: Ic2188bb162f295c208346861fddc137ace19ddcb
+
+commit 0471b9836c9e382dc14bdc3abdf8502fb2b9f266
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Wed Sep 23 13:54:37 2020 -0700
+
+ Using FrameCallbacks instead of windowCallbacks for surface removal
+
+ WindowCallbacks is called during the draw pass, before the frame has
+ been sent to the surfaceFlinger. Frame callback will provide a closer
+ approximation for when the frame is actually rendered on screen.
+
+ Bug: 141126144
+ Change-Id: I62aab526c2ca24b00b5e7b312b36080f26c7b439
+
+commit 2727434c44d06882925369bf4b43687a06be4a3f
+Merge: 59f532fe9 1b9e199b3
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date: Fri Oct 9 20:09:08 2020 +0000
+
+ Merge "Fix hotseat and prediction row to allow updates when empty." into ub-launcher3-master
+
+commit 59f532fe9e2b1817c094641f3c7c517f42e4faf0
+Merge: d2bfce71f b5334e3f0
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Fri Oct 9 19:52:54 2020 +0000
+
+ Merge "Improve search section header" into ub-launcher3-master
+
+commit 979da64d8254599c332d83bf94f3f1fc3fe45fef
+Author: Riddle Hsu <riddlehsu@google.com>
+Date: Tue Sep 22 21:52:40 2020 +0800
+
+ Add app start source info of apps launched from launcher
+
+ Bug: 166614700
+ Test: Enable statsd log: "adb shell cmd stats print-logs"
+ adb logcat | grep statsd | grep "(48)"
+ The line may contain 0x100000->1[I] 0x110000->10[I]
+ that means 1=from launcher and 10=latency 10ms.
+ Change-Id: Iddaff7066b66e241ba58ec87129ddbe2c531dc7e
+ (cherry picked from commit 7bdf3574a3bff06a377b4364877687bfa7619d06)
+
+commit d2bfce71f776fd05633dfd915dfc664309274677
+Merge: ed4530fed 222afb970
+Author: Winson Chung <winsonc@google.com>
+Date: Fri Oct 9 16:39:06 2020 +0000
+
+ Merge "Comply with the ISystemUiProxy.aidl change" into ub-launcher3-master
+
+commit ed4530fedda0bf876f91d0745fc70d0f30d42991
+Merge: 692d2109a 9d4a96ed0
+Author: Winson Chung <winsonc@google.com>
+Date: Fri Oct 9 16:39:06 2020 +0000
+
+ Merge "Add latency metrics for recents gesture" into ub-launcher3-master
+
+commit 1b9e199b3d9c81c793758d96bb03e0c51c1b3fb1
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date: Thu Oct 8 15:50:22 2020 -0400
+
+ Fix hotseat and prediction row to allow updates when empty.
+
+ Rotating the screen in the homescreen empties the hotseat, however it does not get populated while it is visible to the user. The user should not be able to see an empty hotseat or prediction row if predictions are available. It should therefore be possible to populate these when they are empty even if they are visible to the user.
+
+ Change-Id: I8e5252bd29050c2cd9d443aedcb3f3e305c0e2d7
+
+commit b5334e3f07f0561808a2d6e9bba55f1e3a89191e
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Fri Oct 9 00:50:48 2020 -0700
+
+ Improve search section header
+
+ Change-Id: I47cf207f0d0ab792c0e7a47c9d1185eec087ec88
+
+commit 692d2109a6702706d24b3b819d115882f7362509
+Author: Samuel Fufa <sfufa@google.com>
+Date: Thu Oct 8 18:42:48 2020 -0700
+
+ invalidate itemDecoration on predictedRow focus draw
+
+ Change-Id: I66c731f00ae1c1292c51ff281957f05fd2d70dfa
+
+commit 8d5b118060bff7f7518a9a14c0be5d265621f14c
+Author: Samuel Fufa <sfufa@google.com>
+Date: Thu Oct 8 13:11:25 2020 -0700
+
+ Revert PredictionRow shuoldDraw check
+
+ + Show Rounded play result icons
+
+ Bug: 168805872
+ Test: Manual
+ Change-Id: I663c7f7ca1f1ac072e5e9c441deabef7c3fbd97b
+
+commit 86f8df6cf954ac27ab092b9ef8a4db3c9979c4cb
+Merge: 4d19854b2 16045060c
+Author: Hilary Huo <hhuo@google.com>
+Date: Thu Oct 8 18:43:51 2020 +0000
+
+ Merge "[pixel-search] add escape hatch" into ub-launcher3-master
+
+commit 4d19854b25a54599fe9b0ac8be9d60cf6c21d7ba
+Merge: 0827e1e32 ab9ad20be
+Author: Samuel Fufa <sfufa@google.com>
+Date: Thu Oct 8 18:40:56 2020 +0000
+
+ Merge "Search UI cleanup" into ub-launcher3-master
+
+commit 16045060c35639aea85afc572bea768d16e6c9f9
+Author: Hilary Huo <hhuo@google.com>
+Date: Thu Oct 8 10:18:41 2020 -0700
+
+ [pixel-search] add escape hatch
+
+ Change-Id: I33ffea1fc0859564955380d7d1db317293d1a2cb
+
+commit 0827e1e32a5f99fa02418dae37270c6db8c989d2
+Merge: 3463f0a87 68d7a6e5b
+Author: Andy Wickham <awickham@google.com>
+Date: Thu Oct 8 16:53:29 2020 +0000
+
+ Merge "Adds feature flag for BC Smartspace." into ub-launcher3-master
+
+commit ab9ad20be600d1cbdc6b54a491d5fbb4c2cf9c16
+Author: Samuel Fufa <sfufa@google.com>
+Date: Wed Oct 7 15:18:24 2020 -0700
+
+ Search UI cleanup
+
+ - offset all apps header padding with search input margin
+ - avoid check shouldDraw check on HeaderRow. (race condition)
+
+ Bug: 170263425
+ Change-Id: I11a1fbb448aa6afd18ec0984af9bb8b1d7600f69
+
+commit 68d7a6e5b28af8cc55bdae7efc24cc7ebee81257
+Author: Andy Wickham <awickham@google.com>
+Date: Wed Oct 7 14:27:17 2020 -0700
+
+ Adds feature flag for BC Smartspace.
+
+ Change-Id: Iaf9fb7507d0ccd004a4e00188c75dadd6a059246
+
+commit 3463f0a876ff486ce03e160134e0504158271a92
+Merge: 2470d812a 4b7f38b8f
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Wed Oct 7 20:09:04 2020 +0000
+
+ Merge "Align fallback result query with result text" into ub-launcher3-master
+
+commit 2470d812a1ae989e67781e5056b534ad9a960819
+Merge: cae7d74d8 7a6e4c931
+Author: Vadim Tryshev <vadimt@google.com>
+Date: Wed Oct 7 20:04:09 2020 +0000
+
+ Merge "Annotating Quick Switch CUJ for 3-button mode" into ub-launcher3-master
+
+commit cae7d74d898769727105850ea5473c2c0ae25fdb
+Merge: e9bf2bd14 1fddddb4f
+Author: Tony Wickham <twickham@google.com>
+Date: Wed Oct 7 18:32:48 2020 +0000
+
+ Merge "Update launcher_trace.proto for quick switch" into ub-launcher3-master
+
+commit 7a6e4c931f13b369bfa4328196b4632d6d848a19
+Author: vadimt <vadimt@google.com>
+Date: Tue Oct 6 14:09:16 2020 -0700
+
+ Annotating Quick Switch CUJ for 3-button mode
+
+ Bug: 169221288
+ Change-Id: Ief62345fe6004dde699f44aa0c90329b7cd84e8b
+
+commit 4b7f38b8fa004b514244304fcc07ff514a2fa46b
+Author: Samuel Fufa <sfufa@google.com>
+Date: Tue Oct 6 18:37:46 2020 -0700
+
+ Align fallback result query with result text
+
+ screenshot: https://screenshot.googleplex.com/6Daj5vdmz2jmznX
+ bug: 169438169
+ test: Manual
+ Change-Id: Ie621ed3c834aec5e9467607da4f685d05d152183
+
+commit 222afb970434c7972589adfc509bd2c256ca6556
+Author: Hongwei Wang <hwwang@google.com>
+Date: Fri Oct 2 13:51:36 2020 -0700
+
+ Comply with the ISystemUiProxy.aidl change
+
+ Two methods are added to support communications between Launcher and
+ SysUI when user swipes an auto PiP-able Activity to home.
+
+ Bug: 143965596
+ Test: N/A
+ Change-Id: I2c73a287a094e882bde3cd71c27f9f66ae20e64a
+ (cherry picked from commit 88ddae38db924f700082a113670ce5a719116a95)
+
+commit 9d4a96ed029fdad1e369d5eedd082938f0dc9e01
+Author: Riddle Hsu <riddlehsu@google.com>
+Date: Wed Sep 30 00:32:04 2020 +0800
+
+ Add latency metrics for recents gesture
+
+ Pass the touch down time to RecentsAnimation#startRecentsActivity.
+
+ Bug: 169221287
+ Test: Enable statsd log: "adb shell cmd stats print-logs"
+ Touch gesture navigation bar.
+ adb logcat | grep statsd | grep "(48)"
+ The line may contain 0x100000->4[I] 0x110000->20[I]
+ that means 4=by recents and 20=latency 20ms.
+ Change-Id: I81ee804895b7712f4d925736f5b4694c11a12cbe
+ (cherry picked from commit 63623967b83edad56db58173ebb6687c685b9177)
+
+commit e9bf2bd14c9a7a48f8f93687932d41b1418cf4e4
+Merge: 73ae75474 d028937e7
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Wed Oct 7 02:19:04 2020 +0000
+
+ Merge "[Live tile] Finish recents animation when the phone goes to sleep in live tile mode" into ub-launcher3-master
+
+commit 1fddddb4f30505e0fc9bb2e7c0d88b38ad900e54
+Author: Tony Wickham <twickham@google.com>
+Date: Tue Sep 29 17:29:06 2020 -0700
+
+ Update launcher_trace.proto for quick switch
+
+ Sample output from one entry:
+ entry {
+ elapsed_realtime_nanos: 440461382888540
+ launcher {
+ touch_interaction_service {
+ service_connected: true
+ overview_component_obvserver {
+ overview_activity_started: true
+ overview_activity_resumed: false
+ }
+ input_consumer {
+ name: "TYPE_OTHER_ACTIVITY:TYPE_ONE_HANDED"
+ swipe_handler {
+ gesture_state {
+ endTarget: NEW_TASK
+ }
+ is_recents_attached_to_app_window: true
+ scroll_offset: 846
+ app_to_overview_progress: 0
+ }
+ }
+ }
+ }
+ }
+
+ Bug: 167259591
+ Change-Id: I7f199d88f1d736efcea6b9165b8c4b77a5d27c58
+
+commit 73ae75474ec1dd8807d814ea6c22323905d2070c
+Merge: 8a6f3e40d 0ebbc1880
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Tue Oct 6 23:18:44 2020 +0000
+
+ Merge "Removing tracing for a gone flake" into ub-launcher3-master
+
+commit 8a6f3e40d0321217c624055db7929c397e455e0c
+Merge: e29a9f796 565ed4ff6
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Tue Oct 6 22:49:40 2020 +0000
+
+ Merge "Update Search UI" into ub-launcher3-master
+
+commit 0ebbc18803aaf8ef2f6db7d628d7ae1ce322e842
+Author: vadimt <vadimt@google.com>
+Date: Tue Oct 6 14:52:27 2020 -0700
+
+ Removing tracing for a gone flake
+
+ Bug: 156044202
+ Change-Id: Ice142bb941fee7b731f46c2073fab17d83bbc871
+
+commit 565ed4ff69b534812818a2b9aa8789a1aea210eb
+Author: Samuel Fufa <sfufa@google.com>
+Date: Wed Sep 30 10:42:07 2020 -0700
+
+ Update Search UI
+
+ [preview attached to bug]
+
+ Bug: 169438169
+ Test: Manual
+ Change-Id: I085f3dd38ac373c1afab82a637ec08715a6e0cc5
+
+commit e29a9f7961e6db0915bc028ef7e871dcb2c8bde0
+Merge: 2c5ed10ff be17bdcd2
+Author: Jayaprakash Sundararaj <jayaprakashs@google.com>
+Date: Tue Oct 6 21:00:20 2020 +0000
+
+ Merge "[Search] Add logging to People and badding as to icons." into ub-launcher3-master
+
+commit be17bdcd221f501c45876abe2249c1007858d0c0
+Author: jayaprakashs <jayaprakashs@google.com>
+Date: Mon Oct 5 09:01:52 2020 -0700
+
+ [Search] Add logging to People and badding as to icons.
+
+ Change-Id: I65948a2faca436216a94aa46139d425b8eade827
+
+commit 2c5ed10ffa1a870de35f9b3c0c558270aff498dd
+Merge: b2b65a1ef 8ed9707cf
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Tue Oct 6 18:40:57 2020 +0000
+
+ Merge "[Live Tile] Support launching another task (other than the current running task) in Overview" into ub-launcher3-master
+
+commit b2b65a1ef58b020923d112051535b6eb83b582df
+Merge: 3cf264f49 4c14f4b9e
+Author: Samuel Fufa <sfufa@google.com>
+Date: Tue Oct 6 17:45:35 2020 +0000
+
+ Merge "Avoid double search item highlight" into ub-launcher3-master
+
+commit 8ed9707cf3a4300cb61942f08f0752c80eed086b
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Mon Sep 14 23:25:37 2020 -0700
+
+ [Live Tile] Support launching another task (other than the current running task) in Overview
+
+ - Get rid of the defer cancelation logic
+ - Render animation on the task view of the task being launched upon task view appeared callback
+ - Finish the recents animation upon the end of the recents window animation
+
+ Fixes: 164926736
+ Test: manual
+ Change-Id: Ibffb6a9c74c235efc8615a22b0306551532c7b61
+
+commit 3cf264f498e37c482fa4c559bf48ffa791279585
+Author: Schneider Victor-tulias <victortulias@google.com>
+Date: Tue Sep 22 12:58:38 2020 -0700
+
+ Prevent hotseat updates if it is visible to the user.
+
+ Test: manual
+
+ Fixes: 168653219
+
+ Changing app icons under the user's finger could be disruptive. Added a checks for whether the hotseatand all apps predictions are visible and callbacks to update them when they become hidden.
+
+ Change-Id: Ib9e6e904e9f662ecfaeea6a2fe21d1d81ba39b96
+
+commit b6aff1f56d55a36256446ec3970d92e9da39b98c
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Mon Oct 5 16:08:35 2020 -0700
+
+ Fix NPE inside RecentsOrientedState
+
+ Bug: 169963211
+ Change-Id: I86dd337dc1b862f3fa99b91b47fa250076233f96
+
+commit eab40983b9a48b933bde5ca95a82ebd4d83b233d
+Merge: 83ce7c0b5 020e628f2
+Author: Jonathan Miranda <jonmiranda@google.com>
+Date: Mon Oct 5 22:20:27 2020 +0000
+
+ Merge "Add shadow radius to windows during app launch / close animations." into ub-launcher3-master
+
+commit 83ce7c0b5e461386bb92883a8d6cefe8365cd9ae
+Merge: 679d920bf d6b1f3c08
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 5 19:18:39 2020 +0000
+
+ Merge "Action icon should be used as a badge instead of main icon" into ub-launcher3-master
+
+commit 679d920bf5151cffed4e8186c12c25d8d7907af9
+Merge: e108cc609 0c943966d
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 5 19:13:50 2020 +0000
+
+ Merge "Add null check for input receiver before updating batching" into ub-launcher3-master
+
+commit e108cc609d0a7fd58f0c7e16ce45fa79be6dd272
+Merge: 470403eb5 f622e42bf
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 5 18:39:58 2020 +0000
+
+ Merge "Removing unused proto extensions" into ub-launcher3-master
+
+commit 470403eb58879380e2edac2262dc7f40327b2a15
+Merge: a5130482a 1d7ed30db
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 5 18:29:54 2020 +0000
+
+ Merge "Remove widgets that no longer fit the workspace in their current spans." into ub-launcher3-master
+
+commit 4c14f4b9eda8332347c81e0cf51c5de4dbc06399
+Author: Samuel Fufa <sfufa@google.com>
+Date: Mon Oct 5 10:50:00 2020 -0700
+
+ Avoid double search item highlight
+
+ Change-Id: Ic2e28b18f6d5e3ed32cd5646bc3bb4789c378e57
+
+commit 0c943966d373d8ae7eef2b08e88ac44bf57d8a8d
+Author: Winson Chung <winsonc@google.com>
+Date: Mon Oct 5 10:23:27 2020 -0700
+
+ Add null check for input receiver before updating batching
+
+ - A change in the system (ie. sysui crash or nav mode change) could
+ cause the input monitor to be disposed before the swipe animation
+ settles
+
+ Bug: 170121063
+ Test: Kill sysui while swiping up
+
+ Change-Id: I1417b109fecdb98fae6197c7038dbe9307470853
+
+commit a5130482aee1b0592661bc1c6e178a0de7a163da
+Merge: b21819e18 7fcd74abb
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Mon Oct 5 17:14:21 2020 +0000
+
+ Merge "Suggest result should launch Bug: 169980192" into ub-launcher3-master
+
+commit d028937e74a9ea6d36e463de4c87ed37283bbdf6
+Author: Tracy Zhou <tracyzhou@google.com>
+Date: Sat Oct 3 00:36:53 2020 -0700
+
+ [Live tile] Finish recents animation when the phone goes to sleep in live tile mode
+
+ Fixes: 169988381
+ Test: manual
+ Change-Id: Ic71d3e6767eadb6854dbd46581bf9d3242c161a4
+
+commit 7fcd74abb399100ac8243be6ca28c09cc8adc8c8
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Fri Oct 2 19:20:11 2020 -0700
+
+ Suggest result should launch
+ Bug: 169980192
+
+ Change-Id: I762245a5cc4740d093c9cb3b44a508e9e3f2b763
+
+commit b21819e181e99504c22c6ca028261a1f2665c6f9
+Merge: 931bce369 a762b0241
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Fri Oct 2 22:07:12 2020 +0000
+
+ Merge "Annotating Quick Switch CUJ for non-3-button modes" into ub-launcher3-master
+
+commit a762b02418695f5a1ff2f96586660de8c3610280
+Author: vadimt <vadimt@google.com>
+Date: Fri Oct 2 13:56:28 2020 -0700
+
+ Annotating Quick Switch CUJ for non-3-button modes
+
+ Bug: 169221288
+ Change-Id: I7145a9e28a2f0a789d19d2a0e3d15630c6e50f6a
+
+commit 931bce3697595a214023bc72923dad47a61d5711
+Merge: c935ba6b8 733e3c609
+Author: TreeHugger Robot <treehugger-gerrit@google.com>
+Date: Fri Oct 2 19:19:50 2020 +0000
+
+ Merge "Moving some initializations to the background thread" into ub-launcher3-master
+
+commit c935ba6b8a2ec163533c0b19309dacb6199e6552
+Merge: a4111f250 58804ac52
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Fri Oct 2 18:26:06 2020 +0000
+
+ Merge "Adding stats log for add item flow" into ub-launcher3-master
+
+commit 733e3c609b7653a36e58747c881458ec00d98df8
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Tue Sep 29 10:32:32 2020 -0700
+
+ Moving some initializations to the background thread
+
+ HandlerThread.getLooper blocks until the thread is ready. Instead
+ moving all looper dependency to the new thread itself.
+
+ Change-Id: I240e8c56b855a991433a7fe93875059e6dab146b
+
+commit 58804ac5257f45dddbf7a6db35cf8f369ee1e88e
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Wed Sep 16 16:27:40 2020 -0700
+
+ Adding stats log for add item flow
+
+ Bug: 169385783
+ Bug: 168167693
+ Change-Id: I37395f1b118727f67e0f14c02f945b8213b165c8
+
+commit a4111f250003328d1aef8bbaab59512208ec46cb
+Merge: 8d14dbe04 f6b72c4ad
+Author: Hilary Huo <hhuo@google.com>
+Date: Fri Oct 2 17:41:22 2020 +0000
+
+ Merge "[pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon" into ub-launcher3-master
+
+commit f622e42bf6983d3adb95386bfd6375d281f1d4f2
+Author: Sunny Goyal <sunnygoyal@google.com>
+Date: Fri Oct 2 10:35:56 2020 -0700
+
+ Removing unused proto extensions
+
+ Change-Id: I6d0319c99934dad5176b6f70b895a4ca772ec45f
+
+commit d6b1f3c086f9ac097cd03e1ee898b153478ec11a
+Author: Hyunyoung Song <hyunyoungs@google.com>
+Date: Fri Oct 2 00:26:35 2020 -0700
+
+ Action icon should be used as a badge instead of main icon
+
+ Bug: 169796517
+ Change-Id: I3f07fdc2ae6e1af463701f942c26c3ca5d836ee2
+
+commit f6b72c4ad1d2e082441a64c4d6a5a02ee8a251ca
+Author: Hilary Huo <hhuo@google.com>
+Date: Thu Oct 1 12:26:48 2020 -0700
+
+ [pixel-search] Bug fix: automatically launch screenshot + center&crop remoteaction icon
+
+ Bug: b/169330678
+ Change-Id: Id5f8a0ce6d68f7ed9e4d1ff258ee3772229eb63b
+
+commit 1d7ed30dba4b2c71fc7b0981532a872a13e5aedb
+Author: Jon Miranda <jonmiranda@google.com>
+Date: Wed Sep 23 12:15:43 2020 -0700
+
+ Remove widgets that no longer fit the workspace in their current spans.
+
+ This can happen when display size changes.
+ We compare span sizes of widget in the db to the min sizes of the widget
+ in the current display size. If the widget can no longer fit in its existing
+ spans, we remove it.
+
+ Also update test widgets to have minWidth/minHeight of 1dp. This ensures that
+ the spanX, spanY, min* values remain consistent between different test devices.
+
+ Bug: 168818961
+ Change-Id: I723372e4582658f78b2f23ced9073cb77977a6b8
+
+commit 020e628f22cc7975beab439c6da26af2f9ebc15b
+Author: Jon Miranda <jonmiranda@google.com>
+Date: Mon Sep 28 17:01:42 2020 -0700
+
+ Add shadow radius to windows during app launch / close animations.
+
+ Bug: 168608912
+ Change-Id: I2ec50b0b3711c0861659f9c641bbc05fcdeaab45
diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
index e649ce1..aa3710b 100644
--- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
+++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java
@@ -164,6 +164,12 @@
}
case TestProtocol.REQUEST_GET_TEST_EVENTS: {
+ if (sEvents == null) {
+ // sEvents can be null if Launcher died and restarted after
+ // REQUEST_START_EVENT_LOGGING.
+ return response;
+ }
+
synchronized (sEvents) {
response.putStringArrayList(
TestProtocol.TEST_INFO_RESPONSE_FIELD, new ArrayList<>(sEvents));
diff --git a/go/AndroidManifest-launcher.xml b/go/AndroidManifest-launcher.xml
new file mode 100644
index 0000000..d2575b6
--- /dev/null
+++ b/go/AndroidManifest-launcher.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.launcher3">
+ <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="25"/>
+ <!--
+ Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+ Refer comments around specific entries on how to extend individual components.
+ -->
+
+ <application
+ android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:fullBackupOnly="true"
+ android:fullBackupContent="@xml/backupscheme"
+ android:hardwareAccelerated="true"
+ android:icon="@drawable/ic_launcher_home"
+ android:label="@string/derived_app_name"
+ android:theme="@style/AppTheme"
+ android:largeHeap="@bool/config_largeHeap"
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true" >
+
+ <!--
+ Main launcher activity. When extending only change the name, and keep all the
+ attributes and intent filters the same
+ -->
+ <activity
+ android:name="com.android.launcher3.Launcher3QuickStepGo"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:windowSoftInputMode="adjustPan"
+ android:screenOrientation="unspecified"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|uiMode"
+ android:resizeableActivity="true"
+ android:resumeWhilePausing="true"
+ android:taskAffinity=""
+ android:exported="true"
+ android:enabled="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.MONKEY"/>
+ <category android:name="android.intent.category.LAUNCHER_APP" />
+ </intent-filter>
+ <meta-data
+ android:name="com.android.launcher3.grid.control"
+ android:value="${packageName}.grid_control" />
+ </activity>
+
+ </application>
+</manifest>
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
index f84a82e..f36439d 100644
--- a/go/AndroidManifest.xml
+++ b/go/AndroidManifest.xml
@@ -46,6 +46,12 @@
tools:node="replace" >
</activity>
+ <service
+ android:name="com.android.launcher3.notification.NotificationListener"
+ android:label="@string/notification_dots_service_title"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+ android:enabled="false"
+ tools:node="replace" />
</application>
</manifest>
diff --git a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
new file mode 100644
index 0000000..b438da3
--- /dev/null
+++ b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.quickstep.views.GoOverviewActionsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/overview_actions_height"
+ android:layout_gravity="center_horizontal|bottom">
+
+ <LinearLayout
+ android:id="@+id/action_buttons"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:orientation="horizontal">
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/action_listen"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/ic_listen"
+ android:drawablePadding="1dp"
+ android:text="@string/action_listen"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
+ <Button
+ android:id="@+id/action_translate"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/ic_translate"
+ android:drawablePadding="1dp"
+ android:text="@string/action_translate"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
+ <Button
+ android:id="@+id/action_search"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/ic_search"
+ android:drawablePadding="1dp"
+ android:text="@string/action_search"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
+ <Button
+ android:id="@+id/action_screenshot"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableTop="@drawable/ic_screenshot"
+ android:drawablePadding="1dp"
+ android:text="@string/action_screenshot"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/action_share"
+ style="@style/OverviewActionButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawableStart="@drawable/ic_share"
+ android:text="@string/action_share"
+ android:theme="@style/ThemeControlHighlightWorkspaceColor"
+ android:visibility="gone" />
+
+ <Space
+ android:id="@+id/oav_three_button_space"
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1"
+ android:visibility="gone" />
+ </LinearLayout>
+
+</com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/res/values-v22/styles.xml b/go/quickstep/overview_ui_overrides/res/values/config.xml
similarity index 71%
rename from res/values-v22/styles.xml
rename to go/quickstep/overview_ui_overrides/res/values/config.xml
index f86db7a..ec21a01 100644
--- a/res/values-v22/styles.xml
+++ b/go/quickstep/overview_ui_overrides/res/values/config.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
-* Copyright (C) 2018 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.
@@ -16,11 +16,7 @@
* limitations under the License.
*/
-->
-
<resources>
-
- <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
- <item name="widgetsTheme">@style/WidgetContainerTheme</item>
- </style>
-
+ <string name="task_overlay_factory_class" translatable="false">
+ com.android.quickstep.TaskOverlayFactoryGo</string>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/res/drawable/ic_listen.xml b/go/quickstep/res/drawable/ic_listen.xml
new file mode 100644
index 0000000..a8e6c93
--- /dev/null
+++ b/go/quickstep/res/drawable/ic_listen.xml
@@ -0,0 +1,32 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+ <path
+ android:pathData="M10.5,15.17c2.58,0 4.67,-2.09 4.67,-4.67s-2.09,-4.67 -4.67,-4.67c-2.58,0 -4.67,2.09 -4.67,4.67S7.92,15.17 10.5,15.17zM10.5,8.17c1.28,0 2.33,1.05 2.33,2.33s-1.05,2.33 -2.33,2.33c-1.28,0 -2.33,-1.05 -2.33,-2.33S9.22,8.17 10.5,8.17z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M10.5,17.5c-3.11,0 -9.33,1.56 -9.33,4.67v2.33h18.67v-2.33C19.83,19.06 13.62,17.5 10.5,17.5zM3.5,22.17c0.26,-0.84 3.86,-2.33 7,-2.33c3.15,0 6.77,1.5 7,2.33H3.5z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M25.67,10.5c0,0.36 -0.02,0.71 -0.05,1.05c-0.01,0.15 -0.03,0.29 -0.05,0.43c-0.02,0.18 -0.05,0.36 -0.08,0.54c-0.04,0.2 -0.07,0.39 -0.12,0.58c-0.01,0.06 -0.03,0.11 -0.04,0.17c-0.59,2.34 -1.81,4.01 -2.52,4.82c-0.09,0.1 -0.18,0.2 -0.28,0.3c-0.17,0.18 -0.27,0.27 -0.27,0.27l-1.65,-1.63c1.34,-1.33 2.27,-3.07 2.6,-5.01c0.01,-0.08 0.02,-0.16 0.04,-0.24c0.06,-0.42 0.1,-0.85 0.1,-1.29c0,-0.44 -0.04,-0.88 -0.1,-1.3c-0.01,-0.06 -0.02,-0.13 -0.03,-0.19c-0.32,-1.95 -1.25,-3.7 -2.6,-5.04l1.65,-1.63c0,0 0.11,0.1 0.27,0.27c0.09,0.1 0.19,0.2 0.28,0.3c0.71,0.82 1.93,2.48 2.52,4.82c0.01,0.06 0.03,0.11 0.04,0.17c0.04,0.19 0.08,0.38 0.12,0.58c0.03,0.18 0.06,0.36 0.08,0.54c0.02,0.14 0.04,0.28 0.05,0.43C25.65,9.79 25.67,10.14 25.67,10.5z"
+ android:fillColor="#EA4335"/>
+ <path
+ android:pathData="M20.61,8.4C20.85,9.06 21,9.76 21,10.5s-0.15,1.44 -0.39,2.1c-0.28,0.77 -0.71,1.46 -1.25,2.05l-1.66,-1.64c0.56,-0.63 0.91,-1.44 0.95,-2.34c0,-0.06 0.02,-0.11 0.02,-0.17s-0.01,-0.11 -0.02,-0.17c-0.04,-0.9 -0.39,-1.71 -0.95,-2.34l1.66,-1.64C19.9,6.94 20.32,7.63 20.61,8.4z"
+ android:fillColor="#FBBC04"/>
+</vector>
diff --git a/go/quickstep/res/drawable/ic_search.xml b/go/quickstep/res/drawable/ic_search.xml
new file mode 100644
index 0000000..4307330
--- /dev/null
+++ b/go/quickstep/res/drawable/ic_search.xml
@@ -0,0 +1,32 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+ <path
+ android:pathData="M24.5,22.75l-6.84,-6.84c1,-1.35 1.59,-3.02 1.59,-4.83h-2.33c0,3.22 -2.62,5.83 -5.83,5.83v2.33c1.81,0 3.47,-0.6 4.83,-1.59l6.84,6.84L24.5,22.75z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M11.08,2.92v2.33c3.22,0 5.83,2.62 5.83,5.83h2.33C19.25,6.57 15.59,2.92 11.08,2.92z"
+ android:fillColor="#34A853"/>
+ <path
+ android:pathData="M5.25,11.08H2.92c0,4.51 3.66,8.17 8.17,8.17v-2.33C7.87,16.92 5.25,14.3 5.25,11.08z"
+ android:fillColor="#EA4335"/>
+ <path
+ android:pathData="M2.92,11.08h2.33c0,-3.22 2.62,-5.83 5.83,-5.83V2.92C6.57,2.92 2.92,6.57 2.92,11.08z"
+ android:fillColor="#FBBC04"/>
+</vector>
diff --git a/go/quickstep/res/drawable/ic_translate.xml b/go/quickstep/res/drawable/ic_translate.xml
new file mode 100644
index 0000000..1247807
--- /dev/null
+++ b/go/quickstep/res/drawable/ic_translate.xml
@@ -0,0 +1,32 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="28dp"
+ android:viewportWidth="28"
+ android:viewportHeight="28">
+ <path
+ android:pathData="M12.28,15.19l-0.07,-0.05c-0.61,-0.49 -1.15,-1.05 -1.65,-1.63c-1.05,-1.22 -1.88,-2.63 -2.39,-4.17H5.83c0.54,2.17 1.58,4.16 3.01,5.85l-5.93,5.23l1.75,1.75l5.91,-5.26c0.05,0.04 0.1,0.09 0.15,0.13l3.42,2.79l1.02,-2.33L12.28,15.19z"
+ android:fillColor="#FBBC04"/>
+ <path
+ android:pathData="M21.58,11.67h-2.33l-5.25,14h2.33l1.31,-3.5h5.54l1.32,3.5h2.33L21.58,11.67zM18.53,19.83l1.89,-5.05l1.89,5.05H18.53z"
+ android:fillColor="#4285F4"/>
+ <path
+ android:pathData="M11.67,2.33l-2.34,0l0,2.34l-8.16,0l0,2.33l10.5,0l0,-2.33z"
+ android:fillColor="#EA4335"/>
+ <path
+ android:pathData="M11.67,4.67V7H14c-0.61,2.42 -1.79,4.65 -3.44,6.5c0.5,0.59 1.04,1.15 1.65,1.63l0.07,0.05c2.03,-2.32 3.44,-5.14 4.05,-8.19h3.5V4.67H11.67z"
+ android:fillColor="#34A853"/>
+</vector>
diff --git a/quickstep/recents_ui_overrides/res/values/config.xml b/go/quickstep/res/values/config.xml
similarity index 67%
copy from quickstep/recents_ui_overrides/res/values/config.xml
copy to go/quickstep/res/values/config.xml
index 120e034..9dca137 100644
--- a/quickstep/recents_ui_overrides/res/values/config.xml
+++ b/go/quickstep/res/values/config.xml
@@ -14,5 +14,11 @@
limitations under the License.
-->
<resources>
- <integer name="max_depth_blur_radius">150</integer>
+ <!-- The component to receive app sharing Intents -->
+ <string name="app_sharing_component" translatable="false"/>
+ <!-- The package to receive Listen, Translate, and Search Intents -->
+ <string name="niu_actions_package" translatable="false"/>
+
+ <!-- Feature Flags -->
+ <bool name="enable_niu_actions">false</bool>
</resources>
\ No newline at end of file
diff --git a/go/quickstep/res/values/strings.xml b/go/quickstep/res/values/strings.xml
new file mode 100644
index 0000000..71e2f3a
--- /dev/null
+++ b/go/quickstep/res/values/strings.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Label for app share drop target. [CHAR_LIMIT=20] -->
+ <string name="app_share_drop_target_label">Share App</string>
+
+ <!-- ******* Overview ******* -->
+ <!-- Label for a button that lets the user listen to the content of the current app. [CHAR_LIMIT=40] -->
+ <string name="action_listen">Listen</string>
+ <!-- Label for a button that translates a screenshot of the current app. [CHAR_LIMIT=40] -->
+ <string name="action_translate">Translate</string>
+ <!-- Label for a button that triggers Search on a screenshot of the current app. [CHAR_LIMIT=40] -->
+ <string name="action_search">Search</string>
+</resources>
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
new file mode 100644
index 0000000..b72e71c
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -0,0 +1,132 @@
+/*
+ * 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.launcher3;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+
+import androidx.core.content.FileProvider;
+
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.popup.SystemShortcut;
+
+import java.io.File;
+
+/**
+ * Defines the Share system shortcut and its factory.
+ * This shortcut can be added to the app long-press menu on the home screen.
+ * Clicking the button will initiate peer-to-peer sharing of the app.
+ */
+public final class AppSharing {
+ /**
+ * This flag enables this feature. It is defined here rather than in launcher3's FeatureFlags
+ * because it is unique to Go and not toggleable at runtime.
+ */
+ public static final boolean ENABLE_APP_SHARING = true;
+
+ private static final String TAG = "AppSharing";
+ private static final String FILE_PROVIDER_SUFFIX = ".overview.fileprovider";
+ private static final String APP_EXSTENSION = ".apk";
+ private static final String APP_MIME_TYPE = "application/application";
+
+ private final String mSharingComponent;
+
+ private AppSharing(Launcher launcher) {
+ mSharingComponent = launcher.getText(R.string.app_sharing_component).toString();
+ }
+
+ private boolean canShare(ItemInfo info) {
+ /**
+ * TODO: Implement once b/168831749 has been resolved
+ * The implementation should check the validity of the app.
+ * It should also check whether the app is free or paid, returning false in the latter case.
+ * For now, all checks occur in the sharing app.
+ * So, we simply check whether the sharing app is defined.
+ */
+ return !TextUtils.isEmpty(mSharingComponent);
+ }
+
+ private Uri getShareableUri(Context context, String path, String displayName) {
+ String authority = BuildConfig.APPLICATION_ID + FILE_PROVIDER_SUFFIX;
+ File pathFile = new File(path);
+ return FileProvider.getUriForFile(context, authority, pathFile, displayName);
+ }
+
+ private SystemShortcut<Launcher> getShortcut(Launcher launcher, ItemInfo info) {
+ if (!canShare(info)) {
+ return null;
+ }
+
+ return new Share(launcher, info);
+ }
+
+ /**
+ * The Share App system shortcut, used to initiate p2p sharing of a given app
+ */
+ public final class Share extends SystemShortcut<Launcher> {
+ public Share(Launcher target, ItemInfo itemInfo) {
+ super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+
+ ComponentName targetComponent = mItemInfo.getTargetComponent();
+ if (targetComponent == null) {
+ Log.e(TAG, "Item missing target component");
+ return;
+ }
+ String packageName = targetComponent.getPackageName();
+ PackageManager packageManager = view.getContext().getPackageManager();
+ String sourceDir, appLabel;
+ try {
+ PackageInfo packageInfo = packageManager.getPackageInfo(packageName, 0);
+ sourceDir = packageInfo.applicationInfo.sourceDir;
+ appLabel = packageManager.getApplicationLabel(packageInfo.applicationInfo)
+ .toString() + APP_EXSTENSION;
+ } catch (Exception e) {
+ Log.e(TAG, "Could not find info for package \"" + packageName + "\"");
+ return;
+ }
+ Uri uri = getShareableUri(view.getContext(), sourceDir, appLabel);
+ sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ sendIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+
+ sendIntent.setType(APP_MIME_TYPE);
+ sendIntent.setComponent(ComponentName.unflattenFromString(mSharingComponent));
+
+ mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+ }
+ }
+
+ /**
+ * Shortcut factory for generating the Share App button
+ */
+ public static final SystemShortcut.Factory<Launcher> SHORTCUT_FACTORY = (launcher, itemInfo) ->
+ (new AppSharing(launcher)).getShortcut(launcher, itemInfo);
+}
diff --git a/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java b/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java
new file mode 100644
index 0000000..8bd0fec
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/Launcher3QuickStepGo.java
@@ -0,0 +1,40 @@
+/*
+ * 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.launcher3;
+
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+import java.util.stream.Stream;
+
+/**
+ * The Launcher variant used for Android Go Edition
+ */
+public class Launcher3QuickStepGo extends QuickstepLauncher {
+ private static final String TAG = "Launcher3QuickStepGo";
+
+ @Override
+ public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
+ Stream<SystemShortcut.Factory> shortcuts = super.getSupportedShortcuts();
+
+ if (AppSharing.ENABLE_APP_SHARING) {
+ shortcuts = Stream.concat(shortcuts, Stream.of(AppSharing.SHORTCUT_FACTORY));
+ }
+
+ return shortcuts;
+ }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
new file mode 100644
index 0000000..b102a39
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -0,0 +1,147 @@
+/*
+ * 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 static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
+import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Matrix;
+import android.os.SystemClock;
+import android.text.TextUtils;
+
+import com.android.launcher3.R;
+import com.android.quickstep.views.OverviewActionsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Go-specific extension of the factory class that adds an overlay to TaskView
+ */
+public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
+ public static final String ACTION_LISTEN = "com.android.quickstep.ACTION_LISTEN";
+ public static final String ACTION_TRANSLATE = "com.android.quickstep.ACTION_TRANSLATE";
+ public static final String ACTION_SEARCH = "com.android.quickstep.ACTION_SEARCH";
+ public static final String ELAPSED_NANOS = "niu_actions_elapsed_realtime_nanos";
+
+ // Empty constructor required for ResourceBasedOverride
+ public TaskOverlayFactoryGo(Context context) {}
+
+ /**
+ * Create a new overlay instance for the given View
+ */
+ public TaskOverlayGo createOverlay(TaskThumbnailView thumbnailView) {
+ return new TaskOverlayGo(thumbnailView);
+ }
+
+ /**
+ * Overlay on each task handling Overview Action Buttons.
+ * @param <T> The type of View in which the overlay will be placed
+ */
+ public static final class TaskOverlayGo<T extends OverviewActionsView> extends TaskOverlay {
+
+ private String mPackageName;
+
+ private TaskOverlayGo(TaskThumbnailView taskThumbnailView) {
+ super(taskThumbnailView);
+ }
+
+ /**
+ * Called when the current task is interactive for the user
+ */
+ @Override
+ public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
+ boolean rotated) {
+ getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
+ mPackageName =
+ mApplicationContext.getResources().getString(R.string.niu_actions_package);
+
+ if (thumbnail == null || TextUtils.isEmpty(mPackageName)) {
+ return;
+ }
+
+ getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
+ boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
+ }
+
+ private void sendNIUIntent(String actionType) {
+ Intent intent = createNIUIntent(actionType);
+ mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
+ }
+
+ private Intent createNIUIntent(String actionType) {
+ return new Intent(actionType)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
+ .setPackage(mPackageName)
+ .putExtra(ELAPSED_NANOS, SystemClock.elapsedRealtimeNanos());
+ }
+
+ protected class OverlayUICallbacksGoImpl extends OverlayUICallbacksImpl
+ implements OverlayUICallbacksGo {
+ public OverlayUICallbacksGoImpl(boolean isAllowedByPolicy, Task task) {
+ super(isAllowedByPolicy, task);
+ }
+
+ @SuppressLint("NewApi")
+ public void onListen() {
+ if (mIsAllowedByPolicy) {
+ sendNIUIntent(ACTION_LISTEN);
+ } else {
+ showBlockedByPolicyMessage();
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void onTranslate() {
+ if (mIsAllowedByPolicy) {
+ sendNIUIntent(ACTION_TRANSLATE);
+ } else {
+ showBlockedByPolicyMessage();
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void onSearch() {
+ if (mIsAllowedByPolicy) {
+ sendNIUIntent(ACTION_SEARCH);
+ } else {
+ showBlockedByPolicyMessage();
+ }
+ }
+ }
+ }
+
+ /**
+ * Callbacks the Ui can generate. This is the only way for a Ui to call methods on the
+ * controller.
+ */
+ public interface OverlayUICallbacksGo extends OverlayUICallbacks {
+ /** User has requested to listen to the current content read aloud */
+ void onListen();
+
+ /** User has requested a translation of the current content */
+ void onTranslate();
+
+ /** User has requested a visual search of the current content */
+ void onSearch();
+ }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
new file mode 100644
index 0000000..9997d16
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/views/GoOverviewActionsView.java
@@ -0,0 +1,75 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.quickstep.TaskOverlayFactoryGo.OverlayUICallbacksGo;
+
+/**
+ * View for showing Go-specific action buttons in Overview
+ */
+public final class GoOverviewActionsView extends OverviewActionsView<OverlayUICallbacksGo> {
+ public GoOverviewActionsView(Context context) {
+ this(context, null);
+ }
+
+ public GoOverviewActionsView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public GoOverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ if (getResources().getBoolean(R.bool.enable_niu_actions)) {
+ findViewById(R.id.action_listen).setOnClickListener(this);
+ findViewById(R.id.action_translate).setOnClickListener(this);
+ findViewById(R.id.action_search).setOnClickListener(this);
+ } else {
+ findViewById(R.id.action_listen).setVisibility(View.GONE);
+ findViewById(R.id.action_translate).setVisibility(View.GONE);
+ findViewById(R.id.action_search).setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ super.onClick(view);
+
+ if (mCallbacks == null) {
+ return;
+ }
+ int id = view.getId();
+ if (id == R.id.action_listen) {
+ mCallbacks.onListen();
+ } else if (id == R.id.action_translate) {
+ mCallbacks.onTranslate();
+ } else if (id == R.id.action_search) {
+ mCallbacks.onSearch();
+ }
+ }
+}
diff --git a/go/src/com/android/launcher3/model/LoaderResults.java b/go/src/com/android/launcher3/model/LoaderResults.java
index 7130531..5f71061 100644
--- a/go/src/com/android/launcher3/model/LoaderResults.java
+++ b/go/src/com/android/launcher3/model/LoaderResults.java
@@ -20,7 +20,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.BgDataModel.Callbacks;
-import com.android.launcher3.util.LooperExecutor;
/**
* Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
@@ -29,12 +28,7 @@
public LoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
- this(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
- }
-
- public LoaderResults(LauncherAppState app, BgDataModel dataModel,
- AllAppsList allAppsList, Callbacks[] callbacks, LooperExecutor executor) {
- super(app, dataModel, allAppsList, callbacks, executor);
+ super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
@Override
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 3b3dc01..cc5e1cb 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -25,11 +25,12 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -41,21 +42,27 @@
// True is the widget support is disabled.
public static final boolean GO_DISABLE_WIDGETS = true;
+ public static final boolean GO_DISABLE_NOTIFICATION_DOTS = true;
- private static final ArrayList<WidgetListRowEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
+ private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
/**
- * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
- * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
- * is not sorted. This list is sorted at the UI when using
- * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+ * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
+ * sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
+ * not sorted. This list is sorted at the UI when using
+ * {@link com.android.launcher3.widget.picker.WidgetsDiffReporter}
*
- * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+ * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
*/
- public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
+ public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
return EMPTY_WIDGET_LIST;
}
+ /** Returns a mapping of packages to their widgets without static shortcuts. */
+ public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+ return Map.of();
+ }
+
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
diff --git a/gradle.properties b/gradle.properties
index 7a51375..7f4c609 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -10,4 +10,4 @@
PROTOBUF_DEPENDENCY=com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-7
BUILD_TOOLS_VERSION=28.0.3
-COMPILE_SDK=android-R
+COMPILE_SDK=android-S
diff --git a/proguard.flags b/proguard.flags
index 37b8093..a450183 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -45,9 +45,10 @@
# BUG(70852369): Surpress additional warnings after changing from Proguard to R8
-dontwarn android.app.**
--dontwarn android.view.**
--dontwarn android.os.**
-dontwarn android.graphics.**
+-dontwarn android.os.**
+-dontwarn android.view.**
+-dontwarn android.window.**
# Ignore warnings for hidden utility classes referenced from the shared lib
-dontwarn com.android.internal.util.**
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 1a8ab65..fe81b4c 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -18,6 +18,8 @@
option java_package = "com.android.launcher3.logger";
option java_outer_classname = "LauncherAtom";
+import "launcher_atom_extension.proto";
+
//
// ItemInfos
message ItemInfo {
@@ -27,6 +29,8 @@
Shortcut shortcut = 3;
Widget widget = 4;
FolderIcon folder_icon = 9;
+ Slice slice = 10;
+ SearchActionItem search_action_item = 11;
}
// When used for launch event, stores the global predictive rank
optional int32 rank = 5;
@@ -55,7 +59,7 @@
SettingsContainer settings_container = 9;
PredictedHotseatContainer predicted_hotseat_container = 10;
TaskSwitcherContainer task_switcher_container = 11;
- TaskForegroundContainer task_foreground_container = 12;
+ ExtendedContainers extended_containers = 20;
}
}
@@ -93,14 +97,8 @@
message SettingsContainer {
}
-// Container for tasks in the Overview UI.
-// Typically entered using either the overview gesture or overview button.
-message TaskSwitcherContainer {}
-
-// Container for tasks from another foreground app, when not on launcher screen.
-// Typically home gesture or overview gesture can be triggered from
-// this container.
-message TaskForegroundContainer {}
+message TaskSwitcherContainer {
+}
enum Attribute {
UNKNOWN = 0;
@@ -138,6 +136,7 @@
// Legacy shortcuts and shortcuts handled by ShortcutManager
message Shortcut {
optional string shortcut_name = 1;
+ optional string shortcut_id = 2;
}
// AppWidgets handled by AppWidgetManager
@@ -172,6 +171,17 @@
optional string label_info = 4;
}
+// Contains Slice details for logging.
+message Slice{
+ optional string uri = 1;
+}
+
+// Represents SearchAction with in launcher
+message SearchActionItem{
+ optional string package_name = 1;
+ optional string title = 2;
+}
+
//////////////////////////////////////////////
// Containers
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
deleted file mode 100644
index 9423cb2..0000000
--- a/protos/launcher_log.proto
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-syntax = "proto2";
-
-import "launcher_log_extension.proto";
-
-option java_package = "com.android.launcher3.userevent";
-option java_outer_classname = "LauncherLogProto";
-
-package userevent;
-
-message Target {
- enum Type {
- NONE = 0;
- ITEM = 1;
- CONTROL = 2;
- CONTAINER = 3;
- }
-
- optional Type type = 1;
-
- // For container type and item type
- // Used mainly for ContainerType.FOLDER, ItemType.*
- optional int32 page_index = 2;
- optional int32 rank = 3;
- optional int32 grid_x = 4;
- optional int32 grid_y = 5;
-
- // For container types only
- optional ContainerType container_type = 6;
- optional int32 cardinality = 7;
-
- // For control types only
- optional ControlType control_type = 8;
-
- // For item types only
- optional ItemType item_type = 9;
- optional int32 package_name_hash = 10;
- optional int32 component_hash = 11; // Used for ItemType.WIDGET
- optional int32 intent_hash = 12; // Used for ItemType.SHORTCUT
- optional int32 span_x = 13 [default = 1];// Used for ItemType.WIDGET
- optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
- optional int32 predictedRank = 15;
- optional TargetExtension extension = 16;
- optional TipType tip_type = 17;
- optional int32 search_query_length = 18;
- optional bool is_work_app = 19;
- optional FromFolderLabelState from_folder_label_state = 20;
- optional ToFolderLabelState to_folder_label_state = 21;
-
- // Note: proto does not support duplicate enum values, even if they belong to different enum type.
- // Hence "FROM" and "TO" prefix added.
- enum FromFolderLabelState {
- FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
- FROM_EMPTY = 1;
- FROM_CUSTOM = 2;
- FROM_SUGGESTED = 3;
- }
-
- enum ToFolderLabelState {
- TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
- TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
- TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
- TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
- TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
- TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
- TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
- TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
- TO_EMPTY_WITH_VALID_SUGGESTIONS = 8 [deprecated = true];
- TO_EMPTY_WITH_VALID_PRIMARY = 15;
- TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 16;
- TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
- TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
- TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11 [deprecated = true];
- TO_CUSTOM_WITH_VALID_PRIMARY = 17;
- TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 18;
- TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
- TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
- UNCHANGED = 14;
- }
-}
-
-// Used to define what type of item a Target would represent.
-enum ItemType {
- DEFAULT_ITEMTYPE = 0;
- APP_ICON = 1;
- SHORTCUT = 2;
- WIDGET = 3;
- FOLDER_ICON = 4;
- DEEPSHORTCUT = 5;
- SEARCHBOX = 6;
- EDITTEXT = 7;
- NOTIFICATION = 8;
- TASK = 9; // Each page of Recents UI (QuickStep)
- WEB_APP = 10;
- TASK_ICON = 11;
-}
-
-// Used to define what type of container a Target would represent.
-enum ContainerType {
- DEFAULT_CONTAINERTYPE = 0;
- WORKSPACE = 1;
- HOTSEAT = 2;
- FOLDER = 3;
- ALLAPPS = 4;
- WIDGETS = 5;
- OVERVIEW = 6; // Zoomed out workspace (without QuickStep)
- PREDICTION = 7;
- SEARCHRESULT = 8;
- DEEPSHORTCUTS = 9;
- PINITEM = 10; // confirmation screen
- NAVBAR = 11;
- TASKSWITCHER = 12; // Recents UI Container (QuickStep)
- APP = 13; // Foreground activity is another app (QuickStep)
- TIP = 14; // Onboarding texts (QuickStep)
- OTHER_LAUNCHER_APP = 15;
-}
-
-// Used to define what type of control a Target would represent.
-enum ControlType {
- DEFAULT_CONTROLTYPE = 0;
- ALL_APPS_BUTTON = 1;
- WIDGETS_BUTTON = 2;
- WALLPAPER_BUTTON = 3;
- SETTINGS_BUTTON = 4;
- REMOVE_TARGET = 5;
- UNINSTALL_TARGET = 6;
- APPINFO_TARGET = 7;
- RESIZE_HANDLE = 8;
- VERTICAL_SCROLL = 9;
- HOME_INTENT = 10; // Deprecated, use enum Command instead
- BACK_BUTTON = 11;
- QUICK_SCRUB_BUTTON = 12;
- CLEAR_ALL_BUTTON = 13;
- CANCEL_TARGET = 14;
- TASK_PREVIEW = 15;
- SPLIT_SCREEN_TARGET = 16;
- REMOTE_ACTION_SHORTCUT = 17;
- APP_USAGE_SETTINGS = 18;
- BACK_GESTURE = 19;
- UNDO = 20;
- DISMISS_PREDICTION = 21;
- HYBRID_HOTSEAT_ACCEPTED = 22;
- HYBRID_HOTSEAT_CANCELED = 23;
- OVERVIEW_ACTIONS_SHARE_BUTTON = 24;
- OVERVIEW_ACTIONS_SCREENSHOT_BUTTON = 25;
- OVERVIEW_ACTIONS_SELECT_BUTTON = 26;
- SELECT_MODE_CLOSE_BUTTON = 27;
- SELECT_MODE_ITEM = 28;
-}
-
-enum TipType {
- DEFAULT_NONE = 0;
- BOUNCE = 1;
- SWIPE_UP_TEXT = 2;
- QUICK_SCRUB_TEXT = 3;
- PREDICTION_TEXT = 4;
- DWB_TOAST = 5;
- HYBRID_HOTSEAT = 6;
-}
-
-// Used to define the action component of the LauncherEvent.
-message Action {
- enum Type {
- TOUCH = 0;
- AUTOMATED = 1;
- COMMAND = 2;
- TIP = 3;
- SOFT_KEYBOARD = 4;
- // HARD_KEYBOARD, ASSIST
- }
-
- enum Touch {
- TAP = 0;
- LONGPRESS = 1;
- DRAGDROP = 2;
- SWIPE = 3;
- FLING = 4;
- PINCH = 5;
- SWIPE_NOOP = 6;
- }
-
- enum Direction {
- NONE = 0;
- UP = 1;
- DOWN = 2;
- LEFT = 3;
- RIGHT = 4;
- UPRIGHT = 5;
- UPLEFT = 6;
- }
- enum Command {
- HOME_INTENT = 0;
- BACK = 1;
- ENTRY = 2; // Indicates entry to one of Launcher container type target
- // not using the HOME_INTENT
- CANCEL = 3; // Indicates that a confirmation screen was cancelled
- CONFIRM = 4; // Indicates thata confirmation screen was accepted
- STOP = 5; // Indicates onStop() was called (screen time out, power off)
- RECENTS_BUTTON = 6; // Indicates that Recents button was pressed
- RESUME = 7; // Indicates onResume() was called
- }
-
- optional Type type = 1;
- optional Touch touch = 2;
- optional Direction dir = 3;
- optional Command command = 4;
- // Log if the action was performed on outside of the container
- optional bool is_outside = 5;
- optional bool is_state_change = 6;
-}
-
-//
-// Context free grammar of typical user interaction:
-// Action (Touch) + Target
-// Action (Touch) + Target + Target
-//
-message LauncherEvent {
- required Action action = 1;
- // List of targets that touch actions can be operated on.
- repeated Target src_target = 2;
- repeated Target dest_target = 3;
-
- optional int64 action_duration_millis = 4;
- optional int64 elapsed_container_millis = 5;
- optional int64 elapsed_session_millis = 6;
-
- optional bool is_in_multi_window_mode = 7 [deprecated = true];
- optional bool is_in_landscape_mode = 8 [deprecated = true];
-
- optional LauncherEventExtension extension = 9;
-}
diff --git a/protos/launcher_trace.proto b/protos/launcher_trace.proto
index c6f3543..65fcfe5 100644
--- a/protos/launcher_trace.proto
+++ b/protos/launcher_trace.proto
@@ -28,4 +28,40 @@
message TouchInteractionServiceProto {
optional bool service_connected = 1;
+ optional OverviewComponentObserverProto overview_component_obvserver = 2;
+ optional InputConsumerProto input_consumer = 3;
+}
+
+message OverviewComponentObserverProto {
+
+ optional bool overview_activity_started = 1;
+ optional bool overview_activity_resumed = 2;
+}
+
+message InputConsumerProto {
+
+ optional string name = 1;
+ optional SwipeHandlerProto swipe_handler = 2;
+}
+
+message SwipeHandlerProto {
+
+ optional GestureStateProto gesture_state = 1;
+ optional bool is_recents_attached_to_app_window = 2;
+ optional int32 scroll_offset = 3;
+ // Swipe up progress from 0 (app) to 1 (overview); can be > 1 if swiping past overview.
+ optional float app_to_overview_progress = 4;
+}
+
+message GestureStateProto {
+
+ optional GestureEndTarget endTarget = 1 [default = UNSET];
+
+ enum GestureEndTarget {
+ UNSET = 0;
+ HOME = 1;
+ RECENTS = 2;
+ NEW_TASK = 3;
+ LAST_TASK = 4;
+ }
}
diff --git a/protos_overrides/launcher_atom_extension.proto b/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..a07daf8
--- /dev/null
+++ b/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// This proto file contains placeholder messages that can be overridden by
+// other Launcher variants.
+// Variants could have its own launcher_atom_extension.proto file(should have
+// same messages declared here but with different implementation); when building
+// variant's apk launcher_atom.proto will reference variant's extension file,
+// essentially overriding this file.
+
+
+// Wrapper message for additional containers used in variants.
+message ExtendedContainers {}
diff --git a/quickstep/AndroidManifest-launcher.xml b/quickstep/AndroidManifest-launcher.xml
index 60afddb..7fe9b08 100644
--- a/quickstep/AndroidManifest-launcher.xml
+++ b/quickstep/AndroidManifest-launcher.xml
@@ -49,10 +49,11 @@
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
android:taskAffinity=""
+ android:exported="true"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index e49f2ec..5e5cf73 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -17,97 +17,103 @@
** limitations under the License.
*/
-->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.launcher3" >
- <permission
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="com.android.launcher3">
+
+ <permission
android:name="${packageName}.permission.HOTSEAT_EDU"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="signatureOrSystem" />
- <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
- <uses-permission android:name="android.permission.VIBRATE" />
- <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
+ <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS"/>
+ <uses-permission android:name="android.permission.REMOVE_TASKS"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
+ <uses-permission android:name="android.permission.STATUS_BAR"/>
+ <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
+ <uses-permission android:name="android.permission.SET_ORIENTATION"/>
+ <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+ <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
+ <uses-permission android:name="android.permission.MONITOR_INPUT"/>
+
<uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
+ <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
- <application
- android:backupAgent="com.android.launcher3.LauncherBackupAgent"
- android:fullBackupOnly="true"
- android:fullBackupContent="@xml/backupscheme"
- android:hardwareAccelerated="true"
- android:icon="@drawable/ic_launcher_home"
- android:label="@string/derived_app_name"
- android:theme="@style/AppTheme"
- android:largeHeap="@bool/config_largeHeap"
- android:restoreAnyVersion="true"
- android:supportsRtl="true" >
+ <application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+ android:fullBackupOnly="true"
+ android:fullBackupContent="@xml/backupscheme"
+ android:hardwareAccelerated="true"
+ android:icon="@drawable/ic_launcher_home"
+ android:label="@string/derived_app_name"
+ android:theme="@style/AppTheme"
+ android:largeHeap="@bool/config_largeHeap"
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true">
- <service
- android:name="com.android.quickstep.TouchInteractionService"
- android:permission="android.permission.STATUS_BAR_SERVICE"
- android:directBootAware="true" >
+ <service android:name="com.android.quickstep.TouchInteractionService"
+ android:permission="android.permission.STATUS_BAR_SERVICE"
+ android:directBootAware="true"
+ android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.QUICKSTEP_SERVICE" />
+ <action android:name="android.intent.action.QUICKSTEP_SERVICE"/>
</intent-filter>
</service>
<activity android:name="com.android.quickstep.RecentsActivity"
- android:excludeFromRecents="true"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/LauncherTheme"
- android:screenOrientation="unspecified"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
- android:resizeableActivity="true"
- android:resumeWhilePausing="true"
- android:taskAffinity="" />
+ android:excludeFromRecents="true"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:theme="@style/LauncherTheme"
+ android:screenOrientation="unspecified"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
+ android:resizeableActivity="true"
+ android:resumeWhilePausing="true"
+ android:taskAffinity=""/>
<!-- Content provider to settings search. The autority should be same as the packageName -->
- <provider
- android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
- android:authorities="${packageName}"
- android:grantUriPermissions="true"
- android:multiprocess="true"
- android:permission="android.permission.READ_SEARCH_INDEXABLES"
- android:exported="true">
+ <provider android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
+ android:authorities="${packageName}"
+ android:grantUriPermissions="true"
+ android:multiprocess="true"
+ android:permission="android.permission.READ_SEARCH_INDEXABLES"
+ android:exported="true">
<intent-filter>
- <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" />
+ <action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER"/>
</intent-filter>
</provider>
<!-- FileProvider used for sharing images. -->
- <provider
- android:name="androidx.core.content.FileProvider"
- android:authorities="${packageName}.overview.fileprovider"
- android:exported="false"
- android:grantUriPermissions="true">
- <meta-data
- android:name="android.support.FILE_PROVIDER_PATHS"
- android:resource="@xml/overview_file_provider_paths" />
+ <provider android:name="androidx.core.content.FileProvider"
+ android:authorities="${packageName}.overview.fileprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/overview_file_provider_paths"/>
</provider>
- <service
- android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
- tools:node="remove" />
+ <service android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
+ tools:node="remove"/>
- <activity
- android:name="com.android.launcher3.proxy.ProxyActivityStarter"
- android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:exported="false" />
+ <activity android:name="com.android.launcher3.proxy.ProxyActivityStarter"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:exported="false"/>
- <activity
- android:name="com.android.quickstep.interaction.GestureSandboxActivity"
- android:autoRemoveFromRecents="true"
- android:excludeFromRecents="true"
- android:screenOrientation="portrait">
+ <activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
+ android:autoRemoveFromRecents="true"
+ android:excludeFromRecents="true"
+ android:screenOrientation="portrait"
+ android:exported="true">
<intent-filter>
- <action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
- <category android:name="android.intent.category.DEFAULT" />
+ <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
+ <category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity
@@ -116,7 +122,8 @@
android:noHistory="true"
android:launchMode="singleTask"
android:clearTaskOnLaunch="true"
- android:permission="${packageName}.permission.HOTSEAT_EDU">
+ android:permission="${packageName}.permission.HOTSEAT_EDU"
+ android:exported="true">
<intent-filter>
<action android:name="com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU"/>
<category android:name="android.intent.category.DEFAULT" />
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
similarity index 97%
rename from quickstep/res/layout/overview_actions_container.xml
rename to quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index 258f24a..d15a2d2 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -56,7 +56,7 @@
android:visibility="gone" />
<Space
- android:id="@+id/share_space"
+ android:id="@+id/oav_three_button_space"
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
diff --git a/quickstep/recents_ui_overrides/res/values/config.xml b/quickstep/overview_ui_overrides/res/values/config.xml
similarity index 83%
rename from quickstep/recents_ui_overrides/res/values/config.xml
rename to quickstep/overview_ui_overrides/res/values/config.xml
index 120e034..0f09439 100644
--- a/quickstep/recents_ui_overrides/res/values/config.xml
+++ b/quickstep/overview_ui_overrides/res/values/config.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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.
@@ -14,5 +14,5 @@
limitations under the License.
-->
<resources>
- <integer name="max_depth_blur_radius">150</integer>
+ <string name="task_overlay_factory_class" translatable="false"/>
</resources>
\ No newline at end of file
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..6253b41
--- /dev/null
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// Wrapper message for containers used at the quickstep level.
+// Message name should match with launcher_atom_extension.proto message at
+// the AOSP level.
+message ExtendedContainers {
+
+ oneof Container{
+ DeviceSearchResultContainer device_search_result_container = 1;
+ }
+}
+
+// Represents on-device search result container.
+message DeviceSearchResultContainer{
+ optional int32 query_length = 1;
+}
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_scrim_gradient.xml b/quickstep/recents_ui_overrides/res/drawable/chip_scrim_gradient.xml
deleted file mode 100644
index 5a2dfb7..0000000
--- a/quickstep/recents_ui_overrides/res/drawable/chip_scrim_gradient.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <gradient
- android:angle="90"
- android:endColor="@android:color/transparent"
- android:startColor="@color/chip_scrim_start_color"
- android:type="linear" />
-</shape>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
deleted file mode 100644
index f03f118..0000000
--- a/quickstep/recents_ui_overrides/res/values/colors.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <color name="chip_hint_foreground_color">#fff</color>
- <color name="chip_scrim_start_color">#39000000</color>
-
- <color name="all_apps_label_text">#61000000</color>
- <color name="all_apps_label_text_dark">#61FFFFFF</color>
- <color name="all_apps_prediction_row_separator">#3c000000</color>
- <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
-</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
deleted file mode 100644
index 9266b06..0000000
--- a/quickstep/recents_ui_overrides/res/values/dimens.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <dimen name="chip_hint_border_width">1dp</dimen>
- <dimen name="chip_hint_corner_radius">20dp</dimen>
- <dimen name="chip_hint_outer_padding">20dp</dimen>
- <dimen name="chip_hint_start_padding">10dp</dimen>
- <dimen name="chip_hint_end_padding">12dp</dimen>
- <dimen name="chip_hint_horizontal_margin">20dp</dimen>
- <dimen name="chip_hint_vertical_offset">16dp</dimen>
- <dimen name="chip_hint_elevation">2dp</dimen>
- <dimen name="chip_icon_size">16dp</dimen>
- <dimen name="chip_text_height">26dp</dimen>
- <dimen name="chip_text_top_padding">4dp</dimen>
- <dimen name="chip_text_start_padding">10dp</dimen>
- <dimen name="chip_text_size">14sp</dimen>
-
- <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
- <dimen name="all_apps_label_top_padding">16dp</dimen>
- <dimen name="all_apps_label_bottom_padding">8dp</dimen>
- <dimen name="all_apps_label_text_size">14sp</dimen>
-
- <!-- Minimum distance to swipe to trigger accessibility gesture -->
- <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
-</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/REMOVED.txt b/quickstep/recents_ui_overrides/src/REMOVED.txt
new file mode 100644
index 0000000..c3a3eaf
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/REMOVED.txt
@@ -0,0 +1,2 @@
+Temp file to prevent build breakage.
+Will be removed in followup cl.
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
deleted file mode 100644
index 38adf39..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ /dev/null
@@ -1,134 +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;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
-import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
- * {@link RecentsView}.
- */
-public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
-
- public LauncherAppTransitionManagerImpl(Context context) {
- super(context);
- }
-
- @Override
- protected boolean isLaunchingFromRecents(@NonNull View v,
- @Nullable RemoteAnimationTargetCompat[] targets) {
- return mLauncher.getStateManager().getState().overviewUi
- && findTaskViewToLaunch(mLauncher, v, targets) != null;
- }
-
- @Override
- protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
- @NonNull RemoteAnimationTargetCompat[] appTargets,
- @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
- RecentsView recentsView = mLauncher.getOverviewPanel();
- boolean skipLauncherChanges = !launcherClosing;
-
- TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
- PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
- createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
- mLauncher.getDepthController(), pa);
- anim.play(pa.buildAnim());
-
- Animator childStateAnimation = null;
- // Found a visible recents task that matches the opening app, lets launch the app from there
- Animator launcherAnim;
- final AnimatorListenerAdapter windowAnimEndListener;
- if (launcherClosing) {
- launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
- 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 onAnimationEnd(Animator animation) {
- mLauncher.getStateManager().moveToRestState();
- mLauncher.getStateManager().reapplyState();
- }
- };
- } else {
- AnimatorPlaybackController controller =
- mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
- RECENTS_LAUNCH_DURATION);
- controller.dispatchOnStart();
- childStateAnimation = controller.getTarget();
- launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
- windowAnimEndListener = new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLauncher.getStateManager().goToState(NORMAL, false);
- }
- };
- }
- anim.play(launcherAnim);
-
- // Set the current animation first, before adding windowAnimEndListener. Setting current
- // animation adds some listeners which need to be called before windowAnimEndListener
- // (the ordering of listeners matter in this case).
- mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
- anim.addListener(windowAnimEndListener);
- }
-
- @Override
- protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim, float[] alphas,
- float[] trans) {
- RecentsView overview = mLauncher.getOverviewPanel();
- ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
- RecentsView.CONTENT_ALPHA, alphas);
- alpha.setDuration(CONTENT_ALPHA_DURATION);
- alpha.setInterpolator(LINEAR);
- anim.play(alpha);
- overview.setFreezeViewVisibility(true);
-
- ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
- transY.setInterpolator(AGGRESSIVE_EASE);
- transY.setDuration(CONTENT_TRANSLATION_DURATION);
- anim.play(transY);
-
- return () -> {
- overview.setFreezeViewVisibility(false);
- overview.setTranslationY(0);
- mLauncher.getStateManager().reapplyState();
- };
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
deleted file mode 100644
index d200868..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/ComponentKeyMapper.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.appprediction;
-
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.util.ComponentKey;
-
-public class ComponentKeyMapper {
-
- protected final ComponentKey componentKey;
- private final DynamicItemCache mCache;
-
- public ComponentKeyMapper(ComponentKey key, DynamicItemCache cache) {
- componentKey = key;
- mCache = cache;
- }
-
- public String getPackage() {
- return componentKey.componentName.getPackageName();
- }
-
- public String getComponentClass() {
- return componentKey.componentName.getClassName();
- }
-
- public ComponentKey getComponentKey() {
- return componentKey;
- }
-
- @Override
- public String toString() {
- return componentKey.toString();
- }
-
- public ItemInfoWithIcon getApp(AllAppsStore store) {
- AppInfo item = store.getApp(componentKey);
- if (item != null) {
- return item;
- } else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
- return mCache.getInstantApp(componentKey.componentName.getPackageName());
- } else {
- return mCache.getShortcutInfo(componentKey);
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
deleted file mode 100644
index ab96b1340..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/DynamicItemCache.java
+++ /dev/null
@@ -1,268 +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.appprediction;
-
-import static android.content.pm.PackageManager.MATCH_INSTANT;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ShortcutInfo;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.InstantAppResolver;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Utility class which loads and caches predicted items like instant apps and shortcuts, before
- * they can be displayed on the UI
- */
-public class DynamicItemCache {
-
- private static final String TAG = "DynamicItemCache";
- private static final boolean DEBUG = false;
- private static final String DEFAULT_URL = "default-url";
-
- private static final int BG_MSG_LOAD_SHORTCUTS = 1;
- private static final int BG_MSG_LOAD_INSTANT_APPS = 2;
-
- private static final int UI_MSG_UPDATE_SHORTCUTS = 1;
- private static final int UI_MSG_UPDATE_INSTANT_APPS = 2;
-
- private final Context mContext;
- private final Handler mWorker;
- private final Handler mUiHandler;
- private final InstantAppResolver mInstantAppResolver;
- private final Runnable mOnUpdateCallback;
- private final IconCache mIconCache;
-
- private final Map<ComponentKey, WorkspaceItemInfo> mShortcuts;
- private final Map<String, InstantAppItemInfo> mInstantApps;
-
- public DynamicItemCache(Context context, Runnable onUpdateCallback) {
- mContext = context;
- mWorker = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
- mUiHandler = new Handler(Looper.getMainLooper(), this::handleUiMessage);
- mInstantAppResolver = InstantAppResolver.newInstance(context);
- mOnUpdateCallback = onUpdateCallback;
- mIconCache = LauncherAppState.getInstance(mContext).getIconCache();
-
- mShortcuts = new HashMap<>();
- mInstantApps = new HashMap<>();
- }
-
- public void cacheItems(List<ShortcutKey> shortcutKeys, List<String> pkgNames) {
- if (!shortcutKeys.isEmpty()) {
- mWorker.removeMessages(BG_MSG_LOAD_SHORTCUTS);
- Message.obtain(mWorker, BG_MSG_LOAD_SHORTCUTS, shortcutKeys).sendToTarget();
- }
- if (!pkgNames.isEmpty()) {
- mWorker.removeMessages(BG_MSG_LOAD_INSTANT_APPS);
- Message.obtain(mWorker, BG_MSG_LOAD_INSTANT_APPS, pkgNames).sendToTarget();
- }
- }
-
- private boolean handleWorkerMessage(Message msg) {
- switch (msg.what) {
- case BG_MSG_LOAD_SHORTCUTS: {
- List<ShortcutKey> shortcutKeys = msg.obj != null ?
- (List<ShortcutKey>) msg.obj : Collections.EMPTY_LIST;
- Map<ShortcutKey, WorkspaceItemInfo> shortcutKeyAndInfos = new ArrayMap<>();
- for (ShortcutKey shortcutKey : shortcutKeys) {
- WorkspaceItemInfo workspaceItemInfo = loadShortcutWorker(shortcutKey);
- if (workspaceItemInfo != null) {
- shortcutKeyAndInfos.put(shortcutKey, workspaceItemInfo);
- }
- }
- Message.obtain(mUiHandler, UI_MSG_UPDATE_SHORTCUTS, shortcutKeyAndInfos)
- .sendToTarget();
- return true;
- }
- case BG_MSG_LOAD_INSTANT_APPS: {
- List<String> pkgNames = msg.obj != null ?
- (List<String>) msg.obj : Collections.EMPTY_LIST;
- List<InstantAppItemInfo> instantAppItemInfos = new ArrayList<>();
- for (String pkgName : pkgNames) {
- InstantAppItemInfo instantAppItemInfo = loadInstantApp(pkgName);
- if (instantAppItemInfo != null) {
- instantAppItemInfos.add(instantAppItemInfo);
- }
- }
- Message.obtain(mUiHandler, UI_MSG_UPDATE_INSTANT_APPS, instantAppItemInfos)
- .sendToTarget();
- return true;
- }
- }
-
- return false;
- }
-
- private boolean handleUiMessage(Message msg) {
- switch (msg.what) {
- case UI_MSG_UPDATE_SHORTCUTS: {
- mShortcuts.clear();
- mShortcuts.putAll((Map<ShortcutKey, WorkspaceItemInfo>) msg.obj);
- mOnUpdateCallback.run();
- return true;
- }
- case UI_MSG_UPDATE_INSTANT_APPS: {
- List<InstantAppItemInfo> instantAppItemInfos = (List<InstantAppItemInfo>) msg.obj;
- mInstantApps.clear();
- for (InstantAppItemInfo instantAppItemInfo : instantAppItemInfos) {
- mInstantApps.put(instantAppItemInfo.getTargetComponent().getPackageName(),
- instantAppItemInfo);
- }
- mOnUpdateCallback.run();
- if (DEBUG) {
- Log.d(TAG, String.format("Cache size: %d, Cache: %s",
- mInstantApps.size(), mInstantApps.toString()));
- }
- return true;
- }
- }
-
- return false;
- }
-
- @WorkerThread
- private WorkspaceItemInfo loadShortcutWorker(ShortcutKey shortcutKey) {
- List<ShortcutInfo> details = shortcutKey.buildRequest(mContext).query(ShortcutRequest.ALL);
- if (!details.isEmpty()) {
- WorkspaceItemInfo si = new WorkspaceItemInfo(details.get(0), mContext);
- mIconCache.getShortcutIcon(si, details.get(0));
- return si;
- }
- if (DEBUG) {
- Log.d(TAG, "No shortcut found: " + shortcutKey.toString());
- }
- return null;
- }
-
- private InstantAppItemInfo loadInstantApp(String pkgName) {
- PackageManager pm = mContext.getPackageManager();
-
- try {
- ApplicationInfo ai = pm.getApplicationInfo(pkgName, 0);
- if (!mInstantAppResolver.isInstantApp(ai)) {
- return null;
- }
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
-
- String url = retrieveDefaultUrl(pkgName, pm);
- if (url == null) {
- Log.w(TAG, "no default-url available for pkg " + pkgName);
- return null;
- }
-
- Intent intent = new Intent(Intent.ACTION_VIEW)
- .addCategory(Intent.CATEGORY_BROWSABLE)
- .setData(Uri.parse(url));
- InstantAppItemInfo info = new InstantAppItemInfo(intent, pkgName);
- IconCache iconCache = LauncherAppState.getInstance(mContext).getIconCache();
- iconCache.getTitleAndIcon(info, false);
- if (info.bitmap.icon == null || iconCache.isDefaultIcon(info.bitmap, info.user)) {
- return null;
- }
- return info;
- }
-
- @Nullable
- public static String retrieveDefaultUrl(String pkgName, PackageManager pm) {
- Intent mainIntent = new Intent().setAction(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_LAUNCHER).setPackage(pkgName);
- List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
- mainIntent, MATCH_INSTANT | PackageManager.GET_META_DATA);
- String url = null;
- for (ResolveInfo resolveInfo : resolveInfos) {
- if (resolveInfo.activityInfo.metaData != null
- && resolveInfo.activityInfo.metaData.containsKey(DEFAULT_URL)) {
- url = resolveInfo.activityInfo.metaData.getString(DEFAULT_URL);
- }
- }
- return url;
- }
-
- @UiThread
- public InstantAppItemInfo getInstantApp(String pkgName) {
- return mInstantApps.get(pkgName);
- }
-
- @MainThread
- public WorkspaceItemInfo getShortcutInfo(ComponentKey key) {
- return mShortcuts.get(key);
- }
-
- /**
- * requests and caches icons for app targets
- */
- public void updateDependencies(List<ComponentKeyMapper> componentKeyMappers,
- AllAppsStore appsStore, IconCache.ItemInfoUpdateReceiver callback, int itemCount) {
- List<String> instantAppsToLoad = new ArrayList<>();
- List<ShortcutKey> shortcutsToLoad = new ArrayList<>();
- int total = componentKeyMappers.size();
- for (int i = 0, count = 0; i < total && count < itemCount; i++) {
- ComponentKeyMapper mapper = componentKeyMappers.get(i);
- // Update instant apps
- if (COMPONENT_CLASS_MARKER.equals(mapper.getComponentClass())) {
- instantAppsToLoad.add(mapper.getPackage());
- count++;
- } else if (mapper.getComponentKey() instanceof ShortcutKey) {
- shortcutsToLoad.add((ShortcutKey) mapper.getComponentKey());
- count++;
- } else {
- // Reload high res icon
- AppInfo info = (AppInfo) mapper.getApp(appsStore);
- if (info != null) {
- if (info.usingLowResIcon()) {
- mIconCache.updateIconInBackground(callback, info);
- }
- count++;
- }
- }
- }
- cacheItems(shortcutsToLoad, instantAppsToLoad);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
deleted file mode 100644
index 4c7943b..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionAppTracker.java
+++ /dev/null
@@ -1,276 +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.appprediction;
-
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.annotation.TargetApi;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
-import android.app.prediction.AppTargetId;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.UserHandle;
-import android.util.Log;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.systemui.plugins.AppLaunchEventsPlugin;
-import com.android.systemui.plugins.PluginListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Subclass of app tracker which publishes the data to the prediction engine and gets back results.
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public class PredictionAppTracker extends AppLaunchTracker
- implements PluginListener<AppLaunchEventsPlugin> {
-
- private static final String TAG = "PredictionAppTracker";
- private static final boolean DBG = false;
-
- private static final int MSG_INIT = 0;
- private static final int MSG_DESTROY = 1;
- private static final int MSG_LAUNCH = 2;
- private static final int MSG_PREDICT = 3;
-
- protected final Context mContext;
- private final Handler mMessageHandler;
- private final List<AppLaunchEventsPlugin> mAppLaunchEventsPluginsList;
-
- // Accessed only on worker thread
- private AppPredictor mHomeAppPredictor;
- private AppPredictor mRecentsOverviewPredictor;
-
- public PredictionAppTracker(Context context) {
- mContext = context;
- mMessageHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessage);
- InvariantDeviceProfile.INSTANCE.get(mContext).addOnChangeListener(this::onIdpChanged);
-
- mMessageHandler.sendEmptyMessage(MSG_INIT);
-
- mAppLaunchEventsPluginsList = new ArrayList<>();
- PluginManagerWrapper.INSTANCE.get(context)
- .addPluginListener(this, AppLaunchEventsPlugin.class, true);
- }
-
- @UiThread
- private void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
- if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
- // Reinitialize everything
- mMessageHandler.sendEmptyMessage(MSG_INIT);
- }
- }
-
- @WorkerThread
- private void destroy() {
- if (mHomeAppPredictor != null) {
- mHomeAppPredictor.destroy();
- mHomeAppPredictor = null;
- }
- if (mRecentsOverviewPredictor != null) {
- mRecentsOverviewPredictor.destroy();
- mRecentsOverviewPredictor = null;
- }
- }
-
- @WorkerThread
- private AppPredictor createPredictor(Client client, int count) {
- AppPredictionManager apm = mContext.getSystemService(AppPredictionManager.class);
-
- if (apm == null) {
- return null;
- }
-
- AppPredictor predictor = apm.createAppPredictionSession(
- new AppPredictionContext.Builder(mContext)
- .setUiSurface(client.id)
- .setPredictedTargetCount(count)
- .setExtras(getAppPredictionContextExtras(client))
- .build());
- predictor.registerPredictionUpdates(mContext.getMainExecutor(),
- PredictionUiStateManager.INSTANCE.get(mContext).appPredictorCallback(client));
- predictor.requestPredictionUpdate();
- return predictor;
- }
-
- /**
- * Override to add custom extras.
- */
- @WorkerThread
- @Nullable
- public Bundle getAppPredictionContextExtras(Client client) {
- return null;
- }
-
- @WorkerThread
- private boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_INIT: {
- // Destroy any existing clients
- destroy();
-
- // Initialize the clients
- int count = InvariantDeviceProfile.INSTANCE.get(mContext).numAllAppsColumns;
- mHomeAppPredictor = createPredictor(Client.HOME, count);
- mRecentsOverviewPredictor = createPredictor(Client.OVERVIEW, count);
- return true;
- }
- case MSG_DESTROY: {
- destroy();
- return true;
- }
- case MSG_LAUNCH: {
- if (mHomeAppPredictor != null) {
- mHomeAppPredictor.notifyAppTargetEvent((AppTargetEvent) msg.obj);
- }
- return true;
- }
- case MSG_PREDICT: {
- if (mHomeAppPredictor != null) {
- String client = (String) msg.obj;
- if (Client.HOME.id.equals(client)) {
- mHomeAppPredictor.requestPredictionUpdate();
- } else {
- mRecentsOverviewPredictor.requestPredictionUpdate();
- }
- }
- return true;
- }
- }
- return false;
- }
-
- @Override
- @UiThread
- public void onReturnedToHome() {
- String client = Client.HOME.id;
- mMessageHandler.removeMessages(MSG_PREDICT, client);
- Message.obtain(mMessageHandler, MSG_PREDICT, client).sendToTarget();
- if (DBG) {
- Log.d(TAG, String.format("Sent immediate message to update %s", client));
- }
-
- // Relay onReturnedToHome to every plugin.
- mAppLaunchEventsPluginsList.forEach(AppLaunchEventsPlugin::onReturnedToHome);
- }
-
- @Override
- @UiThread
- public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
- String container) {
- // TODO: Use the full shortcut info
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("shortcut:" + shortcutId), packageName, user)
- .setClassName(shortcutId)
- .build();
-
- sendLaunch(target, container);
-
- // Relay onStartShortcut info to every connected plugin.
- mAppLaunchEventsPluginsList
- .forEach(plugin -> plugin.onStartShortcut(
- packageName,
- shortcutId,
- user,
- container != null ? container : CONTAINER_DEFAULT)
- );
-
- }
-
- @Override
- @UiThread
- public void onStartApp(ComponentName cn, UserHandle user, String container) {
- if (cn != null) {
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("app:" + cn), cn.getPackageName(), user)
- .setClassName(cn.getClassName())
- .build();
- sendLaunch(target, container);
-
- // Relay onStartApp to every connected plugin.
- mAppLaunchEventsPluginsList
- .forEach(plugin -> plugin.onStartApp(
- cn,
- user,
- container != null ? container : CONTAINER_DEFAULT)
- );
- }
- }
-
- @Override
- @UiThread
- public void onDismissApp(ComponentName cn, UserHandle user, String container) {
- if (cn == null) return;
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("app: " + cn), cn.getPackageName(), user)
- .setClassName(cn.getClassName())
- .build();
- sendDismiss(target, container);
-
- // Relay onDismissApp to every connected plugin.
- mAppLaunchEventsPluginsList
- .forEach(plugin -> plugin.onDismissApp(
- cn,
- user,
- container != null ? container : CONTAINER_DEFAULT)
- );
- }
-
- @UiThread
- private void sendEvent(AppTarget target, String container, int eventId) {
- AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
- .setLaunchLocation(container == null ? CONTAINER_DEFAULT : container)
- .build();
- Message.obtain(mMessageHandler, MSG_LAUNCH, event).sendToTarget();
- }
-
- @UiThread
- private void sendLaunch(AppTarget target, String container) {
- sendEvent(target, container, AppTargetEvent.ACTION_LAUNCH);
- }
-
- @UiThread
- private void sendDismiss(AppTarget target, String container) {
- sendEvent(target, container, AppTargetEvent.ACTION_DISMISS);
- }
-
- @Override
- public void onPluginConnected(AppLaunchEventsPlugin appLaunchEventsPlugin, Context context) {
- mAppLaunchEventsPluginsList.add(appLaunchEventsPlugin);
- }
-
- @Override
- public void onPluginDisconnected(AppLaunchEventsPlugin appLaunchEventsPlugin) {
- mAppLaunchEventsPluginsList.remove(appLaunchEventsPlugin);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
deleted file mode 100644
index a0f6b04..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionUiStateManager.java
+++ /dev/null
@@ -1,370 +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.appprediction;
-
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
-import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.content.ComponentName;
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
-import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.MainThreadInitializedObject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-
-/**
- * Handler responsible to updating the UI due to predicted apps changes. Operations:
- * 1) Pushes the predicted apps to all-apps. If all-apps is visible, waits until it becomes
- * invisible again before applying the changes. This ensures that the UI does not change abruptly
- * in front of the user, even if an app launched and user pressed back button to return to the
- * all-apps UI again.
- * 2) Prefetch high-res icons for predicted apps. This ensures that we have the icons in memory
- * even if all-apps is not opened as they are shown in search UI as well
- * 3) Load instant app if it is not already in memory. As predictions are persisted on disk,
- * instant app will not be in memory when launcher starts.
- * 4) Maintains the current active client id (for the predictions) and all updates are performed on
- * that client id.
- */
-public class PredictionUiStateManager implements StateListener<LauncherState>,
- ItemInfoUpdateReceiver, OnIDPChangeListener, OnUpdateListener {
-
- public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
-
- // TODO (b/129421797): Update the client constants
- public enum Client {
- HOME("home"),
- OVERVIEW("overview");
-
- public final String id;
-
- Client(String id) {
- this.id = id;
- }
- }
-
- public static final MainThreadInitializedObject<PredictionUiStateManager> INSTANCE =
- new MainThreadInitializedObject<>(PredictionUiStateManager::new);
-
- private final Context mContext;
-
- private final DynamicItemCache mDynamicItemCache;
- private final List[] mPredictionServicePredictions;
-
- private int mMaxIconsPerRow;
- private Client mActiveClient;
-
- private AllAppsContainerView mAppsView;
-
- private PredictionState mPendingState;
- private PredictionState mCurrentState;
-
- private boolean mGettingValidPredictionResults;
-
- private PredictionUiStateManager(Context context) {
- mContext = context;
-
- mDynamicItemCache = new DynamicItemCache(context, this::onAppsUpdated);
-
- mActiveClient = Client.HOME;
-
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
- mMaxIconsPerRow = idp.numColumns;
-
- idp.addOnChangeListener(this);
- mPredictionServicePredictions = new List[Client.values().length];
- for (int i = 0; i < mPredictionServicePredictions.length; i++) {
- mPredictionServicePredictions[i] = Collections.emptyList();
- }
- mGettingValidPredictionResults = Utilities.getDevicePrefs(context)
- .getBoolean(LAST_PREDICTION_ENABLED_STATE, true);
-
- // Call this last
- mCurrentState = parseLastState();
- }
-
- @Override
- public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
- mMaxIconsPerRow = profile.numColumns;
- }
-
- public Client getClient() {
- return mActiveClient;
- }
-
- public void switchClient(Client client) {
- if (client == mActiveClient) {
- return;
- }
- mActiveClient = client;
- dispatchOnChange(true);
- }
-
- public void setTargetAppsView(AllAppsContainerView appsView) {
- if (mAppsView != null) {
- mAppsView.getAppsStore().removeUpdateListener(this);
- }
- mAppsView = appsView;
- if (mAppsView != null) {
- mAppsView.getAppsStore().addUpdateListener(this);
- }
- if (mPendingState != null) {
- applyState(mPendingState);
- mPendingState = null;
- } else {
- applyState(mCurrentState);
- }
- updateDependencies(mCurrentState);
- }
-
- @Override
- public void reapplyItemInfo(ItemInfoWithIcon info) { }
-
- @Override
- public void onStateTransitionComplete(LauncherState state) {
- if (mAppsView == null) {
- return;
- }
- if (mPendingState != null && canApplyPredictions(mPendingState)) {
- applyState(mPendingState);
- mPendingState = null;
- }
- if (mPendingState == null) {
- Launcher.getLauncher(mAppsView.getContext()).getStateManager()
- .removeStateListener(this);
- }
- }
-
- private void scheduleApplyPredictedApps(PredictionState state) {
- boolean registerListener = mPendingState == null;
- mPendingState = state;
- if (registerListener) {
- // Add a listener and wait until appsView is invisible again.
- Launcher.getLauncher(mAppsView.getContext()).getStateManager().addStateListener(this);
- }
- }
-
- private void applyState(PredictionState state) {
- mCurrentState = state;
- if (mAppsView != null) {
- mAppsView.getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
- .setPredictedApps(mCurrentState.apps);
- }
- }
-
- private void updatePredictionStateAfterCallback() {
- boolean validResults = false;
- for (List l : mPredictionServicePredictions) {
- validResults |= l != null && !l.isEmpty();
- }
- if (validResults != mGettingValidPredictionResults) {
- mGettingValidPredictionResults = validResults;
- Utilities.getDevicePrefs(mContext).edit()
- .putBoolean(LAST_PREDICTION_ENABLED_STATE, true)
- .apply();
- }
- dispatchOnChange(true);
- }
-
- public AppPredictor.Callback appPredictorCallback(Client client) {
- return targets -> {
- mPredictionServicePredictions[client.ordinal()] = targets;
- updatePredictionStateAfterCallback();
- };
- }
-
- private void dispatchOnChange(boolean changed) {
- PredictionState newState = changed
- ? parseLastState()
- : mPendingState != null && canApplyPredictions(mPendingState)
- ? mPendingState
- : mCurrentState;
- if (changed && mAppsView != null && !canApplyPredictions(newState)) {
- scheduleApplyPredictedApps(newState);
- } else {
- applyState(newState);
- }
- }
-
- private PredictionState parseLastState() {
- PredictionState state = new PredictionState();
- state.isEnabled = mGettingValidPredictionResults;
- if (!state.isEnabled) {
- state.apps = Collections.EMPTY_LIST;
- return state;
- }
-
- state.apps = new ArrayList<>();
-
- List<AppTarget> appTargets = mPredictionServicePredictions[mActiveClient.ordinal()];
- if (!appTargets.isEmpty()) {
- for (AppTarget appTarget : appTargets) {
- ComponentKey key;
- if (appTarget.getShortcutInfo() != null) {
- key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
- } else {
- key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
- appTarget.getClassName()), appTarget.getUser());
- }
- state.apps.add(new ComponentKeyMapper(key, mDynamicItemCache));
- }
- }
- updateDependencies(state);
- return state;
- }
-
- private void updateDependencies(PredictionState state) {
- if (!state.isEnabled || mAppsView == null) {
- return;
- }
- mDynamicItemCache.updateDependencies(state.apps, mAppsView.getAppsStore(), this,
- mMaxIconsPerRow);
- }
-
- @Override
- public void onAppsUpdated() {
- dispatchOnChange(false);
- }
-
- private boolean canApplyPredictions(PredictionState newState) {
- if (mAppsView == null) {
- // If there is no apps view, no need to schedule.
- return true;
- }
- Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
- PredictionRowView predictionRow = mAppsView.getFloatingHeaderView().
- findFixedRowByType(PredictionRowView.class);
- if (!predictionRow.isShown() || predictionRow.getAlpha() == 0 ||
- launcher.isForceInvisible()) {
- return true;
- }
-
- if (mCurrentState.isEnabled != newState.isEnabled
- || mCurrentState.apps.isEmpty() != newState.apps.isEmpty()) {
- // If the visibility of the prediction row is changing, apply immediately.
- return true;
- }
-
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- // If we are here & mAppsView.isShown() = true, we are probably in all-apps or mid way
- return false;
- }
- if (!launcher.isInState(OVERVIEW) && !launcher.isInState(BACKGROUND_APP)) {
- // Just a fallback as we dont need to apply instantly, if we are not in the swipe-up UI
- return false;
- }
-
- // Instead of checking against 1, we should check against (1 + delta), where delta accounts
- // for the nav-bar height (as app icon can still be visible under the nav-bar). Checking
- // against 1, keeps the logic simple :)
- return launcher.getAllAppsController().getProgress() > 1;
- }
-
- public PredictionState getCurrentState() {
- return mCurrentState;
- }
-
- /**
- * Returns ranking info for the app within all apps prediction.
- * Only applicable when {@link ItemInfo#itemType} is one of the followings:
- * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
- */
- public OptionalInt getAllAppsRank(@Nullable ItemInfo itemInfo) {
- if (itemInfo == null || itemInfo.getTargetComponent() == null || itemInfo.user == null) {
- return OptionalInt.empty();
- }
-
- if (itemInfo.itemType == ITEM_TYPE_APPLICATION
- || itemInfo.itemType == ITEM_TYPE_SHORTCUT
- || itemInfo.itemType == ITEM_TYPE_DEEP_SHORTCUT) {
- ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(),
- itemInfo.user);
- final List<ComponentKeyMapper> apps = getCurrentState().apps;
- return IntStream.range(0, apps.size())
- .filter(index -> key.equals(apps.get(index).getComponentKey()))
- .findFirst();
- }
-
- return OptionalInt.empty();
- }
-
- /**
- * Fill in predicted_rank field based on app prediction.
- * Only applicable when {@link ItemInfo#itemType} is one of the followings:
- * {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
- */
- public static void fillInPredictedRank(
- @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
-
- final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate();
- if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null
- || (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
- && itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
- return;
- }
- if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_PREDICTION) {
- HotseatPredictionController.encodeHotseatLayoutIntoPredictionRank(itemInfo, target);
- return;
- }
-
- final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
- final List<ComponentKeyMapper> predictedApps = manager.getCurrentState().apps;
- IntStream.range(0, predictedApps.size())
- .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
- .findFirst()
- .ifPresent((rank) -> target.predictedRank = 0 - rank);
- }
-
- public static class PredictionState {
-
- public boolean isEnabled;
- public List<ComponentKeyMapper> apps;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
deleted file mode 100644
index 20e1edc..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatFileLog.java
+++ /dev/null
@@ -1,128 +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.launcher3.hybridhotseat;
-
-import android.content.Context;
-import android.os.Handler;
-import android.util.Log;
-
-import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.MainThreadInitializedObject;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.PrintWriter;
-import java.text.DateFormat;
-import java.util.Calendar;
-import java.util.Date;
-
-/**
- * Helper class to allow hot seat file logging
- */
-public class HotseatFileLog {
-
- public static final int LOG_DAYS = 10;
- private static final String FILE_NAME_PREFIX = "hotseat-log-";
- private static final DateFormat DATE_FORMAT =
- DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
- public static final MainThreadInitializedObject<HotseatFileLog> INSTANCE =
- new MainThreadInitializedObject<>(HotseatFileLog::new);
-
-
- private final Handler mHandler = new Handler(
- Executors.createAndStartNewLooper("hotseat-logger"));
- private final File mLogsDir;
- private PrintWriter mCurrentWriter;
- private String mFileName;
-
- private HotseatFileLog(Context context) {
- mLogsDir = context.getFilesDir();
- }
-
- /**
- * Prints log values to disk
- */
- public void log(String tag, String msg) {
- String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
-
- mHandler.post(() -> {
- synchronized (this) {
- PrintWriter writer = getWriter();
- if (writer != null) {
- writer.println(out);
- }
- }
- });
- }
-
- private PrintWriter getWriter() {
- Calendar cal = Calendar.getInstance();
- String fName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) % 10);
- if (fName.equals(mFileName)) return mCurrentWriter;
-
- boolean append = false;
- File logFile = new File(mLogsDir, fName);
- if (logFile.exists()) {
- Calendar modifiedTime = Calendar.getInstance();
- modifiedTime.setTimeInMillis(logFile.lastModified());
-
- // If the file was modified more that 36 hours ago, purge the file.
- // We use instead of 24 to account for day-365 followed by day-1
- modifiedTime.add(Calendar.HOUR, 36);
- append = cal.before(modifiedTime);
- }
-
-
- if (mCurrentWriter != null) {
- mCurrentWriter.close();
- }
- try {
- mCurrentWriter = new PrintWriter(new FileWriter(logFile, append));
- mFileName = fName;
- } catch (Exception ex) {
- Log.e("HotseatLogs", "Error writing logs to file", ex);
- closeWriter();
- }
- return mCurrentWriter;
- }
-
-
- private synchronized void closeWriter() {
- mFileName = null;
- if (mCurrentWriter != null) {
- mCurrentWriter.close();
- }
- mCurrentWriter = null;
- }
-
-
- /**
- * Returns a list of all log files
- */
- public synchronized File[] getLogFiles() {
- File[] files = new File[LOG_DAYS + FileLog.LOG_DAYS];
- //include file log files here
- System.arraycopy(FileLog.getLogFiles(), 0, files, 0, FileLog.LOG_DAYS);
-
- closeWriter();
- for (int i = 0; i < LOG_DAYS; i++) {
- files[FileLog.LOG_DAYS + i] = new File(mLogsDir, FILE_NAME_PREFIX + i);
- }
- return files;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
deleted file mode 100644
index b94e633..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ /dev/null
@@ -1,723 +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.hybridhotseat;
-
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
-import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetEvent;
-import android.content.ComponentName;
-import android.os.Process;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsStore;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.appprediction.ComponentKeyMapper;
-import com.android.launcher3.appprediction.DynamicItemCache;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
-import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
-import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.FolderInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.popup.SystemShortcut;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.IntArray;
-import com.android.launcher3.util.OnboardingPrefs;
-import com.android.launcher3.views.ArrowTipView;
-import com.android.launcher3.views.Snackbar;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.OptionalInt;
-import java.util.stream.IntStream;
-
-/**
- * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
- * pinning of predicted apps and manages replacement of predicted apps with user drag.
- */
-public class HotseatPredictionController implements DragController.DragListener,
- View.OnAttachStateChangeListener, SystemShortcut.Factory<QuickstepLauncher>,
- InvariantDeviceProfile.OnIDPChangeListener, AllAppsStore.OnUpdateListener,
- IconCache.ItemInfoUpdateReceiver, DragSource {
-
- private static final String TAG = "PredictiveHotseat";
- private static final boolean DEBUG = false;
-
- private static final String PREDICTION_CLIENT = "hotseat";
- private DropTarget.DragObject mDragObject;
- private int mHotSeatItemsCount;
- private int mPredictedSpotsCount = 0;
-
- private Launcher mLauncher;
- private final Hotseat mHotseat;
-
- private final HotseatRestoreHelper mRestoreHelper;
-
- private List<ComponentKeyMapper> mComponentKeyMappers = new ArrayList<>();
-
- private DynamicItemCache mDynamicItemCache;
-
- private final HotseatPredictionModel mPredictionModel;
- private AppPredictor mAppPredictor;
- private AllAppsStore mAllAppsStore;
- private AnimatorSet mIconRemoveAnimators;
- private boolean mUIUpdatePaused = false;
- private boolean mIsDestroyed = false;
-
-
- private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
-
- private final View.OnLongClickListener mPredictionLongClickListener = v -> {
- if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
- if (mLauncher.getWorkspace().isSwitchingState()) return false;
- if (!mLauncher.getOnboardingPrefs().getBoolean(
- OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
- Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
- mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- return true;
- }
- // Start the drag
- mLauncher.getWorkspace().beginDragShared(v, this, new DragOptions());
- return true;
- };
-
- public HotseatPredictionController(Launcher launcher) {
- mLauncher = launcher;
- mHotseat = launcher.getHotseat();
- mAllAppsStore = mLauncher.getAppsView().getAppsStore();
- LauncherAppState appState = LauncherAppState.getInstance(launcher);
- mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel();
- mAllAppsStore.addUpdateListener(this);
- mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
- mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
- launcher.getDeviceProfile().inv.addOnChangeListener(this);
- mHotseat.addOnAttachStateChangeListener(this);
- mRestoreHelper = new HotseatRestoreHelper(mLauncher);
- if (mHotseat.isAttachedToWindow()) {
- onViewAttachedToWindow(mHotseat);
- }
- }
-
- /**
- * Shows appropriate hotseat education based on prediction enabled and migration states.
- */
- public void showEdu() {
- mLauncher.getStateManager().goToState(NORMAL, true, () -> {
- if (mComponentKeyMappers.isEmpty()) {
- // launcher has empty predictions set
- Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
- showDiscoveryTip();
- } else {
- HotseatEduController eduController = new HotseatEduController(mLauncher,
- mRestoreHelper,
- this::createPredictor);
- eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
- eduController.showEdu();
- }
- });
- }
-
- /**
- * Shows educational tip for hotseat if user does not go through Tips app.
- */
- private void showDiscoveryTip() {
- if (getPredictedIcons().isEmpty()) {
- new ArrowTipView(mLauncher).show(
- mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
- } else {
- Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
- R.string.hotseat_prediction_settings, null,
- () -> mLauncher.startActivity(getSettingsIntent()));
- }
- }
-
- /**
- * Returns if hotseat client has predictions
- */
- public boolean hasPredictions() {
- return !mComponentKeyMappers.isEmpty();
- }
-
- @Override
- public void onViewAttachedToWindow(View view) {
- mLauncher.getDragController().addDragListener(this);
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- mLauncher.getDragController().removeDragListener(this);
- }
-
- private void fillGapsWithPrediction() {
- fillGapsWithPrediction(false, null);
- }
-
- private void fillGapsWithPrediction(boolean animate, Runnable callback) {
- if (mUIUpdatePaused || mDragObject != null) {
- return;
- }
- List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
- if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) {
- // Safely ignore update as AppsList is not ready yet. This will called again once
- // apps are ready (HotseatPredictionController#onAppsUpdated)
- return;
- }
- int predictionIndex = 0;
- ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
- // make sure predicted icon removal and filling predictions don't step on each other
- if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
- mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- fillGapsWithPrediction(animate, callback);
- mIconRemoveAnimators.removeListener(this);
- }
- });
- return;
- }
- for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
- View child = mHotseat.getChildAt(
- mHotseat.getCellXFromOrder(rank),
- mHotseat.getCellYFromOrder(rank));
-
- if (child != null && !isPredictedIcon(child)) {
- continue;
- }
- if (predictedApps.size() <= predictionIndex) {
- // Remove predicted apps from the past
- if (isPredictedIcon(child)) {
- mHotseat.removeView(child);
- }
- continue;
- }
- WorkspaceItemInfo predictedItem = predictedApps.get(predictionIndex++);
- if (isPredictedIcon(child) && child.isEnabled()) {
- PredictedAppIcon icon = (PredictedAppIcon) child;
- icon.applyFromWorkspaceItem(predictedItem);
- icon.finishBinding(mPredictionLongClickListener);
- } else {
- newItems.add(predictedItem);
- }
- preparePredictionInfo(predictedItem, rank);
- }
- mPredictedSpotsCount = predictionIndex;
- bindItems(newItems, animate, callback);
- }
-
- private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate, Runnable callback) {
- AnimatorSet animationSet = new AnimatorSet();
- for (WorkspaceItemInfo item : itemsToAdd) {
- PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
- mLauncher.getWorkspace().addInScreenFromBind(icon, item);
- icon.finishBinding(mPredictionLongClickListener);
- if (animate) {
- animationSet.play(ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0.2f, 1));
- }
- }
- if (animate) {
- if (callback != null) {
- animationSet.addListener(AnimationSuccessListener.forRunnable(callback));
- }
- animationSet.start();
- } else {
- if (callback != null) callback.run();
- }
- }
-
- /**
- * Unregisters callbacks and frees resources
- */
- public void destroy() {
- mIsDestroyed = true;
- mAllAppsStore.removeUpdateListener(this);
- mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
- mHotseat.removeOnAttachStateChangeListener(this);
- if (mAppPredictor != null) {
- mAppPredictor.destroy();
- }
- }
-
- /**
- * start and pauses predicted apps update on the hotseat
- */
- public void setPauseUIUpdate(boolean paused) {
- mUIUpdatePaused = paused;
- if (!paused) {
- fillGapsWithPrediction();
- }
- }
-
- /**
- * Creates App Predictor with all the current apps pinned on the hotseat
- */
- public void createPredictor() {
- AppPredictionManager apm = mLauncher.getSystemService(AppPredictionManager.class);
- if (apm == null) {
- return;
- }
- if (mAppPredictor != null) {
- mAppPredictor.destroy();
- mAppPredictor = null;
- }
- WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
-
-
- mPredictionModel.createBundle(bundle -> {
- if (mIsDestroyed) return;
- mAppPredictor = apm.createAppPredictionSession(
- new AppPredictionContext.Builder(mLauncher)
- .setUiSurface(PREDICTION_CLIENT)
- .setPredictedTargetCount(mHotSeatItemsCount)
- .setExtras(bundle)
- .build());
- mAppPredictor.registerPredictionUpdates(
- mLauncher.getApplicationContext().getMainExecutor(),
- list -> {
- if (controllerRef.get() != null) {
- controllerRef.get().setPredictedApps(list);
- }
- });
- mAppPredictor.requestPredictionUpdate();
- });
- setPauseUIUpdate(false);
- }
-
- /**
- * Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
- */
- public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
- if (hasPredictions() && mAppPredictor != null) {
- mAppPredictor.requestPredictionUpdate();
- fillGapsWithPrediction();
- return;
- }
- int count = Math.min(ranks.size(), apps.size());
- List<WorkspaceItemInfo> items = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
- ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
- preparePredictionInfo(item, ranks.get(i));
- items.add(item);
-
- mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache));
- }
- updateDependencies();
- bindItems(items, false, null);
- }
-
- private void setPredictedApps(List<AppTarget> appTargets) {
- mComponentKeyMappers.clear();
- if (appTargets.isEmpty()) {
- mRestoreHelper.restoreBackup();
- }
- StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
- ArrayList<ComponentKey> componentKeys = new ArrayList<>();
- for (AppTarget appTarget : appTargets) {
- ComponentKey key;
- if (appTarget.getShortcutInfo() != null) {
- key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
- } else {
- key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
- appTarget.getClassName()), appTarget.getUser());
- }
- componentKeys.add(key);
- predictionLog.append(key.toString());
- predictionLog.append(",rank:");
- predictionLog.append(appTarget.getRank());
- predictionLog.append("\n");
- mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
- }
- predictionLog.append("]");
- if (Utilities.IS_DEBUG_DEVICE) {
- HotseatFileLog.INSTANCE.get(mLauncher).log(TAG, predictionLog.toString());
- }
- updateDependencies();
- fillGapsWithPrediction();
- mPredictionModel.cachePredictionComponentKeys(componentKeys);
- }
-
- private void updateDependencies() {
- mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
- mHotSeatItemsCount);
- }
-
- /**
- * Pins a predicted app icon into place.
- */
- public void pinPrediction(ItemInfo info) {
- PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt(
- mHotseat.getCellXFromOrder(info.rank),
- mHotseat.getCellYFromOrder(info.rank));
- if (icon == null) {
- return;
- }
- WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);
- mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo,
- LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId,
- workspaceItemInfo.cellX, workspaceItemInfo.cellY);
- ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
- icon.pin(workspaceItemInfo);
- AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo);
- if (appTarget != null) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
- AppTargetEvent.ACTION_PIN, workspaceItemInfo));
- }
- }
-
- private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
- List<ComponentKeyMapper> components) {
- AllAppsStore allAppsStore = mLauncher.getAppsView().getAppsStore();
- if (allAppsStore.getApps().length == 0) {
- return Collections.emptyList();
- }
-
- List<WorkspaceItemInfo> predictedApps = new ArrayList<>();
- for (ComponentKeyMapper mapper : components) {
- ItemInfoWithIcon info = mapper.getApp(allAppsStore);
- if (info instanceof AppInfo) {
- WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((AppInfo) info);
- predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- predictedApps.add(predictedApp);
- } else if (info instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo predictedApp = new WorkspaceItemInfo((WorkspaceItemInfo) info);
- predictedApp.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- predictedApps.add(predictedApp);
- } else {
- if (DEBUG) {
- Log.e(TAG, "Predicted app not found: " + mapper);
- }
- }
- // Stop at the number of hotseat items
- if (predictedApps.size() == mHotSeatItemsCount) {
- break;
- }
- }
- return predictedApps;
- }
-
- private List<PredictedAppIcon> getPredictedIcons() {
- List<PredictedAppIcon> icons = new ArrayList<>();
- ViewGroup vg = mHotseat.getShortcutsAndWidgets();
- for (int i = 0; i < vg.getChildCount(); i++) {
- View child = vg.getChildAt(i);
- if (isPredictedIcon(child)) {
- icons.add((PredictedAppIcon) child);
- }
- }
- return icons;
- }
-
- private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines,
- ItemInfo draggedInfo) {
- if (mIconRemoveAnimators != null) {
- mIconRemoveAnimators.end();
- }
- mIconRemoveAnimators = new AnimatorSet();
- removeOutlineDrawings();
- for (PredictedAppIcon icon : getPredictedIcons()) {
- if (!icon.isEnabled()) {
- continue;
- }
- if (icon.getTag().equals(draggedInfo)) {
- mHotseat.removeView(icon);
- continue;
- }
- int rank = ((WorkspaceItemInfo) icon.getTag()).rank;
- outlines.add(new PredictedAppIcon.PredictedIconOutlineDrawing(
- mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank), icon));
- icon.setEnabled(false);
- ObjectAnimator animator = ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0);
- animator.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- if (icon.getParent() != null) {
- mHotseat.removeView(icon);
- }
- }
- });
- mIconRemoveAnimators.play(animator);
- }
- mIconRemoveAnimators.start();
- }
-
- private void notifyItemAction(AppTargetEvent event) {
- if (mAppPredictor != null) {
- mAppPredictor.notifyAppTargetEvent(event);
- }
- }
-
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- removePredictedApps(mOutlineDrawings, dragObject.dragInfo);
- mDragObject = dragObject;
- if (mOutlineDrawings.isEmpty()) return;
- for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
- mHotseat.addDelegatedCellDrawing(outlineDrawing);
- }
- mHotseat.invalidate();
- }
-
- /**
- * Unpins pinned app when it's converted into a folder
- */
- public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
- AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
- AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
- if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
- AppTargetEvent.ACTION_PIN, folderInfo));
- }
- // using folder info with isTrackedForPrediction as itemInfo.container is already changed
- // to folder by this point
- if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
- AppTargetEvent.ACTION_UNPIN, folderInfo
- ));
- }
- }
-
- /**
- * Pins workspace item created when all folder items are removed but one
- */
- public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
- AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
- AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
- if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
- AppTargetEvent.ACTION_UNPIN, folderInfo));
- }
- if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
- AppTargetEvent.ACTION_PIN, itemInfo));
- }
- }
-
- @Override
- public void onDragEnd() {
- if (mDragObject == null) {
- return;
- }
-
- ItemInfo dragInfo = mDragObject.dragInfo;
- if (mDragObject.isMoved()) {
- AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo);
- //always send pin event first to prevent AiAi from predicting an item moved within
- // the same page
- if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
- AppTargetEvent.ACTION_PIN, dragInfo));
- }
- if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(
- mDragObject.originalDragInfo)) {
- notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
- AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo));
- }
- }
- mDragObject = null;
- fillGapsWithPrediction(true, this::removeOutlineDrawings);
- }
-
-
- @Nullable
- @Override
- public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
- ItemInfo itemInfo) {
- if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
- return null;
- }
- return new PinPrediction(activity, itemInfo);
- }
-
- private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
- itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- itemInfo.rank = rank;
- itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
- itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
- itemInfo.screenId = rank;
- }
-
- private void removeOutlineDrawings() {
- if (mOutlineDrawings.isEmpty()) return;
- for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
- mHotseat.removeDelegatedCellDrawing(outlineDrawing);
- }
- mHotseat.invalidate();
- mOutlineDrawings.clear();
- }
-
- @Override
- public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
- if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
- this.mHotSeatItemsCount = profile.numHotseatIcons;
- createPredictor();
- }
- }
-
- @Override
- public void onAppsUpdated() {
- fillGapsWithPrediction();
- }
-
- @Override
- public void reapplyItemInfo(ItemInfoWithIcon info) {
- }
-
- @Override
- public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
- //Does nothing
- }
-
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
- ArrayList<LauncherLogProto.Target> parents) {
- mHotseat.fillInLogContainerData(childInfo, child, parents);
- }
-
- /**
- * Logs rank info based on current list of predicted items
- */
- public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
- if (Utilities.IS_DEBUG_DEVICE) {
- final String pkg = itemInfo.getTargetComponent() != null
- ? itemInfo.getTargetComponent().getPackageName() : "unknown";
- HotseatFileLog.INSTANCE.get(mLauncher).log("UserEvent",
- "appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
- && !Process.myUserHandle().equals(itemInfo.user))
- + ",launchLocation:" + itemInfo.container);
- }
-
- if (itemInfo.getTargetComponent() == null || itemInfo.user == null) {
- return;
- }
-
- final ComponentKey key = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-
- final List<ComponentKeyMapper> predictedApps = new ArrayList<>(mComponentKeyMappers);
- OptionalInt rank = IntStream.range(0, predictedApps.size())
- .filter(index -> key.equals(predictedApps.get(index).getComponentKey()))
- .findFirst();
- if (!rank.isPresent()) {
- return;
- }
-
- int cardinality = 0;
- for (PredictedAppIcon icon : getPredictedIcons()) {
- ItemInfo info = (ItemInfo) icon.getTag();
- cardinality |= 1 << info.screenId;
- }
-
- PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
- containerBuilder.setCardinality(cardinality);
- if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
- containerBuilder.setIndex(rank.getAsInt());
- }
- mLauncher.getStatsLogManager().logger()
- .withInstanceId(instanceId)
- .withRank(rank.getAsInt())
- .withContainerInfo(ContainerInfo.newBuilder()
- .setPredictedHotseatContainer(containerBuilder)
- .build())
- .log(LAUNCHER_HOTSEAT_RANKED);
- }
-
- private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
-
- private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
- super(R.drawable.ic_pin, R.string.pin_prediction, target,
- itemInfo);
- }
-
- @Override
- public void onClick(View view) {
- dismissTaskMenuView(mTarget);
- pinPrediction(mItemInfo);
- }
- }
-
- /**
- * Fill in predicted_rank field based on app prediction.
- * Only applicable when {@link ItemInfo#itemType} is PREDICTED_HOTSEAT
- */
- public static void encodeHotseatLayoutIntoPredictionRank(
- @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
- QuickstepLauncher launcher = QuickstepLauncher.ACTIVITY_TRACKER.getCreatedActivity();
- if (launcher == null || launcher.getHotseatPredictionController() == null
- || itemInfo.getTargetComponent() == null) {
- return;
- }
- HotseatPredictionController controller = launcher.getHotseatPredictionController();
-
- final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
-
- final List<ComponentKeyMapper> predictedApps = controller.mComponentKeyMappers;
- OptionalInt rank = IntStream.range(0, predictedApps.size())
- .filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
- .findFirst();
-
- target.predictedRank = 10000 + (controller.mPredictedSpotsCount * 100)
- + (rank.isPresent() ? rank.getAsInt() + 1 : 0);
- }
-
- private static boolean isPredictedIcon(View view) {
- return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
- && ((WorkspaceItemInfo) view.getTag()).container
- == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
deleted file mode 100644
index fc9a11b..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.states;
-
-import com.android.launcher3.Launcher;
-
-public class OverviewPeekState extends OverviewState {
- private static final float OVERVIEW_OFFSET = 0.7f;
-
- public OverviewPeekState(int id) {
- super(id);
- }
-
- @Override
- public float[] getOverviewScaleAndOffset(Launcher launcher) {
- return new float[] {NO_SCALE, OVERVIEW_OFFSET};
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
deleted file mode 100644
index daa1aad..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ /dev/null
@@ -1,246 +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.launcher3.uioverrides.states;
-
-import static android.view.View.VISIBLE;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-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.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
-import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
-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.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_1_2;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-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_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
-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_SCRIM_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_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.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.RecentsAtomicAnimationFactory;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Animation factory for quickstep specific transitions
- */
-public class QuickstepAtomicAnimationFactory extends
- RecentsAtomicAnimationFactory<Launcher, LauncherState> {
-
- // Scale recents takes before animating in
- private static final float RECENTS_PREPARE_SCALE = 1.33f;
-
- public static final int INDEX_SHELF_ANIM = RecentsAtomicAnimationFactory.NEXT_INDEX + 0;
- public static final int INDEX_PAUSE_TO_OVERVIEW_ANIM =
- RecentsAtomicAnimationFactory.NEXT_INDEX + 1;
-
- private static final int MY_ANIM_COUNT = 2;
- protected static final int NEXT_INDEX = RecentsAtomicAnimationFactory.NEXT_INDEX
- + MY_ANIM_COUNT;
-
- // Due to use of physics, duration may differ between devices so we need to calculate and
- // cache the value.
- private int mHintToNormalDuration = -1;
-
- public static final long ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW = 300;
-
- public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
- super(activity, MY_ANIM_COUNT);
- }
-
- @Override
- public Animator createStateElementAnimation(int index, float... values) {
- switch (index) {
- case INDEX_SHELF_ANIM: {
- AllAppsTransitionController aatc = mActivity.getAllAppsController();
- Animator springAnim = aatc.createSpringAnimation(values);
-
- if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
- // Translate hotseat with the shelf until reaching overview.
- float overviewProgress = OVERVIEW.getVerticalProgress(mActivity);
- ScaleAndTranslation sat = OVERVIEW.getHotseatScaleAndTranslation(mActivity);
- float shiftRange = aatc.getShiftRange();
- if (values.length == 1) {
- values = new float[] {aatc.getProgress(), values[0]};
- }
- ValueAnimator hotseatAnim = ValueAnimator.ofFloat(values);
- hotseatAnim.addUpdateListener(anim -> {
- float progress = (Float) anim.getAnimatedValue();
- if (progress >= overviewProgress || mActivity.isInState(BACKGROUND_APP)) {
- float hotseatShift = (progress - overviewProgress) * shiftRange;
- mActivity.getHotseat().setTranslationY(hotseatShift + sat.translationY);
- }
- });
- hotseatAnim.setInterpolator(LINEAR);
- hotseatAnim.setDuration(springAnim.getDuration());
-
- AnimatorSet anim = new AnimatorSet();
- anim.play(hotseatAnim);
- anim.play(springAnim);
- return anim;
- }
-
- return springAnim;
- }
- case INDEX_PAUSE_TO_OVERVIEW_ANIM: {
- StateAnimationConfig config = new StateAnimationConfig();
- config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
-
- config.setInterpolator(ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_3);
- if ((OVERVIEW.getVisibleElements(mActivity) & HOTSEAT_ICONS) != 0) {
- config.setInterpolator(ANIM_HOTSEAT_SCALE, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_HOTSEAT_TRANSLATE, OVERSHOOT_1_2);
- }
-
- StateManager<LauncherState> stateManager = mActivity.getStateManager();
- return stateManager.createAtomicAnimation(
- stateManager.getCurrentStableState(), OVERVIEW, config);
- }
- default:
- return super.createStateElementAnimation(index, values);
- }
- }
-
- @Override
- public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
- StateAnimationConfig config) {
- if (toState == NORMAL && fromState == OVERVIEW) {
- config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
- config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
- config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
- config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
-
- if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
- config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
- } else {
- config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
- }
-
- 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;
- if (isWorkspaceVisible) {
- CellLayout currentChild = (CellLayout) workspace.getChildAt(
- workspace.getCurrentPage());
- isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
- && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
- }
- if (!isWorkspaceVisible) {
- workspace.setScaleX(0.92f);
- workspace.setScaleY(0.92f);
- }
- Hotseat hotseat = mActivity.getHotseat();
- boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
- if (!isHotseatVisible) {
- hotseat.setScaleX(0.92f);
- hotseat.setScaleY(0.92f);
- if (ENABLE_OVERVIEW_ACTIONS.get()) {
- AllAppsContainerView qsbContainer = mActivity.getAppsView();
- View qsb = qsbContainer.getSearchView();
- boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
- if (!qsbVisible) {
- qsbContainer.setScaleX(0.92f);
- qsbContainer.setScaleY(0.92f);
- }
- }
- }
- } else if (toState == NORMAL && fromState == OVERVIEW_PEEK) {
- // Keep fully visible until the very end (when overview is offscreen) to make invisible.
- config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
- } else if (toState == OVERVIEW_PEEK && fromState == NORMAL) {
- config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
- config.setInterpolator(ANIM_OVERVIEW_SCRIM_FADE, FAST_OUT_SLOW_IN);
- } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
- if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
- config.setInterpolator(ANIM_WORKSPACE_SCALE,
- fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
- config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
- config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
- } else {
- config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
-
- // Scale up the recents, if it is not coming from the side
- RecentsView overview = mActivity.getOverviewPanel();
- if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
- RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
- }
- }
- config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
- config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
- Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()
- && removeShelfFromOverview(mActivity)
- ? OVERSHOOT_1_2
- : OVERSHOOT_1_7;
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, translationInterpolator);
- config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, translationInterpolator);
- } else if (fromState == HINT_STATE && toState == NORMAL) {
- config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
- if (mHintToNormalDuration == -1) {
- ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
- toState.getWorkspaceScaleAndTranslation(mActivity).scale);
- mHintToNormalDuration = (int) va.getDuration();
- }
- config.duration = Math.max(config.duration, mHintToNormalDuration);
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
deleted file mode 100644
index fac478e..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-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.LauncherState.OVERVIEW_PEEK;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
-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.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_PEEK;
-import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.VibratorWrapper;
-import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Touch controller which handles swipe and hold to go to Overview
- */
-public class FlingAndHoldTouchController extends PortraitStatesTouchController {
-
- private static final long PEEK_IN_ANIM_DURATION = 240;
- private static final long PEEK_OUT_ANIM_DURATION = 100;
- private static final float MAX_DISPLACEMENT_PERCENT = 0.75f;
-
- protected final MotionPauseDetector mMotionPauseDetector;
- private final float mMotionPauseMinDisplacement;
- private final float mMotionPauseMaxDisplacement;
-
- private AnimatorSet mPeekAnim;
-
- public FlingAndHoldTouchController(Launcher l) {
- super(l, false /* allowDragToOverview */);
- mMotionPauseDetector = new MotionPauseDetector(l);
- mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
- mMotionPauseMaxDisplacement = getMotionPauseMaxDisplacement();
- }
-
- protected float getMotionPauseMaxDisplacement() {
- return getShiftRange() * MAX_DISPLACEMENT_PERCENT;
- }
-
- @Override
- protected long getAtomicDuration() {
- return QuickstepAtomicAnimationFactory.ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
- }
-
- @Override
- public void onDragStart(boolean start, float startDisplacement) {
- mMotionPauseDetector.clear();
-
- super.onDragStart(start, startDisplacement);
-
- if (handlingOverviewAnim()) {
- mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
- }
-
- if (mAtomicAnim != null) {
- mAtomicAnim.cancel();
- }
- }
-
- protected void onMotionPauseChanged(boolean isPaused) {
- RecentsView recentsView = mLauncher.getOverviewPanel();
- recentsView.setOverviewStateEnabled(isPaused);
- if (mPeekAnim != null) {
- mPeekAnim.cancel();
- }
- LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
- LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
- long peekDuration = isPaused ? PEEK_IN_ANIM_DURATION : PEEK_OUT_ANIM_DURATION;
-
- StateAnimationConfig config = new StateAnimationConfig();
- config.duration = peekDuration;
- config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
- mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
- fromState, toState, config);
- mPeekAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mPeekAnim = null;
- }
- });
- mPeekAnim.start();
- VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
-
- mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(isPaused ? 0 : 1)
- .setDuration(peekDuration).start();
- }
-
- /**
- * @return Whether we are handling the overview animation, rather than
- * having it as part of the existing animation to the target state.
- */
- protected boolean handlingOverviewAnim() {
- int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
- return mStartState == NORMAL && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
- }
-
- @Override
- protected StateAnimationConfig getConfigForStates(
- LauncherState fromState, LauncherState toState) {
- if (fromState == NORMAL && toState == ALL_APPS) {
- StateAnimationConfig builder = new StateAnimationConfig();
- // Fade in prediction icons quickly, then rest of all apps after reaching overview.
- float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
- - OVERVIEW.getVerticalProgress(mLauncher);
- builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
- ACCEL,
- 0,
- ALL_APPS_CONTENT_FADE_THRESHOLD));
- builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
- ACCEL,
- progressToReachOverview,
- progressToReachOverview + ALL_APPS_CONTENT_FADE_THRESHOLD));
-
- // Get workspace out of the way quickly, to prepare for potential pause.
- builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
- builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3);
- builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
- return builder;
- } else if (fromState == ALL_APPS && toState == NORMAL) {
- StateAnimationConfig builder = new StateAnimationConfig();
- // Keep all apps/predictions opaque until the very end of the transition.
- float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);
- builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
- DEACCEL,
- progressToReachOverview - ALL_APPS_CONTENT_FADE_THRESHOLD,
- progressToReachOverview));
- builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
- DEACCEL,
- 1 - ALL_APPS_CONTENT_FADE_THRESHOLD,
- 1));
- return builder;
- }
- return super.getConfigForStates(fromState, toState);
- }
-
- @Override
- public boolean onDrag(float displacement, MotionEvent event) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "FlingAndHoldTouchController");
- }
- float upDisplacement = -displacement;
- mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
- || upDisplacement < mMotionPauseMinDisplacement
- || upDisplacement > mMotionPauseMaxDisplacement);
- mMotionPauseDetector.addPosition(event);
- return super.onDrag(displacement, event);
- }
-
- @Override
- public void onDragEnd(float velocity) {
- if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
- goToOverviewOnDragEnd(velocity);
- } else {
- super.onDragEnd(velocity);
- }
-
- View searchView = mLauncher.getAppsView().getSearchView();
- if (searchView instanceof FeedbackHandler) {
- ((FeedbackHandler) searchView).resetFeedback();
- }
- mMotionPauseDetector.clear();
- }
-
- protected void goToOverviewOnDragEnd(float velocity) {
- if (mPeekAnim != null) {
- mPeekAnim.cancel();
- }
-
- Animator overviewAnim = mLauncher.createAtomicAnimationFactory()
- .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM);
- mAtomicAnim = new AnimatorSet();
- mAtomicAnim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- if (mCancelled) {
- StateAnimationConfig config = new StateAnimationConfig();
- config.animFlags = PLAY_ATOMIC_OVERVIEW_PEEK;
- config.duration = PEEK_OUT_ANIM_DURATION;
- mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(
- mFromState, mToState, config);
- mPeekAnim.start();
- }
- mAtomicAnim = null;
- }
- });
- mAtomicAnim.play(overviewAnim);
- mAtomicAnim.start();
- }
-
- @Override
- protected void goToTargetState(LauncherState targetState, int logAction) {
- if (mPeekAnim != null && mPeekAnim.isStarted()) {
- // Don't jump to the target state until overview is no longer peeking.
- mPeekAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- FlingAndHoldTouchController.super.goToTargetState(targetState, logAction);
- }
- });
- } else {
- super.goToTargetState(targetState, logAction);
- }
- }
-
- @Override
- @AnimationFlags
- protected int updateAnimComponentsOnReinit(@AnimationFlags int animComponents) {
- if (handlingOverviewAnim()) {
- // We don't want the state transition to all apps to animate overview,
- // as that will cause a jump after our atomic animation.
- return animComponents | SKIP_OVERVIEW;
- } else {
- return animComponents;
- }
- }
-
- /**
- * Interface for views with feedback animation requiring reset
- */
- public interface FeedbackHandler {
-
- /**
- * reset searchWidget feedback
- */
- void resetFeedback();
- }
-
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
deleted file mode 100644
index 9091168..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.quickstep.TouchInteractionService;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Touch controller from going from OVERVIEW to ALL_APPS.
- *
- * This is used in landscape mode. It is also used in portrait mode for the fallback recents.
- */
-public class OverviewToAllAppsTouchController extends PortraitStatesTouchController {
-
- public OverviewToAllAppsTouchController(Launcher l) {
- super(l, true /* allowDragToOverview */);
- }
-
- @Override
- protected boolean canInterceptTouch(MotionEvent ev) {
- if (mCurrentAnimation != null) {
- // If we are already animating from a previous state, we can intercept.
- return true;
- }
- if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
- return false;
- }
- if (mLauncher.isInState(ALL_APPS)) {
- // In all-apps only listen if the container cannot scroll itself
- return mLauncher.getAppsView().shouldContainerScroll(ev);
- } else if (mLauncher.isInState(NORMAL)) {
- return (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0;
- } else if (mLauncher.isInState(OVERVIEW)) {
- RecentsView rv = mLauncher.getOverviewPanel();
- return ev.getY() > (rv.getBottom() - rv.getPaddingBottom());
- } else {
- return false;
- }
- }
-
- @Override
- protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- if (fromState == ALL_APPS && !isDragTowardPositive) {
- // Should swipe down go to OVERVIEW instead?
- return TouchInteractionService.isConnected() ?
- mLauncher.getStateManager().getLastState() : NORMAL;
- } else if (isDragTowardPositive) {
- return ALL_APPS;
- }
- return fromState;
- }
-
- @Override
- protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return LauncherLogProto.ContainerType.WORKSPACE;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
deleted file mode 100644
index c90f701..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ /dev/null
@@ -1,394 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static android.widget.Toast.LENGTH_SHORT;
-
-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.VibratorWrapper.OVERVIEW_HAPTIC;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.VibratorWrapper;
-import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.InputConsumerProxy;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TransformParams;
-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.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Base class for swipe up handler with some utility methods
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
- extends SwipeUpAnimationLogic implements RecentsAnimationListener {
-
- private static final String TAG = "BaseSwipeUpHandler";
-
- protected final BaseActivityInterface<?, T> mActivityInterface;
- protected final InputConsumerProxy mInputConsumerProxy;
-
- protected final ActivityInitListener mActivityInitListener;
-
- protected RecentsAnimationController mRecentsAnimationController;
- protected RecentsAnimationTargets mRecentsAnimationTargets;
-
- // Callbacks to be made once the recents animation starts
- private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
-
- protected T mActivity;
- protected Q mRecentsView;
-
- protected Runnable mGestureEndCallback;
-
- protected MultiStateCallback mStateCallback;
-
- protected boolean mCanceled;
-
- private boolean mRecentsViewScrollLinked = false;
-
- protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, new TransformParams());
- mActivityInterface = gestureState.getActivityInterface();
- mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
- mInputConsumerProxy =
- new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
- }
-
- /**
- * 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(
- mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
- }
-
- protected void performHapticFeedback() {
- VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
- }
-
- public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
- return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
- }
-
- public void setGestureEndCallback(Runnable gestureEndCallback) {
- mGestureEndCallback = gestureEndCallback;
- }
-
- public abstract Intent getLaunchIntent();
-
- protected void linkRecentsViewScroll() {
- SurfaceTransactionApplier.create(mRecentsView, applier -> {
- mTransformParams.setSyncTransactionApplier(applier);
- runOnRecentsAnimationStart(() ->
- mRecentsAnimationTargets.addReleaseCheck(applier));
- });
-
- mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
- if (moveWindowWithRecentsScroll()) {
- updateFinalShift();
- }
- });
- runOnRecentsAnimationStart(() ->
- mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
- mRecentsAnimationTargets));
- mRecentsViewScrollLinked = true;
- }
-
- protected void startNewTask(Consumer<Boolean> resultCallback) {
- // Launch the task user scrolled to (mRecentsView.getNextPage()).
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- // We finish recents animation inside launchTask() when live tile is enabled.
- mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
- true /* freezeTaskList */);
- } else {
- if (!mCanceled) {
- TaskView nextTask = mRecentsView.getNextPageTaskView();
- if (nextTask != null) {
- int taskId = nextTask.getTask().key.id;
- mGestureState.updateLastStartedTaskId(taskId);
- boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
- .contains(taskId);
- nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
- success -> {
- resultCallback.accept(success);
- if (success) {
- if (hasTaskPreviouslyAppeared) {
- onRestartPreviouslyAppearedTask();
- }
- } else {
- mActivityInterface.onLaunchTaskFailed();
- nextTask.notifyTaskLaunchFailed(TAG);
- mRecentsAnimationController.finish(true /* toRecents */, null);
- }
- }, MAIN_EXECUTOR.getHandler());
- } else {
- mActivityInterface.onLaunchTaskFailed();
- Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
- mRecentsAnimationController.finish(true /* toRecents */, null);
- }
- }
- mCanceled = false;
- }
- }
-
- /**
- * Called when we successfully startNewTask() on the task that was previously running. Normally
- * we call resumeLastTask() when returning to the previously running task, but this handles a
- * specific edge case: if we switch from A to B, and back to A before B appears, we need to
- * start A again to ensure it stays on top.
- */
- @CallSuper
- protected void onRestartPreviouslyAppearedTask() {
- // Finish the controller here, since we won't get onTaskAppeared() for a task that already
- // appeared.
- if (mRecentsAnimationController != null) {
- mRecentsAnimationController.finish(false, null);
- }
- }
-
- /**
- * Runs the given {@param action} if the recents animation has already started, or queues it to
- * be run when it is next started.
- */
- protected void runOnRecentsAnimationStart(Runnable action) {
- if (mRecentsAnimationTargets == null) {
- mRecentsAnimationStartCallbacks.add(action);
- } else {
- action.run();
- }
- }
-
- /**
- * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
- * @return whether the recents animation has started and there are valid app targets.
- */
- protected boolean hasTargets() {
- return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
- }
-
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
- RecentsAnimationTargets targets) {
- mRecentsAnimationController = recentsAnimationController;
- mRecentsAnimationTargets = targets;
- mTransformParams.setTargetSet(mRecentsAnimationTargets);
- RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
- mGestureState.getRunningTaskId());
-
- if (runningTaskTarget != null) {
- mTaskViewSimulator.setPreview(runningTaskTarget);
- }
-
- // 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) {
- DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
- if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
- Rect overviewStackBounds = mActivityInterface
- .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
- dp = dp.getMultiWindowProfile(mContext,
- new WindowBounds(overviewStackBounds, targets.homeContentInsets));
- } else {
- // If we are not in multi-window mode, home insets should be same as system insets.
- dp = dp.copy(mContext);
- }
- dp.updateInsets(targets.homeContentInsets);
- dp.updateIsSeascape(mContext);
- initTransitionEndpoints(dp);
- }
-
- // Notify when the animation starts
- if (!mRecentsAnimationStartCallbacks.isEmpty()) {
- for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
- action.run();
- }
- mRecentsAnimationStartCallbacks.clear();
- }
- }
-
- @Override
- public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
- if (mRecentsView != null) {
- mRecentsView.setRecentsAnimationTargets(null, null);
- }
- }
-
- @Override
- public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
- if (mRecentsView != null) {
- mRecentsView.setRecentsAnimationTargets(null, null);
- }
- }
-
- @Override
- public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
- if (mRecentsAnimationController != null) {
- if (handleTaskAppeared(appearedTaskTarget)) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- mActivityInterface.onLaunchTaskSuccess();
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
- }
- }
- }
-
- /** @return Whether this was the task we were waiting to appear, and thus handled it. */
- protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
-
- /**
- * @return The index of the TaskView in RecentsView whose taskId matches the task that will
- * resume if we finish the controller.
- */
- protected int getLastAppearedTaskIndex() {
- return mGestureState.getLastAppearedTaskId() != -1
- ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
- : mRecentsView.getRunningTaskIndex();
- }
-
- /**
- * @return Whether we are continuing a gesture that already landed on a new task,
- * but before that task appeared.
- */
- protected boolean hasStartedNewTask() {
- return mGestureState.getLastStartedTaskId() != -1;
- }
-
- /**
- * Return true if the window should be translated horizontally if the recents view scrolls
- */
- protected abstract boolean moveWindowWithRecentsScroll();
-
- protected boolean onActivityInit(Boolean alreadyOnHome) {
- T createdActivity = mActivityInterface.getCreatedActivity();
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
- }
- if (createdActivity != null) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
- }
- initTransitionEndpoints(createdActivity.getDeviceProfile());
- }
- return true;
- }
-
- /**
- * Called to create a input proxy for the running task
- */
- @UiThread
- protected abstract InputConsumer createNewInputProxyHandler();
-
- /**
- * Called when the value of {@link #mCurrentShift} changes
- */
- @UiThread
- public abstract void updateFinalShift();
-
- /**
- * Called when motion pause is detected
- */
- public abstract void onMotionPauseChanged(boolean isPaused);
-
- @UiThread
- public void onGestureStarted(boolean isLikelyToStartNewTask) { }
-
- @UiThread
- public abstract void onGestureCancelled();
-
- @UiThread
- public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
-
- public abstract void onConsumerAboutToBeSwitched();
-
- public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
-
- /**
- * Registers a callback to run when the activity is ready.
- * @param intent The intent that will be used to start the activity if it doesn't exist already.
- */
- public void initWhenReady(Intent intent) {
- // Preload the plan
- RecentsModel.INSTANCE.get(mContext).getTasks(null);
-
- mActivityInitListener.register(intent);
- }
-
- /**
- * Applies the transform on the recents animation
- */
- protected void applyWindowTransform() {
- if (mWindowTransitionController != null) {
- mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
- }
- if (mRecentsAnimationTargets != null) {
- if (mRecentsViewScrollLinked) {
- mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
- }
- mTaskViewSimulator.apply(mTransformParams);
- }
- }
-
- @Override
- protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
- HomeAnimationFactory homeAnimationFactory) {
- RectFSpringAnim anim =
- super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
- if (mRecentsAnimationTargets != null) {
- mRecentsAnimationTargets.addReleaseCheck(anim);
- }
- return anim;
- }
-
- public interface Factory {
-
- BaseSwipeUpHandler newHandler(
- GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
deleted file mode 100644
index e2e25f3..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Matrix;
-import android.graphics.Matrix.ScaleToFit;
-import android.graphics.RectF;
-import android.os.Build;
-import android.view.View;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.statehandlers.DepthController;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TaskViewSimulator;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskThumbnailView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
- */
-@TargetApi(Build.VERSION_CODES.R)
-public final class TaskViewUtils {
-
- private TaskViewUtils() {}
-
- /**
- * Try to find a TaskView that corresponds with the component of the launched view.
- *
- * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
- * Otherwise, we will assume we are using a normal app transition, but it's possible that the
- * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
- */
- public static TaskView findTaskViewToLaunch(
- BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
- RecentsView recentsView = activity.getOverviewPanel();
- if (v instanceof TaskView) {
- TaskView taskView = (TaskView) v;
- return recentsView.isTaskViewVisible(taskView) ? taskView : null;
- }
-
- // It's possible that the launched view can still be resolved to a visible task view, check
- // the task id of the opening task and see if we can find a match.
- if (v.getTag() instanceof ItemInfo) {
- ItemInfo itemInfo = (ItemInfo) v.getTag();
- ComponentName componentName = itemInfo.getTargetComponent();
- int userId = itemInfo.user.getIdentifier();
- if (componentName != null) {
- for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
- TaskView taskView = recentsView.getTaskViewAt(i);
- if (recentsView.isTaskViewVisible(taskView)) {
- Task.TaskKey key = taskView.getTask().key;
- if (componentName.equals(key.getComponent()) && userId == key.userId) {
- return taskView;
- }
- }
- }
- }
- }
-
- if (targets == null) {
- return null;
- }
- // Resolve the opening task id
- int openingTaskId = -1;
- for (RemoteAnimationTargetCompat target : targets) {
- if (target.mode == MODE_OPENING) {
- openingTaskId = target.taskId;
- break;
- }
- }
-
- // If there is no opening task id, fall back to the normal app icon launch animation
- if (openingTaskId == -1) {
- return null;
- }
-
- // If the opening task id is not currently visible in overview, then fall back to normal app
- // icon launch animation
- TaskView taskView = recentsView.getTaskView(openingTaskId);
- if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
- return null;
- }
- return taskView;
- }
-
- /**
- * Creates an animation that controls the window of the opening targets for the recents launch
- * animation.
- */
- public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
- RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
- PendingAnimation out) {
-
- SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
- final RemoteAnimationTargets targets =
- new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
- targets.addReleaseCheck(applier);
-
- TransformParams params = new TransformParams()
- .setSyncTransactionApplier(applier)
- .setTargetSet(targets);
-
- final RecentsView recentsView = v.getRecentsView();
- int taskIndex = recentsView.indexOfChild(v);
- boolean parallaxCenterAndAdjacentTask = taskIndex != recentsView.getCurrentPage();
- int startScroll = recentsView.getScrollOffset(taskIndex);
-
- Context context = v.getContext();
- DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
- // RecentsView never updates the display rotation until swipe-up so the value may be stale.
- // Use the display value instead.
- int displayRotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
-
- TaskViewSimulator topMostSimulator = null;
- if (targets.apps.length > 0) {
- TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
- tsv.setDp(dp);
- tsv.setLayoutRotation(displayRotation, displayRotation);
- tsv.setPreview(targets.apps[targets.apps.length - 1]);
- tsv.fullScreenProgress.value = 0;
- tsv.recentsViewScale.value = 1;
- tsv.setScroll(startScroll);
-
- out.setFloat(tsv.fullScreenProgress,
- AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
- out.setFloat(tsv.recentsViewScale,
- AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
- out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
-
- out.addOnFrameCallback(() -> tsv.apply(params));
- topMostSimulator = tsv;
- }
-
- // Fade in the task during the initial 20% of the animation
- out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
-
- if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
- out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
-
- TaskViewSimulator simulatorToCopy = topMostSimulator;
- simulatorToCopy.apply(params);
-
- // Mt represents the overall transformation on the thumbnailView relative to the
- // Launcher's rootView
- // K(t) represents transformation on the running window by the taskViewSimulator at
- // any time t.
- // at t = 0, we know that the simulator matches the thumbnailView. So if we apply K(0)`
- // on the Launcher's rootView, the thumbnailView would match the full running task
- // window. If we apply "K(0)` K(t)" thumbnailView will match the final transformed
- // window at any time t. This gives the overall matrix on thumbnailView to be:
- // Mt K(0)` K(t)
- // During animation we apply transformation on the thumbnailView (and not the rootView)
- // to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
- // Mt K(0)` K(t) Mt`
- TaskThumbnailView ttv = v.getThumbnail();
- RectF tvBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
- float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
- getDescendantCoordRelativeToAncestor(ttv, ttv.getRootView(), tvBoundsMapped, false);
- RectF tvBoundsInRoot = new RectF(
- tvBoundsMapped[0], tvBoundsMapped[1],
- tvBoundsMapped[2], tvBoundsMapped[3]);
-
- Matrix mt = new Matrix();
- mt.setRectToRect(tvBounds, tvBoundsInRoot, ScaleToFit.FILL);
-
- Matrix mti = new Matrix();
- mt.invert(mti);
-
- Matrix k0i = new Matrix();
- simulatorToCopy.getCurrentMatrix().invert(k0i);
-
- Matrix animationMatrix = new Matrix();
- out.addOnFrameCallback(() -> {
- animationMatrix.set(mt);
- animationMatrix.postConcat(k0i);
- animationMatrix.postConcat(simulatorToCopy.getCurrentMatrix());
- animationMatrix.postConcat(mti);
- ttv.setAnimationMatrix(animationMatrix);
- });
-
- out.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- ttv.setAnimationMatrix(null);
- }
- });
- }
-
- out.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- targets.release();
- }
- });
-
- if (depthController != null) {
- out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
- TOUCH_RESPONSE_INTERPOLATOR);
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java
deleted file mode 100644
index cbb6ad4..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/ViewUtils.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import android.graphics.Canvas;
-import android.view.View;
-
-import com.android.systemui.shared.system.WindowCallbacksCompat;
-
-import java.util.function.BooleanSupplier;
-
-/**
- * Utility class for helpful methods related to {@link View} objects.
- */
-public class ViewUtils {
-
- /** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */
- public static boolean postDraw(View view, Runnable onFinishRunnable) {
- return postDraw(view, onFinishRunnable, () -> false);
- }
-
- /**
- * Inject some addition logic in order to make sure that the view is updated smoothly post
- * draw, and allow addition task to be run after view update.
- *
- * @param onFinishRunnable runnable to be run right after the view finishes drawing.
- */
- public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
- // Defer finishing the animation until the next launcher frame with the
- // new thumbnail
- return new WindowCallbacksCompat(view) {
- // The number of frames to defer until we actually finish the animation
- private int mDeferFrameCount = 2;
-
- @Override
- public void onPostDraw(Canvas canvas) {
- // If we were cancelled after this was attached, do not update
- // the state.
- if (canceled.getAsBoolean()) {
- detach();
- return;
- }
-
- if (mDeferFrameCount > 0) {
- mDeferFrameCount--;
- // Workaround, detach and reattach to invalidate the root node for
- // another draw
- detach();
- attach();
- view.invalidate();
- return;
- }
-
- if (onFinishRunnable != null) {
- onFinishRunnable.run();
- }
- detach();
- }
- }.attach();
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
deleted file mode 100644
index e000803..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/logging/UserEventDispatcherAppPredictionExtension.java
+++ /dev/null
@@ -1,49 +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.quickstep.logging;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-
-import java.util.ArrayList;
-
-/**
- * This class handles AOSP MetricsLogger function calls and logging around
- * quickstep interactions and app launches.
- */
-@SuppressWarnings("unused")
-public class UserEventDispatcherAppPredictionExtension extends UserEventDispatcherExtension {
-
- public static final int ALL_APPS_PREDICTION_TIPS = 2;
-
- private static final String TAG = "UserEventDispatcher";
-
- public UserEventDispatcherAppPredictionExtension(Context context) {
- super(context);
- }
-
- @Override
- protected void onFillInLogContainerData(
- @NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target,
- @NonNull ArrayList<LauncherLogProto.Target> targets) {
- PredictionUiStateManager.fillInPredictedRank(itemInfo, target);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java
deleted file mode 100644
index 190763a..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ProtoTracer.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.quickstep.util;
-
-import static com.android.launcher3.tracing.nano.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_H;
-import static com.android.launcher3.tracing.nano.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_L;
-
-import android.content.Context;
-import android.os.SystemClock;
-
-import com.android.launcher3.tracing.nano.LauncherTraceProto;
-import com.android.launcher3.tracing.nano.LauncherTraceEntryProto;
-import com.android.launcher3.tracing.nano.LauncherTraceFileProto;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.systemui.shared.tracing.FrameProtoTracer;
-import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
-import com.android.systemui.shared.tracing.ProtoTraceable;
-import com.google.protobuf.nano.MessageNano;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Queue;
-
-
-/**
- * Controller for coordinating winscope proto tracing.
- */
-public class ProtoTracer implements ProtoTraceParams<MessageNano,
- LauncherTraceFileProto, LauncherTraceEntryProto, LauncherTraceProto> {
-
- public static final MainThreadInitializedObject<ProtoTracer> INSTANCE =
- new MainThreadInitializedObject<>(ProtoTracer::new);
-
- private static final String TAG = "ProtoTracer";
- private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-
- private final Context mContext;
- private final FrameProtoTracer<MessageNano,
- LauncherTraceFileProto, LauncherTraceEntryProto, LauncherTraceProto> mProtoTracer;
-
- public ProtoTracer(Context context) {
- mContext = context;
- mProtoTracer = new FrameProtoTracer<>(this);
- }
-
- @Override
- public File getTraceFile() {
- return new File(mContext.getFilesDir(), "launcher_trace.pb");
- }
-
- @Override
- public LauncherTraceFileProto getEncapsulatingTraceProto() {
- return new LauncherTraceFileProto();
- }
-
- @Override
- public LauncherTraceEntryProto updateBufferProto(LauncherTraceEntryProto reuseObj,
- ArrayList<ProtoTraceable<LauncherTraceProto>> traceables) {
- LauncherTraceEntryProto proto = reuseObj != null
- ? reuseObj
- : new LauncherTraceEntryProto();
- proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
- proto.launcher = proto.launcher != null ? proto.launcher : new LauncherTraceProto();
- for (ProtoTraceable t : traceables) {
- t.writeToProto(proto.launcher);
- }
- return proto;
- }
-
- @Override
- public byte[] serializeEncapsulatingProto(LauncherTraceFileProto encapsulatingProto,
- Queue<LauncherTraceEntryProto> buffer) {
- encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE;
- encapsulatingProto.entry = buffer.toArray(new LauncherTraceEntryProto[0]);
- return MessageNano.toByteArray(encapsulatingProto);
- }
-
- @Override
- public byte[] getProtoBytes(MessageNano proto) {
- return MessageNano.toByteArray(proto);
- }
-
- @Override
- public int getProtoSize(MessageNano proto) {
- return proto.getCachedSize();
- }
-
- public void start() {
- mProtoTracer.start();
- }
-
- public void stop() {
- mProtoTracer.stop();
- }
-
- public void add(ProtoTraceable<LauncherTraceProto> traceable) {
- mProtoTracer.add(traceable);
- }
-
- public void remove(ProtoTraceable<LauncherTraceProto> traceable) {
- mProtoTracer.remove(traceable);
- }
-
- public void scheduleFrameUpdate() {
- mProtoTracer.scheduleFrameUpdate();
- }
-
- public void update() {
- mProtoTracer.update();
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
deleted file mode 100644
index 85006da..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ShelfPeekAnim.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.util;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.uioverrides.states.OverviewState;
-
-/**
- * Animates the shelf between states HIDE, PEEK, and OVERVIEW.
- */
-public class ShelfPeekAnim {
-
- public static final Interpolator INTERPOLATOR = OVERSHOOT_1_2;
- public static final long DURATION = 240;
-
- private final Launcher mLauncher;
-
- private ShelfAnimState mShelfState;
- private boolean mIsPeeking;
-
- public ShelfPeekAnim(Launcher launcher) {
- mLauncher = launcher;
- }
-
- /**
- * Animates to the given state, canceling the previous animation if it was still running.
- */
- public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
- if (mShelfState == shelfState || FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
- return;
- }
- mLauncher.getStateManager().cancelStateElementAnimation(INDEX_SHELF_ANIM);
- mShelfState = shelfState;
- mIsPeeking = mShelfState == ShelfAnimState.PEEK || mShelfState == ShelfAnimState.HIDE;
- if (mShelfState == ShelfAnimState.CANCEL) {
- return;
- }
- float shelfHiddenProgress = BACKGROUND_APP.getVerticalProgress(mLauncher);
- float shelfOverviewProgress = OVERVIEW.getVerticalProgress(mLauncher);
- // Peek based on default overview progress so we can see hotseat if we're showing
- // that instead of predictions in overview.
- float defaultOverviewProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
- float shelfPeekingProgress = shelfHiddenProgress
- - (shelfHiddenProgress - defaultOverviewProgress) * 0.25f;
- float toProgress = mShelfState == ShelfAnimState.HIDE
- ? shelfHiddenProgress
- : mShelfState == ShelfAnimState.PEEK
- ? shelfPeekingProgress
- : shelfOverviewProgress;
- Animator shelfAnim = mLauncher.getStateManager()
- .createStateElementAnimation(INDEX_SHELF_ANIM, toProgress);
- shelfAnim.setInterpolator(interpolator);
- shelfAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationCancel(Animator animation) {
- mShelfState = ShelfAnimState.CANCEL;
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- mIsPeeking = mShelfState == ShelfAnimState.PEEK;
- }
- });
- shelfAnim.setDuration(duration).start();
- }
-
- /** @return Whether the shelf is currently peeking or animating to or from peeking. */
- public boolean isPeeking() {
- return mIsPeeking;
- }
-
- /** The various shelf states we can animate to. */
- public enum ShelfAnimState {
- HIDE(true), PEEK(true), OVERVIEW(false), CANCEL(false);
-
- ShelfAnimState(boolean shouldPreformHaptic) {
- this.shouldPreformHaptic = shouldPreformHaptic;
- }
-
- public final boolean shouldPreformHaptic;
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
deleted file mode 100644
index 30c9f77..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package com.android.quickstep.views;
-
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.util.FloatProperty;
-import android.view.ViewOverlay;
-
-import com.android.launcher3.anim.Interpolators;
-
-public class LiveTileOverlay extends Drawable {
-
- private static final long ICON_ANIM_DURATION = 120;
-
- private static final FloatProperty<LiveTileOverlay> PROGRESS =
- new FloatProperty<LiveTileOverlay>("progress") {
- @Override
- public void setValue(LiveTileOverlay liveTileOverlay, float progress) {
- liveTileOverlay.setIconAnimationProgress(progress);
- }
-
- @Override
- public Float get(LiveTileOverlay liveTileOverlay) {
- return liveTileOverlay.mIconAnimationProgress;
- }
- };
-
- public static final LiveTileOverlay INSTANCE = new LiveTileOverlay();
-
- private final Paint mPaint = new Paint();
- private final Rect mBoundsRect = new Rect();
-
- private RectF mCurrentRect;
- private float mCornerRadius;
- private Drawable mIcon;
- private Animator mIconAnimator;
-
- private boolean mDrawEnabled = true;
- private float mIconAnimationProgress = 0f;
- private boolean mIsAttached;
-
- private LiveTileOverlay() {
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
- }
-
- public void update(RectF currentRect, float cornerRadius) {
- invalidateSelf();
-
- mCurrentRect = currentRect;
- mCornerRadius = cornerRadius;
-
- mCurrentRect.roundOut(mBoundsRect);
- setBounds(mBoundsRect);
- invalidateSelf();
- }
-
- public void setIcon(Drawable icon) {
- mIcon = icon;
- }
-
- public void startIconAnimation() {
- if (mIconAnimator != null) {
- mIconAnimator.cancel();
- }
- // This animator must match the icon part of {@link TaskView#FOCUS_TRANSITION} animation.
- mIconAnimator = ObjectAnimator.ofFloat(this, PROGRESS, 1);
- mIconAnimator.setDuration(ICON_ANIM_DURATION).setInterpolator(LINEAR);
- mIconAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mIconAnimator = null;
- }
- });
- mIconAnimator.start();
- }
-
- public float cancelIconAnimation() {
- if (mIconAnimator != null) {
- mIconAnimator.cancel();
- }
- return mIconAnimationProgress;
- }
-
- public void setDrawEnabled(boolean drawEnabled) {
- if (mDrawEnabled != drawEnabled) {
- mDrawEnabled = drawEnabled;
- invalidateSelf();
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mCurrentRect != null && mDrawEnabled) {
- canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
- if (mIcon != null && mIconAnimationProgress > 0f) {
- canvas.save();
- float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
- 1f).getInterpolation(mIconAnimationProgress);
- canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
- mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
- canvas.scale(scale, scale);
- mIcon.draw(canvas);
- canvas.restore();
- }
- }
- }
-
- @Override
- public void setAlpha(int i) { }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) { }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- public boolean attach(ViewOverlay overlay) {
- if (overlay != null && !mIsAttached) {
- overlay.add(this);
- mIsAttached = true;
- return true;
- }
-
- return false;
- }
-
- public void detach(ViewOverlay overlay) {
- if (overlay != null) {
- overlay.remove(this);
- mIsAttached = false;
- }
- }
-
- private void setIconAnimationProgress(float progress) {
- mIconAnimationProgress = progress;
- invalidateSelf();
- }
-}
diff --git a/quickstep/recents_ui_overrides/res/drawable/all_apps_edu_circle.xml b/quickstep/res/drawable/all_apps_edu_circle.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/drawable/all_apps_edu_circle.xml
rename to quickstep/res/drawable/all_apps_edu_circle.xml
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml b/quickstep/res/drawable/chip_hint_background_light.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
rename to quickstep/res/drawable/chip_hint_background_light.xml
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_app_icon.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_app_icon.xml
index f1b1053..e9c9701 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_app_icon.xml
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+ 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.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <solid android:color="@color/gesture_tutorial_fake_task_view_color" />
+</shape>
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
similarity index 70%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
index f1b1053..9c95497 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_app_previous_task_thumbnail.xml
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+ 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.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/gesture_tutorial_fake_previous_task_view_color" />
+</shape>
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
index f1b1053..9d4cc95 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_app_task_thumbnail.xml
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+ 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.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@color/gesture_tutorial_fake_task_view_color" />
+</shape>
diff --git a/go/res/values/dimens.xml b/quickstep/res/drawable/default_sandbox_wallpaper.xml
similarity index 72%
copy from go/res/values/dimens.xml
copy to quickstep/res/drawable/default_sandbox_wallpaper.xml
index f1b1053..816d1d6 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/drawable/default_sandbox_wallpaper.xml
@@ -1,20 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
+<!--
+ 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.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackground" />
+</shape>
diff --git a/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml b/quickstep/res/drawable/hotseat_edu_notification_icon.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
rename to quickstep/res/drawable/hotseat_edu_notification_icon.xml
diff --git a/quickstep/res/drawable/task_menu_bg.xml b/quickstep/res/drawable/task_menu_bg.xml
index 7334d98..a60defc 100644
--- a/quickstep/res/drawable/task_menu_bg.xml
+++ b/quickstep/res/drawable/task_menu_bg.xml
@@ -14,25 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:gravity="bottom">
- <!-- Shadow -->
- <shape>
- <gradient android:angle="270"
- android:endColor="@android:color/transparent"
- android:startColor="#26000000" />
- <size android:height="@dimen/task_card_menu_shadow_height" />
- </shape>
- </item>
- <item android:bottom="@dimen/task_card_menu_shadow_height">
- <!-- Background -->
- <shape>
- <corners
- android:topLeftRadius="?android:attr/dialogCornerRadius"
- android:topRightRadius="?android:attr/dialogCornerRadius"
- android:bottomLeftRadius="0dp"
- android:bottomRightRadius="0dp" />
- <solid android:color="?attr/popupColorPrimary" />
- </shape>
- </item>
-</layer-list>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="?attr/popupColorPrimary" />
+</shape>
diff --git a/quickstep/recents_ui_overrides/res/layout/all_apps_edu_view.xml b/quickstep/res/layout/all_apps_edu_view.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/all_apps_edu_view.xml
rename to quickstep/res/layout/all_apps_edu_view.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
rename to quickstep/res/layout/fallback_recents_activity.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/floating_header_content.xml b/quickstep/res/layout/floating_header_content.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/floating_header_content.xml
rename to quickstep/res/layout/floating_header_content.xml
diff --git a/quickstep/res/layout/gesture_tutorial_fragment.xml b/quickstep/res/layout/gesture_tutorial_fragment.xml
index 43bf0ea..9d06dfb 100644
--- a/quickstep/res/layout/gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/gesture_tutorial_fragment.xml
@@ -13,10 +13,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.quickstep.interaction.RootSandboxLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/colorBackground">
+ android:layout_height="match_parent">
<View
android:id="@+id/gesture_tutorial_ripple_view"
@@ -28,15 +28,20 @@
android:id="@+id/gesture_tutorial_fake_icon_view"
android:layout_width="20dp"
android:layout_height="20dp"
- android:background="@drawable/bg_circle"
- android:backgroundTint="@color/gesture_tutorial_fake_task_view_color"
+ android:visibility="invisible" />
+
+ <View
+ android:id="@+id/gesture_tutorial_fake_previous_task_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleX="0.98"
+ android:scaleY="0.98"
android:visibility="invisible" />
<View
android:id="@+id/gesture_tutorial_fake_task_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@color/gesture_tutorial_fake_task_view_color"
android:visibility="invisible" />
<ImageView
@@ -93,11 +98,11 @@
style="@style/TextAppearance.GestureTutorial.Feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_above="@id/gesture_tutorial_fragment_action_button"
+ android:layout_below="@id/gesture_tutorial_fragment_titles_container"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/gesture_tutorial_feedback_margin_start_end"
android:layout_marginEnd="@dimen/gesture_tutorial_feedback_margin_start_end"
- android:layout_marginBottom="10dp"/>
+ android:layout_marginTop="40dp"/>
<!-- android:stateListAnimator="@null" removes shadow and normal on click behavior (increase
of elevation and shadow) which is replaced by ripple effect in android:foreground -->
@@ -126,4 +131,4 @@
android:background="@null"
android:foreground="?android:attr/selectableItemBackgroundBorderless"
android:stateListAnimator="@null"/>
-</RelativeLayout>
\ No newline at end of file
+</com.android.quickstep.interaction.RootSandboxLayout>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index fc06ba0..34ff91d 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -23,5 +23,4 @@
android:text="@string/recents_clear_all"
android:textColor="?attr/workspaceTextColor"
android:textSize="14sp"
- android:translationY="@dimen/task_thumbnail_half_top_margin"
- />
\ No newline at end of file
+ android:translationY="@dimen/task_thumbnail_half_top_margin" />
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/overview_panel.xml
rename to quickstep/res/layout/overview_panel.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_app_icon.xml b/quickstep/res/layout/predicted_app_icon.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/predicted_app_icon.xml
rename to quickstep/res/layout/predicted_app_icon.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/res/layout/predicted_hotseat_edu.xml
similarity index 100%
rename from quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
rename to quickstep/res/layout/predicted_hotseat_edu.xml
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index f9bb2f2..0f9a6aa 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -17,8 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipChildren="false"
android:defaultFocusHighlightEnabled="false"
- android:elevation="4dp"
android:focusable="true">
<com.android.quickstep.views.TaskThumbnailView
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index 098b34f..3916ff9 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -24,20 +24,12 @@
android:orientation="vertical"
android:visibility="invisible">
- <com.android.quickstep.views.IconView
- android:id="@+id/task_icon"
- android:layout_width="@dimen/task_thumbnail_icon_size"
- android:layout_height="@dimen/task_thumbnail_icon_size"
- android:layout_gravity="top|center_horizontal"
- android:layout_marginBottom="@dimen/deep_shortcut_drawable_padding"
- android:focusable="false"
- android:importantForAccessibility="no" />
-
<TextView
android:id="@+id/task_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
+ android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:textSize="12sp"/>
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
new file mode 100644
index 0000000..b124b33
--- /dev/null
+++ b/quickstep/res/layout/taskbar.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.launcher3.taskbar.TaskbarContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/taskbar_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <com.android.launcher3.taskbar.TaskbarView
+ android:id="@+id/taskbar_view"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@color/taskbar_background"
+ android:gravity="center"
+ android:animateLayoutChanges="true"/>
+
+</com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/quickstep/res/layout/taskbar_app_icon.xml
similarity index 78%
rename from go/res/values/dimens.xml
rename to quickstep/res/layout/taskbar_app_icon.xml
index f1b1053..6fefdb6 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/layout/taskbar_app_icon.xml
@@ -1,5 +1,5 @@
<?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.
@@ -14,7 +14,4 @@
limitations under the License.
-->
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/quickstep/res/layout/taskbar_divider.xml
similarity index 60%
copy from res/drawable-v24/drag_handle_indicator_shadow.xml
copy to quickstep/res/layout/taskbar_divider.xml
index 774bc38..6e1aa1e 100644
--- a/res/drawable-v24/drag_handle_indicator_shadow.xml
+++ b/quickstep/res/layout/taskbar_divider.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.graphics.ShadowDrawable
+
+<View
xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/drag_handle_indicator_no_shadow"
- android:elevation="@dimen/vertical_drag_handle_elevation" />
+ android:layout_width="@dimen/taskbar_divider_thickness"
+ android:layout_height="@dimen/taskbar_divider_height"
+ android:layout_marginStart="@dimen/taskbar_icon_spacing"
+ android:layout_marginEnd="@dimen/taskbar_icon_spacing"
+ android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/quickstep/res/layout/taskbar_predicted_app_icon.xml
similarity index 78%
copy from go/res/values/dimens.xml
copy to quickstep/res/layout/taskbar_predicted_app_icon.xml
index f1b1053..211ebc8 100644
--- a/go/res/values/dimens.xml
+++ b/quickstep/res/layout/taskbar_predicted_app_icon.xml
@@ -1,5 +1,5 @@
<?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.
@@ -14,7 +14,4 @@
limitations under the License.
-->
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<com.android.launcher3.uioverrides.PredictedAppIcon style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 03d774d..64b8e2c 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Verdeelde skerm"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Speld vas"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vormvry"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oorsig"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Maak toe"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programgebruikinstellings"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vee alles uit"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Programvoorstelle"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Alle programme"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Jou voorspelde programme"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Kry programvoorstelle in die onderste ry van jou tuisskerm"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Programme wat die meeste gebruik word, verskyn hier, en verander op grond van roetines"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Sleep programme van die onderste ry af om programvoorstelle te kry"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Programvoorstelle is in leë spasie bygevoeg"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Programvoorstelle is geaktiveer"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Programvoorstelle is gedeaktiveer"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde program: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Deel"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Skermkiekie"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Jou organisasie laat nie hierdie program toe nie"</string>
</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index bd54283..3daa922 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"የተከፈለ ማያ ገጽ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ነጻ ቅጽ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ማጠቃለያ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ዝጋ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"የመተግበሪያ አጠቃቀም ቅንብሮች"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ሁሉንም አጽዳ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"የቅርብ ጊዜ መተግበሪያዎች"</string>
<string name="task_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 ደቂቃ"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"የመተግበሪያ ጥቆማዎች"</string>
<string name="all_apps_label" msgid="8542784161730910663">"ሁሉም መተግበሪያዎች"</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_accept" msgid="1611544083278999837">"የመተግበሪያ አስተያየት ጥቆማዎችን አግኝ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"አይ፣ አመሰግናለሁ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ቅንብሮች"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"በብዛት ስራ ላይ የዋሉ መተግበሪያዎች እዚህ ይመጣሉ፣ እና በዕለት ተዕለት ተግባራት ላይ በመመስረት ይቀየራሉ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"የመተግበሪያ ጥቆማዎችን ለማግኘት መተግበሪያዎችን ከታችኛው ረድፍ ይጎትቱ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"የመተግበሪያ አስተያየት ጥቆማዎች ወደ ባዶ ቦታ ታክለዋል"</string>
- <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="action_share" msgid="2648470652637092375">"አጋራ"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ቅጽበታዊ ገጽ እይታ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ይህ ድርጊት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string>
</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index c2348ec..b036bc1 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"تقسيم الشاشة"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"تثبيت"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"شكل مجاني"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نظرة عامة"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"إغلاق"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"إعدادات استخدام التطبيق"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"محو الكل"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات المستخدمة مؤخرًا"</string>
<string name="task_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">"أقل من دقيقة"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"اقتراحات التطبيقات"</string>
<string name="all_apps_label" msgid="8542784161730910663">"جميع التطبيقات"</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_accept" msgid="1611544083278999837">"رؤية تطبيقات مقترحة"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"لا، شكرًا"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"الإعدادات"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"تظهر هنا التطبيقات الأكثر استخدامًا، ويستند التغيير إلى سلاسل الإجراءات."</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"يمكنك سحب التطبيقات من الصف الأسفل لتلقّي اقتراحات."</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"تمت إضافة التطبيقات المقترحة إلى مساحة فارغة."</string>
- <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="action_share" msgid="2648470652637092375">"مشاركة"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"لقطة شاشة"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"لا يسمح التطبيق أو لا تسمح مؤسستك بهذا الإجراء."</string>
</resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index ff0dc20..c188493 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"বিভাজিত স্ক্ৰীণ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"পিন"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"অৱলোকন"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"কোনো শেহতীয়া বস্তু নাই"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"বন্ধ কৰক"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"এপে ব্যৱহাৰ কৰা ডেটাৰ ছেটিংসমূহ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"সকলো মচক"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"শেহতীয়া এপসমূহ"</string>
<string name="task_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">"< ১ মিনিট"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"এপৰ পৰামৰ্শসমূহ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"সকলো এপ্"</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_accept" msgid="1611544083278999837">"এপৰ পৰামর্শসমূহ পাওক"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"নালাগে, ধন্যবাদ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ছেটিংসমূহ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"সকলোতকৈ বেছিকৈ ব্যৱহৃত এপ্সমূহ ইয়াত প্ৰদর্শিত হয় আৰু ৰুটিনসমূহ ওপৰত ভিত্তি কৰি সলনি হয়"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"এপৰ পৰামর্শসমূহ পাবলৈ একেবাৰে তলৰ শাৰীত থকা এপ্সমূহ টানি আঁতৰাওক"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"খালী ঠাইত এপৰ পৰামর্শসমূহ যোগ কৰা হ\'ল"</string>
- <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="action_share" msgid="2648470652637092375">"শ্বেয়াৰ কৰক"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"স্ক্ৰীনশ্বট"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"এপ্টোৱে অথবা আপোনাৰ প্ৰতিষ্ঠানে এই কাৰ্যটোৰ অনুমতি নিদিয়ে"</string>
</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index 6e169b4..aa8fa53 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Bölünmüş ekran"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Sancın"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Sərbəst rejim"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"İcmal"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Bağlayın"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tətbiq istifadə ayarları"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hamısını silin"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Tətbiq təklifləri"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Bütün tətbiqlər"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Təklif edilən tətbiqlər"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Ana ekranın aşağı sırasında tətbiq təklifləri alın"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Ən çox istifadə edilən tətbiqlər burada görünür və rejimlərə uyğun olaraq dəyişir"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Tətbiq təklifləri əldə etmək üçün tətbiqləri aşağı sıradan kənara sürüşdürün"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Tətbiq təklifləri boş sahəyə əlavə edildi"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Tətbiq təklifləri aktivdir"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tətbiq təklifləri deaktivdir"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Proqnozlaşdırılan tətbiq: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Paylaşın"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Ekran şəkli"</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>
</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 4d12c9b..fbbe9d2 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Podeljeni ekran"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Podešavanja korišćenja aplikacije"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Predlozi aplikacija"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predviđene aplikacije"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Dobijajte predloge aplikacija u donjem redu početnog ekrana"</string>
- <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 direktorijum."</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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Ovde se prikazuju najčešće korišćene aplikacije i menjaju se u zavisnosti od upotrebe"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Prevucite aplikacije iz donjeg reda da biste dobili predloge"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Predlozi aplikacija se dodaju na prazno mesto"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlozi aplikacija su omogućeni"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlozi aplikacija su onemogućeni"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđamo aplikaciju: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Deli"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili organizacija ne dozvoljavaju ovu radnju"</string>
</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index b3f5e52..c4a2772 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Падзяліць экран"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Замацаваць"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Адвольная форма"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Агляд"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыць"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налады выкарыстання праграмы"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ачысціць усё"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Нядаўнія праграмы"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Прапановы праграм"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Усе праграмы"</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">"Атрымл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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Тут з\'яўляюцца праграмы, якімі вы карыстаецеся найбольш часта. Гэты спіс змяняецца на падставе вашых дзеянняў"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Перацягніце праграмы з ніжняга радка, каб атрымаць прапановы праграм"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Прапановы праграм дададзены на свабоднае месца"</string>
- <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="action_share" msgid="2648470652637092375">"Абагуліць"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Здымак экрана"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Гэта дзеянне не дазволена праграмай ці вашай арганізацыяй"</string>
</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 5484bc9..9e8c54a 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Разделен екран"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Фиксиране"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Свободна форма"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Общ преглед"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Няма скорошни елементи"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Затваряне"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки за използването на приложенията"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Изчистване на всички"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Скорошни приложения"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Предложения за приложения"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Всички приложения"</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_accept" msgid="1611544083278999837">"Предложения"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, благодаря"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Настройки"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Най-използваните приложения се показват тук и се променят въз основа на поредиците"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"За да получавате предложения за приложения, с плъзгане премахнете приложенията от най-долния ред"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Предложенията за приложения са добавени на празното място"</string>
- <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="action_share" msgid="2648470652637092375">"Споделяне"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Екранна снимка"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Това действие не е разрешено от приложението или организацията ви"</string>
</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 9212a4b..57f92e5 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"স্ক্রিন স্প্লিট করুন"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"পিন করুন"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ফ্রি-ফর্ম"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"এক নজরে"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"কোনো সাম্প্রতিক আইটেম নেই"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"বন্ধ করুন"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"অ্যাপ ব্যবহারের সেটিংস"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"সবকিছু খালি করুন"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"অ্যাপের সাজেশন"</string>
<string name="all_apps_label" msgid="8542784161730910663">"সব অ্যাপ"</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_accept" msgid="1611544083278999837">"অ্যাপ সাজেশন পান"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"না থাক"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"সেটিংস"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"সব থেকে বেশি ব্যবহার করা অ্যাপ এখানে দেখানো হয় এবং রুটিনের উপর ভিত্তি করে পরিবর্তন হতে পারে"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"অ্যাপ সাজেশন পেতে নিচের সারিতে অ্যাপগুলি টেনে আনুন"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"খালি জায়গাতে অ্যাপ সাজেশন যোগ করা হয়েছে"</string>
- <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="action_share" msgid="2648470652637092375">"শেয়ার করুন"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"স্ক্রিনশট নিন"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"এই অ্যাপ বা আপনার প্রতিষ্ঠান এই অ্যাকশনটি পারফর্ম করার অনুমতি দেয়নি"</string>
</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index 6b513b7..7968f7c 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Način rada podijeljenog ekrana"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodan oblik"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvaranje"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke korištenja aplikacije"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Obriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</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 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Preostalo vrijeme: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Prijedlozi aplikacija"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Prijedlozi za aplikacije"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predviđene aplikacije"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Primajte prijedloge aplikacija u donjem redu početnog ekrana"</string>
- <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 na početnom ekranu. 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 na početnom ekranu. 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 na početnom ekranu. 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Ovdje se prikazuju najčešće korištene aplikacije i njihov prikaz se mijenja na osnovu rutina"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Prevucite aplikacije iz donjeg reda da dobijete prijedloge aplikacija"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Prijedlozi aplikacija su dodani u prazan prostor"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Prijedlozi aplikacija su omogućeni"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Prijedlozi aplikacija su onemogućeni"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Dijeli"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Snimak ekrana"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Ovu radnju ne dozvoljava aplikacija ili vaša organizacija"</string>
</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index 4b0acca..6420aa8 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixa"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format lliure"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicacions recents"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No hi ha cap element recent"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Tanca"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuració d\'ús d\'aplicacions"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Esborra-ho tot"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacions recents"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Suggeriments d\'aplicacions"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Totes les aplicacions"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Prediccions d\'aplicacions"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtén suggeriments d\'aplicacions a la fila inferior de la pantalla d\'inici"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Les aplicacions que més utilitzes apareixen aquí i poden variar en funció dels teus hàbits"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arrossega les aplicacions fora de la fila inferior per obtenir suggeriments d\'aplicacions"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"S\'han afegit suggeriments d\'aplicacions en un espai buit"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Els suggeriments d\'aplicacions estan activats"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Els suggeriments d\'aplicacions estan desactivats"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicció d\'aplicació: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Comparteix"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"L\'aplicació o la teva organització no permeten aquesta acció"</string>
</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 2cff6eb..194ff87 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Rozdělená obrazovka"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"PIN"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Neomezený režim"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Přehled"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Žádné nedávné položky"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zavřít"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavení využití aplikací"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazat vše"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Návrhy aplikací"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Všechny aplikace"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše předpovídané aplikace"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Nechte si ve spodním řádku na ploše zobrazovat návrhy aplikací"</string>
- <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 v závislosti na sledech činností. Aplikace ve spodním řádku se přesunou 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 v závislosti na sledech činností. 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 v závislosti na sledech činností. 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Zde se zobrazují nejpoužívanější aplikace (které se mění podle sledů činností)"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Chcete-li získat návrhy aplikací, přetáhněte aplikace z dolního řádku"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Volné místo bylo vyplněno návrhy aplikací"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikací jsou povoleny"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikací jsou zakázány"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Předpokládaná aplikace: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Sdílet"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Snímek obrazovky"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikace nebo organizace zakazuje tuto akci"</string>
</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 2cd265c..b43a76e 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Opdel skærm"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fastgør"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frit format"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversigt"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Luk"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Indstillinger for appforbrug"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ryd alt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Appforslag"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Alle apps"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Dine foreslåede apps"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Få appforslag på den nederste række af din startskærm"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"De mest brugte apps vises her, og visningen ændres ud fra dine vaner"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Træk apps væk fra den nederste række for at få appforslag"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Appforslag blev føjet til tom plads"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er aktiveret"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er deaktiveret"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App, du forventes at skulle bruge: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Del"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller din organisation tillader ikke denne handling"</string>
</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index b51f04d..449cc8c 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -20,33 +20,18 @@
<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_split_screen" msgid="5353188922202653570">"Splitscreen"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Anpinnen"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform-Modus"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Übersicht"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Schließen"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Einstellungen zur App-Nutzung"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Alle Apps schließen"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Zuletzt aktive Apps"</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 Min."</string>
+ <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"< 1 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Heute noch <xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="title_app_suggestions" msgid="4185902664111965088">"App-Vorschläge"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Alle Apps"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"App-Vorschläge für dich"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Lass dir in der unteren Reihe auf deinem Startbildschirm Vorschläge für Apps anzeigen"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Hier erscheinen die meistverwendeten Apps. Die Angaben können sich je nach deiner gewöhnlichen Nutzung ändern"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Ziehe Apps aus der unteren Reihe heraus, um Vorschläge für Apps zu erhalten"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App-Vorschläge in freiem Bereich hinzugefügt"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Funktion \"App-Vorschläge\" aktiviert"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Funktion \"App-Vorschläge\" deaktiviert"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Vorgeschlagene App: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Teilen"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Die App oder deine Organisation lässt diese Aktion nicht zu"</string>
</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index 015ec22..87268df 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Διαχωρισμός οθόνης"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Καρφίτσωμα"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Ελεύθερη μορφή"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Επισκόπηση"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Κλείσιμο"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ρυθμίσεις χρήσης εφαρμογής"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Διαγραφή όλων"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
<string name="task_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 λ."</string>
<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="title_app_suggestions" msgid="4185902664111965088">"Προτάσεις εφαρμογών"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Όλες οι εφαρμογές"</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_accept" msgid="1611544083278999837">"Προβολή προτεινόμενων εφαρμογών"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Όχι, ευχαριστώ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ρυθμίσεις"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Οι εφαρμογές που χρησιμοποιείτε περισσότερο εμφανίζονται εδώ και αλλάζουν με βάση τις ρουτίνες"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Σύρετε εφαρμογές μακριά από την κάτω σειρά, για να δείτε τις προτεινόμενες εφαρμογές"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Οι προτεινόμενες εφαρμογές προστέθηκαν στον κενό χώρο"</string>
- <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="action_share" msgid="2648470652637092375">"Κοινοποίηση"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Στιγμιότυπο οθόνης"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Αυτή η ενέργεια δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας."</string>
</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index fa60ca8..2d1418e 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
<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>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
<string name="all_apps_label" msgid="8542784161730910663">"All apps"</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_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>
- <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>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App suggestions added to empty space"</string>
- <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="action_share" msgid="2648470652637092375">"Share"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
</resources>
diff --git a/quickstep/res/values-en-rCA/strings.xml b/quickstep/res/values-en-rCA/strings.xml
deleted file mode 100644
index fa60ca8..0000000
--- a/quickstep/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,52 +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.
-*/
- -->
-
-<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_split_screen" msgid="5353188922202653570">"Split screen"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
- <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
- <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_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_label" msgid="8542784161730910663">"All apps"</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_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>
- <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>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App suggestions added to empty space"</string>
- <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="action_share" msgid="2648470652637092375">"Share"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
-</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index fa60ca8..2d1418e 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
<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>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
<string name="all_apps_label" msgid="8542784161730910663">"All apps"</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_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>
- <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>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App suggestions added to empty space"</string>
- <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="action_share" msgid="2648470652637092375">"Share"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index fa60ca8..2d1418e 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Split screen"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Close"</string>
<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>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"App suggestions"</string>
<string name="all_apps_label" msgid="8542784161730910663">"All apps"</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_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>
- <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>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App suggestions added to empty space"</string>
- <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="action_share" msgid="2648470652637092375">"Share"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organisation"</string>
</resources>
diff --git a/quickstep/res/values-en-rXC/strings.xml b/quickstep/res/values-en-rXC/strings.xml
deleted file mode 100644
index d7008ca..0000000
--- a/quickstep/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,52 +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.
-*/
- -->
-
-<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_split_screen" msgid="5353188922202653570">"Split screen"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Pin"</string>
- <string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
- <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
- <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_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_label" msgid="8542784161730910663">"All apps"</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 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>
- <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>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App suggestions added to empty space"</string>
- <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="action_share" msgid="2648470652637092375">"Share"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"This action isn\'t allowed by the app or your organization"</string>
-</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index d1ae0fe..5f5d0bd 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recientes"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Cerrar"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración de uso de la app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</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 minuto"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de aplicaciones"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de apps"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Todas las apps"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predicción de tus apps"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtén sugerencias de aplicaciones en la fila inferior de la pantalla principal"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Las apps que más se usan se muestran aquí y cambian según las rutinas"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arrastra apps fuera de la fila inferior para obtener sugerencias"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Se agregaron sugerencias de aplicaciones a un espacio vacío"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de apps habilitadas"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predicción de app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Captura de pantalla"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"La app o tu organización no permiten realizar esta acción"</string>
</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index a2b930d..329286b 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Dividir pantalla"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formato libre"</string>
- <string name="recents_empty_message" msgid="7040467240571714191">"No hay nada reciente"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicaciones recientes"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Cerrar"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ajustes de uso de la aplicación"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicaciones recientes"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Sugerencias de aplicaciones"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Todas las aplicaciones"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predicción de aplicaciones"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtén sugerencias de aplicaciones en la fila inferior de la pantalla de inicio"</string>
- <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 pasarán a mostrarse 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 se pondrán en 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Las aplicaciones que más usas aparecen aquí, y van variando según tus rutinas"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arrastra aplicaciones de la fila inferior para ver sugerencias de aplicaciones"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Sugerencias de aplicaciones añadidas a espacios vacíos"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugerencias de aplicaciones habilitadas"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Las sugerencias de aplicaciones están inhabilitadas"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación sugerida: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Hacer captura"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"No puedes hacerlo porque la aplicación o tu organización no lo permiten"</string>
</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 3425b33..0577b0f 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Jagatud ekraan"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Kinnita"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vabavorm"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ülevaade"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Sule"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Rakenduse kasutuse seaded"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Sule kõik"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Hiljutised rakendused"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Rakenduste soovitused"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Kõik rakendused"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Teie ennustatud rakendused"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Hankige avakuva alumisel real rakenduste soovitusi"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Siin kuvatakse enim kasutatavad rakendused, mis võivad olenevalt rutiinist muutuda."</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Rakenduste soovituste hankimiseks lohistage rakendused alumiselt realt ära"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Rakenduste soovitused lisati tühjale kohale"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Rakenduste soovitused on lubatud"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Rakenduste soovitused on keelatud"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennustatud rakendus: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Rakendus või teie organisatsioon on selle toimingu keelanud"</string>
</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 550d3fb..c2d149e 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Zatitu pantaila"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ainguratu"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Modu librea"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikuspegi orokorra"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ez dago azkenaldi honetako ezer"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Itxi"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Aplikazioen erabileraren ezarpenak"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Garbitu guztiak"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Azken aplikazioak"</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 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gelditzen dira gaur"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Aplikazioen iradokizunak"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Iradokitako aplikazioak"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Aplikazio guztiak"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Lagungarri izan dakizkizukeen aplikazioak"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Jaso aplikazioen iradokizunak hasierako pantailaren beheko errenkadan"</string>
- <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"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Ezarpenak"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Hemen agertzen dira aplikazio erabilienak, eta ohituren arabera aldatzen dira"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arrastatu aplikazioak beheko errenkadatik aplikazioen iradokizunak jasotzeko"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Aplikazioen iradokizunak eremu huts batean gehitu dira"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Gaituta daude aplikazioen iradokizunak"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Desgaituta daude aplikazioen iradokizunak"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Iragarritako aplikazioa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Partekatu"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Atera pantaila-argazki bat"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikazioak edo erakundeak ez du eman ekintza hori gauzatzeko baimena"</string>
</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 4d40115..cc26695 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"تقسیم صفحه"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"پین"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نمای کلی"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"بدون موارد اخیر"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"بستن"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"تنظیمات استفاده از برنامه"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"پاک کردن همه"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامههای اخیر"</string>
<string name="task_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">"< ۱ دقیقه"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"برنامههای پیشنهادی"</string>
<string name="all_apps_label" msgid="8542784161730910663">"همه برنامهها"</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_accept" msgid="1611544083278999837">"دریافت پیشنهادهای برنامه"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"نه متشکرم"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"تنظیمات"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"پرکاربردترین برنامهها اینجا ظاهر میشوند و براساس روالها تغییر میکنند"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"برای دریافت پیشنهادهای برنامه، برنامهها را به بیرون از ردیف پایین بکشید"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"«پیشنهاد برنامه» به فضای خالی اضافه شد"</string>
- <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="action_share" msgid="2648470652637092375">"همرسانی"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"نماگرفت"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"برنامه یا سازمان شما اجازه نمیدهد این کنش انجام شود."</string>
</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 9bc3d49..f43433e 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Jaettu näyttö"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Kiinnitä"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vapaamuotoinen"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Viimeisimmät"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ei viimeaikaisia kohteita"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Sulje"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Sovelluksen käyttöasetukset"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Poista kaikki"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Viimeisimmät sovellukset"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Sovellusehdotukset"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Kaikki sovellukset"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Sovellusennusteet"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Näytä sovellusehdotuksia aloitusnäytön alimmaisella rivillä"</string>
- <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. Alimmaisella 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. Alimmaisella 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Käytetyimmät sovellukset näkyvät täällä ja muuttuvat rutiiniesi perusteella"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Siirrä sovelluksia pois alimmaiselta riviltä, niin voit saada sovellusehdotuksia"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Sovellusehdotuksia lisätty tyhjään kohtaan"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sovellusehdotukset käytössä"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sovellusehdotukset on poistettu käytöstä"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ennakoitu sovellus: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Jaa"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Sovellus tai organisaatio ei salli tätä toimintoa"</string>
</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 46096f5..a9a1cff 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Écran divisé"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forme libre"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aperçu"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Fermer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres d\'utilisation de l\'application"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Suggestions d\'applications"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Toutes les applications"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vos prédictions d\'applications"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtenir des suggestions d\'applications dans la rangée du bas de votre écran d\'accueil"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Les applications les plus utilisées s\'affichent ici et changent en fonction des habitudes"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Faites glisser des applications hors de la rangée du bas pour obtenir des suggestions d\'applications"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Applications suggérées ajoutées à l\'espace vide"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Les suggestions d\'applications sont activées"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Partager"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"L\'application ou votre organisation n\'autorise pas cette action"</string>
</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index 306d533..01dcff2 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Écran partagé"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format libre"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aperçu"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Aucun élément récent"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Fermer"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Paramètres de consommation de l\'application"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tout effacer"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</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 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Encore <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Applications suggérées"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Suggestions d\'applications"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Toutes les applications"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Applications prévues pour vous"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Retrouvez vos applications favorites au bas de votre écran d\'accueil"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Les applications dont vous vous servez le plus s\'affichent ici (ces suggestions peuvent varier en fonction de vos habitudes d\'utilisation)"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Faites glisser des applications hors de la rangée du bas pour obtenir des suggestions d\'applications"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Des suggestions d\'applications ont été ajoutées à un emplacement vide"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Suggestions d\'applications activées"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Les suggestions d\'applications sont désactivées"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Application prédite : <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Partager"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Capture d\'écran"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Cette action n\'est pas autorisée par l\'application ou par votre organisation"</string>
</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index f3dc1ed..356d10d 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Pantalla dividida"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libre"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visión xeral"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Non hai elementos recentes"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Pecha a aplicación"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configuración do uso de aplicacións"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Borrar todo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Suxestións de aplicacións"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Todas as aplicacións"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"As túas aplicacións preditas"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Recibe suxestións de aplicacións na fila inferior da pantalla de inicio"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"As aplicacións máis usadas aparecen aquí e van cambiando en función das túas rutinas"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arrastra aplicacións desde a fila inferior para recibir suxestións de aplicacións"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Engadíronse suxestións de aplicacións ao espazo baleiro"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"As suxestións de aplicacións están activadas"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As suxestións de aplicacións están desactivadas"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplicación predita: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Compartir"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Facer captura"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"A aplicación ou a túa organización non permite realizar esta acción"</string>
</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index b1c40b9..660ad87 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"સ્ક્રીનને વિભાજિત કરો"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"પિન કરો"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ફ્રિફોર્મ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ઝલક"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"તાજેતરની કોઈ આઇટમ નથી"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"બંધ કરો"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ઍપ વપરાશનું સેટિંગ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"બધું સાફ કરો"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"તાજેતરની ઍપ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ઍપ સૂચનો"</string>
<string name="all_apps_label" msgid="8542784161730910663">"બધી ઍપ"</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_accept" msgid="1611544083278999837">"ઍપ અંગેના સુઝાવો મેળવો"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ના, આભાર"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"સેટિંગ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"સૌથી વધુ વપરાતી ઍપ અહીં દેખાય છે અને રૂટિનના આધારે બદલાય છે"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ઍપ અંગેના સૂચનો મેળવવા માટે ઍપને નીચલી પંક્તિમાંથી બહાર ખેંચો"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ઍપ અંગેના સૂચનો ખાલી જગ્યામાં ઉમેરાયા"</string>
- <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="action_share" msgid="2648470652637092375">"શેર કરો"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"સ્ક્રીનશૉટ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ઍપ કે તમારી સંસ્થા દ્વારા આ ક્રિયા કરવાની મંજૂરી નથી"</string>
</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index d98cc77..387d509 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"स्क्रीन को दो हिस्सों में बाँटना (स्प्लिट स्क्रीन)"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करना"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ़्रीफ़ॉर्म"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"खास जानकारी"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"हाल ही में इस्तेमाल किया गया कोई ऐप्लिकेशन नहीं है"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"बंद करें"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ऐप्लिकेशन इस्तेमाल की सेटिंग"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सभी ऐप्लिकेशन बंद करें"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"हाल ही में इस्तेमाल किए गए ऐप्लिकेशन"</string>
<string name="task_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 मिनट"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"ऐप्लिकेशन के सुझाव"</string>
<string name="all_apps_label" msgid="8542784161730910663">"सभी ऐप्लिकेशन"</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_accept" msgid="1611544083278999837">"ऐप्लिकेशन के बारे में सुझाव पाएं"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"रहने दें"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"सेटिंग"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"सबसे ज़्यादा इस्तेमाल होने वाले ऐप्लिकेशन यहां दिखेंगे. यह ऐप्लिकेशन, आपके इस्तेमाल के रूटीन के हिसाब से बदलते रहते हैं"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"नीचे वाली पंक्ति से ऐप्लिकेशन को खींचकर हटाएं, ताकि आप ऐप्लिकेशन के सुझाव पा सकें"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"खाली जगह पर ऐप्लिकेशन के सुझाव जोड़े गए"</string>
- <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="action_share" msgid="2648470652637092375">"शेयर करें"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ऐप्लिकेशन या आपका संगठन इस कार्रवाई की अनुमति नहीं देता"</string>
</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index a026ef8..ab56e57 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Podijeljeni zaslon"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Prikvači"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Slobodni oblik"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zatvori"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Postavke upotrebe aplikacija"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Izbriši sve"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Predložene aplikacije"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Sve aplikacije"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše predviđene aplikacije"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Prijedloge aplikacija vidjet ćete u donjem retku početnog zaslona"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Ovdje se prikazuju najčešće upotrebljavane aplikacije i mijenjaju se na temelju rutina"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Povucite aplikacije iz donjeg retka da biste dobili prijedloge aplikacija"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Predložene aplikacije dodane u prazan prostor"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlaganje apl. omogućeno"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlaganje apl. onemogućeno"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predviđena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Podijeli"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Snimka zaslona"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ili vaša organizacija ne dopuštaju ovu radnju"</string>
</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index f81355e..dec6ea0 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -20,9 +20,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_split_screen" msgid="5353188922202653570">"Osztott képernyő"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Kitűzés"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Rögzítés"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Szabad forma"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Áttekintés"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nincsenek mostanában használt elemek"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Bezárás"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Alkalmazáshasználati beállítások"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Összes törlése"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Legutóbbi alkalmazások"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Alkalmazásjavaslatok"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Az összes alkalmazás"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Várható alkalmazások"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Alkalmazásjavaslatokat kaphat a kezdőképernyő alsó sorában"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"A leggyakrabban használt alkalmazások jelennek meg itt; a lista a rutinok alapján változhat"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Alkalmazásjavaslatok kéréséhez húzzon ki alkalmazásokat az alsó sorból"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Alkalmazásjavaslatok hozzáadva az üres területhez"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Alkalmazásjavaslatok engedélyezve"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Alkalmazásjavaslatok letiltva"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Várható alkalmazás: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Megosztás"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Képernyőkép"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Az alkalmazás vagy az Ön szervezete nem engedélyezi ezt a műveletet"</string>
</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index 53988e1..1656a14 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Տրոհել էկրանը"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ամրացնել"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Կամայական ձև"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ընդհանուր տեղեկություններ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Վերջին տարրեր չկան"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Փակել"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Հավելվածի օգտագործման կարգավորումներ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Փակել բոլորը"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Վերջին օգտագործած հավելվածները"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Առաջարկվող հավելվածներ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Բոլոր հավելվածները"</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_accept" msgid="1611544083278999837">"Ստանալ հավելվածների առաջարկներ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Ոչ, շնորհակալություն"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Կարգավորումներ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Հաճախ օգտագործվող հավելվածները ցուցադրվում են այստեղ և փոփոխվում են ըստ ձեր գործողությունների հերթականության։"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Քաշեք հավելվածները ներքևի տողից՝ հավելվածների առաջարկները տեսնելու համար"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Առաջարկվող հավելվածները կավելացվեն ազատ տեղերում"</string>
- <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="action_share" msgid="2648470652637092375">"Կիսվել"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Սքրինշոթ անել"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Այս գործողությունն արգելված է հավելվածի կամ ձեր կազմակերպության կողմից"</string>
</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index d7437d0..6824d16 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Layar terpisah"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pasang pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Format bebas"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ringkasan"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item yang baru dibuka"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Setelan penggunaan aplikasi"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hapus semua"</string>
- <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi terbaru"</string>
+ <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi baru-baru ini"</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 menit"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tersisa hari ini"</string>
<string name="title_app_suggestions" msgid="4185902664111965088">"Saran aplikasi"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Semua aplikasi"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplikasi yang diprediksi"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Dapatkan saran aplikasi di baris paling bawah Layar utama"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Aplikasi yang paling sering digunakan muncul di sini, dan berubah berdasarkan rutinitas"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Tarik aplikasi dari baris paling bawah untuk mendapatkan saran aplikasi"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Saran aplikasi ditambahkan ke ruang kosong"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Saran aplikasi diaktifkan"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Saran aplikasi dinonaktifkan"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikasi yang diprediksi: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Bagikan"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak diizinkan oleh aplikasi atau organisasi Anda"</string>
</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index 824fd7f..f60a2c6 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skipta skjá"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Festa"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Frjálst snið"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Yfirlit"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Engin nýleg atriði"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Loka"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Notkunarstillingar forrits"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hreinsa allt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nýleg forrit"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Tillögur að forritum"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Öll forrit"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Spáð forrit"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Fáðu tillögur að forritum í neðstu röð heimaskjásins"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Mest notuðu forritin birtast hér og breytast í samræmi við rútínur"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Dragðu forrit af neðstu röð til að fá tillögð forrit"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Tillögðum forritum bætt við autt svæði"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Kveikt á tillögum að forritum"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Slökkt er á tillögðum forritum"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tillaga að forriti: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Deila"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Skjámynd"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Forritið eða fyrirtækið leyfir ekki þessa aðgerð"</string>
</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index 4038966..559fdb4 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Schermo diviso"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Blocca"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma libera"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Panoramica"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Chiudi"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Impostazioni di utilizzo delle app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Cancella tutto"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"App suggerite"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Tutte le app"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Le app previste"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Visualizza app suggerite nella riga inferiore della schermata Home"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Le app più utilizzate vengono visualizzate qui e variano in base alle routine"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Trascina le app fuori dalla riga inferiore per visualizzare le app suggerite"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App suggerite aggiunte a uno spazio vuoto"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"La funzionalità app suggerite è attiva"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"La funzionalità app suggerite è disattivata"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Condividi"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Questa azione non è consentita dall\'app o dall\'organizzazione"</string>
</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 81753e1..58cab4e 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"מסך מפוצל"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"הצמדה"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"מצב חופשי"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"מסכים אחרונים"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"סגירה"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"הגדרות שימוש באפליקציה"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ניקוי הכול"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"הצעות לאפליקציות"</string>
<string name="all_apps_label" msgid="8542784161730910663">"כל האפליקציות"</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_accept" msgid="1611544083278999837">"קבלת הצעות לאפליקציות"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"לא, תודה"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"הגדרות"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"רוב האפליקציות הכי נפוצות מופיעות כאן ומשתנות בהתאם להרגלי השימוש שלך"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"יש לגרור אפליקציות מהשורה התחתונה כדי לקבל הצעות לאפליקציות"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"הצעות לאפליקציות נוספו לאזור ריק"</string>
- <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="action_share" msgid="2648470652637092375">"שיתוף"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"צילום מסך"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"האפליקציה או הארגון שלך אינם מתירים את הפעולה הזאת"</string>
</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index d4d6572..d3fecde 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割画面"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"フリーフォーム"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概要"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"閉じる"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"アプリの使用状況の設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"すべてクリア"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使ったアプリ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"アプリの候補"</string>
<string name="all_apps_label" msgid="8542784161730910663">"すべてのアプリ"</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_accept" msgid="1611544083278999837">"アプリの候補を利用"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"使用しない"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"使用頻度の高いアプリがここに表示されます(ルーティンに応じて変わります)"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"一番下の行からアプリをドラッグするとアプリの候補が表示されます"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"空いたスペースにアプリの候補が追加されます"</string>
- <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="action_share" msgid="2648470652637092375">"共有"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"スクリーンショット"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"この操作はアプリまたは組織で許可されていません"</string>
</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 1d0a712..67b03a7 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"ეკრანის გაყოფა"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ჩამაგრება"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"თავისუფალი ფორმა"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"მიმოხილვა"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"დახურვა"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"აპების გამოყენების პარამეტრები"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ყველას გასუფთავება"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
<string name="task_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 წუთი"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"აპების შემოთავაზებები"</string>
<string name="all_apps_label" msgid="8542784161730910663">"ყველა აპი"</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_accept" msgid="1611544083278999837">"აპის შეთავაზებების მიღება"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"არა, გმადლობთ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"პარამეტრები"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ყველაზე ხშირად გამოყენებული აპები აქ ჩანს და ცვალებადობს რუტინების მიხედვით"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"გადაიტანეთ აპები ეკრანის ქვედა რიგში, რათა აპის შეთავაზებები მიიღოთ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"აპის შეთავაზებები დამატებულია ცარიელ სივრცეში"</string>
- <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="action_share" msgid="2648470652637092375">"გაზიარება"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ეკრანის ანაბეჭდი"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ეს მოქმედება არ არის დაშვებული აპის ან თქვენი ორგანიზაციის მიერ"</string>
</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index fb76660..a9fcbed 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Экранды бөлу"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Бекіту"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Еркін форма"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Шолу"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Жабу"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Қолданбаны пайдалану параметрлері"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Барлығын өшіру"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Соңғы пайдаланылған қолданбалар"</string>
<string name="task_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 мин"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"Қолданба ұсыныстары"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Барлық қолданбалар"</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_accept" msgid="1611544083278999837">"Ұсынылған қолданбаларды көру"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Жоқ, рақмет"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Параметрлер"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Жиі пайдаланылатын қолданбалар осы жерде көрсетіледі. Олар күнделікті әрекеттеріңізге сәйкес өзгереді."</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Ұсынылған қолданбаларды көру үшін төменгі қатардан керектерін сүйреп шығарыңыз."</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Ұсынылған қолданбалар бос орынға қосылды."</string>
- <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="action_share" msgid="2648470652637092375">"Бөлісу"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Бұл әрекетке қолданба не ұйым рұқсат етпейді."</string>
</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 3c3fccc..c422041 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"មុខងារបំបែកអេក្រង់"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ដៅ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"មុខងារទម្រង់សេរី"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ទិដ្ឋភាពរួម"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"បិទ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ការកំណត់ការប្រើប្រាស់កម្មវិធី"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"សម្អាតទាំងអស់"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"កម្មវិធីថ្មីៗ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ការណែនាំកម្មវិធី"</string>
<string name="all_apps_label" msgid="8542784161730910663">"កម្មវិធីទាំងអស់"</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_accept" msgid="1611544083278999837">"ទទួលការណែនាំកម្មវិធី"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ទេ អរគុណ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ការកំណត់"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"កម្មវិធីដែលប្រើញឹកញាប់បំផុតបង្ហាញនៅទីនេះ និងប្រែប្រួលទៅតាមទម្លាប់"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"អូសកម្មវិធីចេញពីជួរខាងក្រោម ដើម្បីទទួលបានការណែនាំកម្មវិធី"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"បានបញ្ចូលការណែនាំកម្មវិធីទៅក្នុងកន្លែងទំនេរ"</string>
- <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="action_share" msgid="2648470652637092375">"ចែករំលែក"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"រូបថតអេក្រង់"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"សកម្មភាពនេះមិនត្រូវបានអនុញ្ញាតដោយកម្មវិធី ឬស្ថាប័នរបស់អ្នកទេ"</string>
</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index 6458d6e..5278261 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"ಪರದೆಯನ್ನು ಬೇರ್ಪಡಿಸಿ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ಪಿನ್ ಮಾಡಿ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ಮುಕ್ತಸ್ವರೂಪ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ಅವಲೋಕನ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ಮುಚ್ಚಿ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ಆ್ಯಪ್ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ಎಲ್ಲವನ್ನೂ ತೆರವುಗೊಳಿಸಿ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ಆ್ಯಪ್ ಸಲಹೆಗಳು"</string>
<string name="all_apps_label" msgid="8542784161730910663">"ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು"</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_accept" msgid="1611544083278999837">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಿರಿ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ಬೇಡ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ಹೆಚ್ಚು ಬಳಸಿದ ಆ್ಯಪ್ಗಳು ಇಲ್ಲಿ ಕಾಣಿಸುತ್ತವೆ ಮತ್ತು ದಿನಚರಿಯನ್ನು ಆಧರಿಸಿ ಬದಲಾಗುತ್ತದೆ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಪಡೆಯಲು, ಆ್ಯಪ್ಗಳನ್ನು ಕೆಳಭಾಗದ ಸಾಲಿನಿಂದ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ಆ್ಯಪ್ ಸಲಹೆಗಳನ್ನು ಖಾಲಿ ಸ್ಥಳಕ್ಕೆ ಸೇರಿಸಲಾಗಿದೆ"</string>
- <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="action_share" msgid="2648470652637092375">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ಸ್ಕ್ರೀನ್ಶಾಟ್"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ಆ್ಯಪ್ ಅಥವಾ ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಕ್ರಿಯೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index f7051f5..7a8e6a1 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"화면 분할"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"고정"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"자유 형식"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"최근 사용"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"최근 항목이 없습니다."</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"닫기"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"앱 사용 설정"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"모두 삭제"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"최근 앱"</string>
<string name="task_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분"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"앱 추천"</string>
<string name="all_apps_label" msgid="8542784161730910663">"모든 앱"</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_accept" msgid="1611544083278999837">"앱 제안받기"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"나중에"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"설정"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"가장 많이 사용한 앱이 여기에 표시되며 루틴에 따라 달라짐"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"하단 행에서 앱을 드래그하여 앱 제안 받기"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"빈 공간에 앱 제안이 추가됨"</string>
- <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="action_share" msgid="2648470652637092375">"공유"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"스크린샷"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"이 작업은 앱 또는 조직에서 허용되지 않습니다."</string>
</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index bdc065e..4018e57 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Экранды бөлүү"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Кадап коюу"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Эркин форма режими"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Сереп салуу"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Жабуу"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Колдонмону пайдалануу жөндөөлөрү"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Баарын тазалоо"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
<string name="task_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 мүнөт"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"Колдонмо сунуштары"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Бардык колдонмолор"</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_accept" msgid="1611544083278999837">"Сунушталган колдонолорду алуу"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Жок, рахмат"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Жөндөөлөр"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Көп иштетилген колдонмолор ушул жерде көрүнүп, тартиптин негизинде өзгөрөт"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Сунуштарды алып туруу үчүн, ылдый жактагы тилкедеги колдонмолорду сүйрөп келиңиз"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Сунушталган колдонмолор бош жерге кошулат"</string>
- <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="action_share" msgid="2648470652637092375">"Бөлүшүү"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Бул аракетти аткарууга колдонмо же ишканаңыз тыюу салган"</string>
</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index b8c507b..e406b70 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"ແບ່ງໜ້າຈໍ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ປັກໝຸດ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ຮູບແບບອິດສະຫລະ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ພາບຮວມ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ປິດ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ການຕັ້ງຄ່າການນຳໃຊ້ແອັບ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ລຶບລ້າງທັງໝົດ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ແອັບຫຼ້າສຸດ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ການແນະນຳແອັບ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"ແອັບທັງໝົດ"</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_accept" msgid="1611544083278999837">"ຮັບການແນະນຳແອັບ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ບໍ່, ຂອບໃຈ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ການຕັ້ງຄ່າ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ແອັບທີ່ໃຊ້ຫຼາຍທີ່ສຸດຈະປາກົດຢູ່ບ່ອນນີ້ ແລະ ປ່ຽນໄປຕາມການນຳໃຊ້ປະຈຳ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ລາກແອັບຈາກແຖບລຸ່ມສຸດເພື່ອຮັບການແນະນຳແອັບ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ເພີ່ມການແນະນຳແອັບໃສ່ພື້ນທີ່ຫວ່າງແລ້ວ"</string>
- <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="action_share" msgid="2648470652637092375">"ແບ່ງປັນ"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ຮູບໜ້າຈໍ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ແອັບ ຫຼື ອົງການຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ໃຊ້ຄຳສັ່ງນີ້"</string>
</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index cd4310a..ed1fc37 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skaidyti ekraną"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Prisegti"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Laisva forma"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Apžvalga"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nėra jokių naujausių elementų"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Uždaryti"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Programos naudojimo nustatymai"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Išvalyti viską"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Naujausios programos"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Programų pasiūlymai"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Visos programos"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Numatomos programos"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Gaukite programų pasiūlymų apatinėje pagrindinio ekrano eilutėje"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Dažniausiai naudojamos programos rodomos čia ir keičiasi pagal tai, kaip jas naudojate"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Nuvilkę programas į apatinę eilutę gausite programų pasiūlymų"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Programų pasiūlymai pridedami tuščioje vietoje"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Siūlomų programų funkcija įgalinta"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Siūlomų programų funkcija išjungta"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Numatoma programa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Bendrinti"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Ekrano kopija"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Jūsų organizacijoje arba naudojant šią programą neleidžiama atlikti šio veiksmo"</string>
</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 65620c4..85ce0e0 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Sadalīt ekrānu"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Piespraust"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Brīva forma"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pārskats"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nav nesenu vienumu."</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Aizvērt"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Lietotņu izmantošanas iestatījumi"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Notīrīt visu"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Pēdējās izmantotās lietotnes"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Ieteicamās lietotnes"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Visas lietotnes"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Jūsu prognozētās lietotnes"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Sākuma ekrāna apakšējā rindā tiks rādītas ieteicamās lietotnes"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Šeit tiek rādītas visbiežāk izmantotās lietotnes, un tās mainās atkarībā no jūsu paradumiem"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Lai skatītu ieteicamās lietotnes, velciet lietotnes prom no apakšējās rindas"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Ieteicamās lietotnes tika pievienotas tukšajā vietā"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ieteicamās lietotnes ir iespējotas"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Ieteicamās lietotnes ir atspējotas"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Prognozētā lietotne: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Kopīgot"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Veikt ekrānuzņēmumu"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Lietotne vai jūsu organizācija neatļauj veikt šo darbību."</string>
</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 130e012..9f11521 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -22,9 +22,11 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Поделен екран"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Прикачување"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Поставки за користење на апликациите"</string>
- <string name="recents_clear_all" msgid="5328176793634888831">"Избриши ги сите"</string>
+ <string name="recents_clear_all" msgid="5328176793634888831">"Исчисти ги сите"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Неодамнешни апликации"</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 минута"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Предлози за апликации"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Сите апликации"</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_accept" msgid="1611544083278999837">"Добивајте предлози за апликации"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, фала"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Поставки"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Најкористените апликации се појавуваат тука и се менуваат според рутините"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Отстранете апликации од долниот ред со повлекување за да добивате предлози за апликации"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Предлозите за апликации се додадени во празен простор"</string>
- <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="action_share" msgid="2648470652637092375">"Сподели"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Слика од екранот"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Апликацијата или вашата организација не го дозволува дејствово"</string>
</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index 8e1eec5..2e02e80 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"സ്ക്രീൻ വിഭജിക്കുക"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"പിൻ ചെയ്യുക"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ഫ്രീഫോം"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"അവലോകനം"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"അവസാനിപ്പിക്കുക"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ആപ്പ് ഉപയോഗ ക്രമീകരണം"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"എല്ലാം മായ്ക്കുക"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"സമീപകാല ആപ്പുകൾ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ആപ്പ് നിർദ്ദേശങ്ങൾ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"എല്ലാ ആപ്പുകളും"</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_accept" msgid="1611544083278999837">"ആപ്പ് നിർദ്ദേശങ്ങൾ നേടുക"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"വേണ്ട"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ക്രമീകരണം"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ഏറ്റവുമധികം ഉപയോഗിക്കുന്ന ആപ്പുകൾ ഇവിടെ ദൃശ്യമാകും, ദിനചര്യയ്ക്ക് അനുസരിച്ച് അത് മാറുകയും ചെയ്യും"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ആപ്പ് നിർദ്ദേശങ്ങൾ നേടാൻ താഴത്തെ നിരയിലെ ആപ്പുകൾ വലിച്ചിടുക"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ആപ്പ് നിർദ്ദേശങ്ങൾ ഒഴിഞ്ഞ സ്ഥലത്തേക്ക് ചേർത്തു"</string>
- <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="action_share" msgid="2648470652637092375">"പങ്കിടുക"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"സ്ക്രീൻഷോട്ട്"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ഈ നടപടി എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string>
</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index ba083ef..5de8602 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Дэлгэцийг хуваах"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Тогтоох"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Чөлөөтэй хувьсах"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Тойм"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Хаах"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Апп ашиглалтын тохиргоо"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Бүгдийг устгах"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string>
<string name="task_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 минут"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"Аппын зөвлөмж"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Бүх апп"</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_accept" msgid="1611544083278999837">"Санал болгож буй аппуудыг авах"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Үгүй, баярлалаа"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Тохиргоо"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Хамгийн их ашигладаг аппуудыг энд харуулах бөгөөд хэвшлээс хамаарч өөрчилдөг"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Санал болгож буй аппуудыг авахын тулд доод мөрөөс аппуудыг чирж гаргаарай"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Санал болгож буй аппуудыг хоосон зайд нэмсэн"</string>
- <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="action_share" msgid="2648470652637092375">"Хуваалцах"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Дэлгэцийн агшин дарах"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Энэ үйлдлийг апп эсвэл танай байгууллага зөвшөөрдөггүй"</string>
</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index 4a5fddd..cccece7 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"विभाजित स्क्रीन"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करा"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रीफॉर्म"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"अवलोकन"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"बंद"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अॅप वापर सेटिंग्ज"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सर्व साफ करा"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अॅप्स"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"अॅप सूचना"</string>
<string name="all_apps_label" msgid="8542784161730910663">"सर्व अॅप्स"</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_accept" msgid="1611544083278999837">"अॅप सूचना मिळवा"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"नाही, नको"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"सेटिंग्ज"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"सर्वाधिक वापरली जाणारी अॅप्स येथे दिसतात आणि दिनक्रमांच्या आधारावर बदलतात"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"अॅप सूचना मिळवण्यासाठी तळाच्या पंक्तीवरून अॅप्स ड्रॅग करा"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"रिकाम्या जागेवर जोडलेल्या अॅप सूचना"</string>
- <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="action_share" msgid="2648470652637092375">"शेअर करा"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"स्क्रीनशॉट"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"अॅप किंवा तुमच्या संस्थेद्वारे ही क्रिया करण्याची अनुमती नाही"</string>
</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index 714831a..2542963 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Skrin pisah"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Semat"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Bentuk bebas"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikhtisar"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Tutup"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Tetapan penggunaan apl"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Kosongkan semua"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apl terbaharu"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Cadangan apl"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Semua apl"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Apl ramalan anda"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Dapatkan cadangan apl di baris bawah Skrin utama anda"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Apl yang paling kerap digunakan dipaparkan di sini dan berubah berdasarkan rutin"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Seret apl keluar dari baris bawah untuk mendapatkan cadangan apl"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Cadangan apl ditambahkan pada ruang kosong"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Cadangan apl didayakan"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Cadangan apl dilumpuhkan"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Apl yang diramalkan: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Kongsi"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Tangkapan skrin"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Tindakan ini tidak dibenarkan oleh apl atau organisasi anda"</string>
</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index a18c1c4..7683e05 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ပင်ထိုးခြင်း"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"အလွတ်ပုံစံ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"အနှစ်ချုပ်"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ပိတ်ရန်"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"အက်ပ်အသုံးပြုမှု ဆက်တင်များ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"အားလုံးကို ရှင်းရန်"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"လတ်တလောသုံး အက်ပ်များ"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"အက်ပ်အကြံပြုချက်များ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"အက်ပ်အားလုံး"</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_accept" msgid="1611544083278999837">"အက်ပ်အကြံပြုချက်များ ရယူရန်"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"မလိုပါ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ဆက်တင်များ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"အသုံးအများဆုံးအက်ပ်များကို ဤနေရာတွင် ပြပြီး ပုံမှန်အစီအစဉ်များပေါ် အခြေခံ၍ ပြောင်းလဲသည်"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"အက်ပ်အကြံပြုချက်များ ရယူရန် အောက်ခြေအတန်းရှိ အက်ပ်များကို ဖိဆွဲထုတ်လိုက်ပါ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"အက်ပ်အကြံပြုချက်များကို နေရာလွတ်သို့ ထည့်လိုက်သည်"</string>
- <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="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ဤလုပ်ဆောင်ချက်ကို အက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string>
</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index 161d1c4..01bbb6a 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Delt skjerm"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fest"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversikt"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ingen nylige elementer"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Lukk"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Innstillinger for appbruk"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Fjern alt"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nylige apper"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Appanbefalinger"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Alle apper"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Forslag til apper"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Få appforslag i den nederste raden på startskjermen"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Appene som brukes oftest, vises her og endres basert på rutiner"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Dra appene vekk fra den nederste raden for å få appforslag."</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Appforslag er lagt til på et tomt område"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appforslag er på"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appforslag er slått av"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Foreslått app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Del"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Skjermdump"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisasjonen din tillater ikke denne handlingen"</string>
</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index 99a22f9..60e9bd5 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"स्क्रिन विभाजन गर्नुहोस्"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"पिन गर्नुहोस्"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"फ्रिफर्म"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"परिदृश्य"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
- <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"एपको उपयोगका सेटिङहरू"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"बन्द गर्नुहोस्"</string>
+ <string name="accessibility_app_usage_settings" msgid="6312864233673544149">"अनुप्रयोगको उपयोगका सेटिङहरू"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"सबै खाली गर्नुहोस्"</string>
- <string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका एपहरू"</string>
+ <string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका अनुप्रयोगहरू"</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">"< १ मिनेट"</string>
<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_label" msgid="8542784161730910663">"सबै एपहरू"</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_accept" msgid="1611544083278999837">"सिफारिस गरिएका एपहरू प्राप्त गर्नुहोस्"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"पर्दैन धन्यवाद"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"सेटिङ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"सबैभन्दा बढी प्रयोग हुने एपहरू यहाँ देखिन्छन् र यी एपहरूको क्रम तपाईंले एप प्रयोग गर्ने समयतालिकाअनुसार बदलिरहन्छ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"सिफारिस गरिएका एपहरू प्राप्त गर्न फेदको पङ्क्तिमा रहेका एपहरू ड्र्याग गरी हटाउनुहोस्"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"खाली ठाउँमा सिफारिस गरिएका एपहरू थपिए"</string>
- <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="action_share" msgid="2648470652637092375">"सेयर गर्नुहोस्"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"स्क्रिनसट"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"यो एप वा तपाईंको सङ्गठनले यो कारबाही गर्ने अनुमति दिँदैन"</string>
+ <string name="all_apps_label" msgid="8542784161730910663">"सबै अनुप्रयोगहरू"</string>
+ <string name="all_apps_prediction_tip" msgid="2672336544844936186">"तपाईंका पूर्वानुमानित अनुप्रयोगहरू"</string>
</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index b814d39..8032567 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Gesplitst scherm"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Vastzetten"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Vrije vorm"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overzicht"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Geen recente items"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Sluiten"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Instellingen voor app-gebruik"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Alles wissen"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Recente apps"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"App-suggesties"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Alle apps"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Je voorspelde apps"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"App-suggesties ontvangen op de onderste rij van je startscherm"</string>
- <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"App-suggesties ontvangen op de rij met favorieten op het startscherm"</string>
- <string name="hotseat_edu_message_migrate" msgid="8927179260533775320">"Open je meestgebruikte apps gemakkelijk vanaf het startscherm. De suggesties veranderen op basis van je routines. Apps op de onderste rij worden naar je startscherm verplaatst."</string>
- <string name="hotseat_edu_message_migrate_landscape" msgid="4248943380443387697">"Open je meestgebruikte apps gemakkelijk vanaf het startscherm. De suggesties veranderen op basis van je routines. Apps op de rij met favorieten worden naar het startscherm verplaatst."</string>
- <string name="hotseat_edu_message_migrate_alt" msgid="3042360119039646356">"Open je meestgebruikte apps gemakkelijk vanaf het startscherm. De suggesties veranderen op basis van je routines. Apps op de onderste rij worden naar een nieuwe map verplaatst."</string>
- <string name="hotseat_edu_accept" msgid="1611544083278999837">"App-suggesties ontvangen"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Nee, bedankt"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Instellingen"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"De meestgebruikte apps worden hier weergegeven en kunnen veranderen op basis van je routines"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Sleep apps weg van de onderste rij om app-suggesties te ontvangen"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"App-suggesties toegevoegd aan lege ruimte"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"App-suggesties zijn ingeschakeld"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"App-suggesties zijn uitgeschakeld"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Voorspelde app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Delen"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Deze actie wordt niet toegestaan door de app of je organisatie"</string>
</resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 1a63e11..2ebec4e 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"ସ୍କ୍ରୀନ୍କୁ ଭାଗ କରନ୍ତୁ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ପିନ୍"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ଫ୍ରିଫର୍ମ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ସଂକ୍ଷିପ୍ତ ବିବରଣ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ଆପ୍ ବ୍ୟବହାର ସେଟିଂସ୍"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ବର୍ତ୍ତମାନର ଆପ୍"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"ସମସ୍ତ ଆପ୍ସ"</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_accept" msgid="1611544083278999837">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଆନ୍ତୁ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ନାହିଁ, ଥାଉ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ସେଟିଂସ୍"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ସବୁଠାରୁ ଅଧିକ-ବ୍ୟବହୃତ ଆପଗୁଡ଼ିକ ଏଠାରେ ଦେଖାଯାଏ ଏବଂ ରୁଟିନଗୁଡ଼ିକ ଆଧାରରେ ପରିବର୍ତ୍ତିତ ହୋଇଥାଏ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ପାଇବାକୁ ଆପଗୁଡ଼ିକୁ ତଳ ଧାଡ଼ିରୁ ଟାଣି ଆଣନ୍ତୁ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ଆପ୍ ପରାମର୍ଶଗୁଡ଼ିକ ଖାଲି ସ୍ଥାନରେ ଯୋଗ କରାଯାଇଛି"</string>
- <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="action_share" msgid="2648470652637092375">"ସେୟାର୍ କରନ୍ତୁ"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ସ୍କ୍ରିନସଟ୍"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ଆପ୍ କିମ୍ବା ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ଏହି କାର୍ଯ୍ୟକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 22fa519..58c0d2a 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ਪਿੰਨ ਕਰੋ"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"ਫ੍ਰੀਫਾਰਮ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ਰੂਪ-ਰੇਖਾ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ਬੰਦ ਕਰੋ"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ਐਪ ਵਰਤੋਂ ਦੀਆਂ ਸੈਟਿੰਗਾਂ"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ਹਾਲੀਆ ਐਪਾਂ"</string>
<string name="task_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 ਮਿੰਟ"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"ਐਪ ਸੰਬੰਧੀ ਸੁਝਾਅ"</string>
<string name="all_apps_label" msgid="8542784161730910663">"ਸਾਰੀਆਂ ਐਪਾਂ"</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_accept" msgid="1611544083278999837">"ਐਪ ਸੁਝਾਅ ਪ੍ਰਾਪਤ ਕਰੋ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ਸੈਟਿੰਗਾਂ"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ਸਭ ਤੋਂ ਵੱਧ ਵਰਤੀਆਂ ਗਈਆਂ ਐਪਾਂ ਇੱਥੇ ਦਿਸਦੀਆਂ ਹਨ, ਅਤੇ ਨਿਯਮਬੱਧ ਕੰਮਾਂ ਦੇ ਆਧਾਰ \'ਤੇ ਬਦਲਦੀਆਂ ਹਨ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ਐਪ ਸੁਝਾਅ ਪ੍ਰਾਪਤ ਕਰਨ ਲਈ ਹੇਠਲੀ ਕਤਾਰ ਤੋਂ ਐਪਾਂ ਨੂੰ ਘਸੀਟੋ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ਐਪ ਸੁਝਾਅ ਖਾਲੀ ਜਗ੍ਹਾ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤੇ ਗਏ"</string>
- <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="action_share" msgid="2648470652637092375">"ਸਾਂਝਾ ਕਰੋ"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਇਸ ਕਾਰਵਾਈ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index 33ea9b4..d83160d 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Podziel ekran"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Przypnij"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Tryb dowolny"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Przegląd"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Brak ostatnich elementów"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zamknij"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ustawienia użycia aplikacji"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Wyczyść wszystko"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Ostatnie aplikacje"</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 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Na dziś zostało <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestie aplikacji"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Sugerowane aplikacje"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Wszystkie aplikacje"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Przewidywane aplikacje"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Otrzymuj sugestie aplikacji w dolnym wierszu ekranu głównego"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Najczęściej używane aplikacje wyświetlają się tutaj i zmieniają się w zależności od rutyny"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Przeciągnij aplikacje z dolnego wiersza, aby otrzymać sugestie aplikacji"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Sugestie aplikacji dodane w pustym obszarze"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Włączono sugestie aplikacji"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugestie aplikacji są wyłączone"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Przewidywana aplikacja: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Udostępnij"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Zrzut ekranu"</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>
</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index cff747f..2fd34d6 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ecrã dividido"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Vista geral"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Fechar"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Definições de utilização de aplicações"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
- <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
+ <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicações recentes"</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 minuto"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Resta(m) <xliff:g id="TIME">%1$s</xliff:g> hoje."</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de apps"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de aplicações"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Todas as aplicações"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"As suas aplicações previstas"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Obtenha sugestões de apps na última fila do ecrã principal"</string>
- <string name="hotseat_edu_title_migrate_landscape" msgid="3633942953997845243">"Obtenha 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"As apps mais utilizadas aparecem aqui e mudam em função das rotinas."</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arraste as apps para fora da última fila para obter sugestões de apps."</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Sugestões de apps adicionadas a um espaço vazio."</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Sugestões de apps ativadas"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"As sugestões de apps estão desativadas"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App prevista: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Partilhar"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Tirar captura de ecrã"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Esta ação não é permitida pela app ou a sua entidade."</string>
</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index 7c80a58..673dfe2 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Tela dividida"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Forma livre"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visão geral"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Fechar"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Configurações de uso do app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Limpar tudo"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Sugestões de apps"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Todos os apps"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Suas predições de apps"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Receba sugestões de apps na linha inferior da tela inicial"</string>
- <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 mudarão de acordo com sua rotina. Os apps na linha inferior serã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 mudarão de acordo com sua rotina. Os apps na linha \"Favoritos\" serã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 mudarão de acordo com sua rotina. Os apps na linha inferior serã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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Os apps mais usados aparecem aqui e mudam de acordo com sua rotina"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Arraste apps da linha inferior para receber sugestões de apps"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Sugestões de apps adicionadas a um espaço vazio"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"O recurso \"sugestões de apps\" está ativado"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"O recurso \"sugestões de apps\" está desativado"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"App previsto: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Compartilhar"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Capturar tela"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Essa ação não é permitida pelo app ou pela organização"</string>
</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index e13ed18..2ac783e 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ecran împărțit"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fixați"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formă liberă"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recente"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Închideți"</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="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Sugestii de aplicații"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Toate aplicațiile"</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_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_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="action_share" msgid="2648470652637092375">"Distribuiți"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Captură de ecran"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Această acțiune nu este permisă de aplicație sau de organizația dvs."</string>
</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 736bb14..5dd89a6 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -20,9 +20,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_split_screen" msgid="5353188922202653570">"Разделить экран"</string>
- <string name="recent_task_option_pin" msgid="7929860679018978258">"Закрепить"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Блокировать"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Произвольная форма"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Обзор"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Здесь пока ничего нет."</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Закрыть"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Настройки использования приложения"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистить все"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавние приложения"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Рекомендуемые приложения"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Все приложения"</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_accept" msgid="1611544083278999837">"Показывать рекомендации"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Отмена"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Настройки"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Здесь появляются часто используемые приложения. Список меняется с учетом ваших привычек."</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Перетащите приложения из нижнего ряда, чтобы получить рекомендации"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Рекомендуемые приложения будут появляться на свободных местах."</string>
- <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="action_share" msgid="2648470652637092375">"Поделиться"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Скриншот"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Это действие заблокировано приложением или организацией."</string>
</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 7deb5f6..f6584c4 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"බෙදුම් තිරය"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"අමුණන්න"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"දළ විශ්ලේෂණය"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"වසන්න"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"යෙදුම් භාවිත සැකසීම්"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"සියල්ල හිස් කරන්න"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"මෑත යෙදුම්"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"යෙදුම් යෝජනා"</string>
<string name="all_apps_label" msgid="8542784161730910663">"සියලු යෙදුම්"</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_accept" msgid="1611544083278999837">"යෙදුම් යෝජනා ලබා ගන්න"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"එපා ස්තුතියි"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"සැකසීම්"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"වැඩිපුරම භාවිත කළ යෙදුම් මෙහි දිස්වන අතර දිනචරියා මත පදනම්ව වෙනස් වේ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"යෙදුම් යෝජනා ලබා ගැනීමට පහළ පේළියෙන් ඉවතට යෙදුම් ඇදගෙන යන්න"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"යෙදුම් යෝජනා හිස් අවකාශයට එක් කර ඇත"</string>
- <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="action_share" msgid="2648470652637092375">"බෙදා ගන්න"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"තිර රුව"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"මෙම ක්රියාව යෙදුම හෝ ඔබේ සංවිධානය මගින් ඉඩ නොදේ"</string>
</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index 9c8f95f..8a9c736 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Rozdeliť obrazovku"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pripnúť"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Voľný režim"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Prehľad"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Žiadne nedávne položky"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zavrieť"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavenia využívania aplikácie"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Vymazať všetko"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Návrhy aplikácií"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Všetky aplikácie"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Vaše predpovedané aplikácie"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Nechajte si v spodnom riadku na ploche zobrazovať návrhy aplikácií"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Tu sú zobrazené najpoužívanejšie aplikácie a menia sa podľa postupov"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Ak chcete získať návrhy aplikácií, presuňte aplikácie zo spodného riadka"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Na prázdne miesto pridané návrhy aplikácií"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Návrhy aplikácií zapnuté"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Návrhy aplikácií vypnuté"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predpovedaná aplikácia: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Zdieľať"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Snímka obrazovky"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikácia alebo vaša organizácia túto akciu nepovoľuje"</string>
</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index 5d45f6d..15f8f89 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Razdeljen zaslon"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Pripni"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Prosta oblika"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Ni nedavnih elementov"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Zapri"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Nastavitve uporabe aplikacij"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Počisti vse"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</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 min"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Danes je ostalo še <xliff:g id="TIME">%1$s</xliff:g>"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Predlagane aplikacije"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Predlogi za aplikacije"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Vse aplikacije"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Predvidene aplikacije"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Oglejte si predlagane aplikacije v spodnji vrstici začetnega zaslona"</string>
- <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 se iz spodnje vrstice 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 se iz spodnje vrstice 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Tukaj so navedene najpogosteje uporabljene aplikacije in spremembe na podlagi rutin"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Če si želite ogledati predlagane aplikacije, povlecite aplikacije iz spodnje vrstice"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Predlagane aplikacije so bile dodane v prazni prostor"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Predlogi aplikacij so omogočeni"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Predlogi aplikacij so onemogočeni"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Predvidena aplikacija: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Deli"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Posnetek zaslona"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Aplikacija ali vaša organizacija ne dovoljuje tega dejanja"</string>
</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index a144b92..d8f5f28 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ekrani i ndarë"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Gozhdo"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Formë e lirë"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Përmbledhja"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Nuk ka asnjë artikull të fundit"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Mbyll"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cilësimet e përdorimit të aplikacionit"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Pastroji të gjitha"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</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 minutë"</string>
<string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura sot"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Aplikacionet e sugjeruara"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Sugjerimet e aplikacioneve"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Të gjitha aplikacionet"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Aplikacionet e tua të parashikuara"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Merr aplikacione të sugjeruara në rreshtin e poshtëm të ekranit tënd bazë"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Aplikacionet më të përdorura shfaqen këtu dhe ndryshojnë bazuar në rutinat"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Zvarrit aplikacionet jashtë rreshtit të poshtëm për të marrë aplikacione të sugjeruara"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Aplikacionet e sugjeruara u shtuan në hapësirën bosh"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Aplikacionet e sugjeruara janë aktivizuar"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Sugjerimet e aplikacioneve janë çaktivizuar"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Aplikacioni i parashikuar: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Ndaj"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Pamja e ekranit"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Ky veprim nuk lejohet nga aplikacioni ose organizata jote"</string>
</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index ae29bf5..b721641 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Подељени екран"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Слободни облик"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Нема недавних ставки"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Затвори"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Подешавања коришћења апликације"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Обриши све"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавне апликације"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Предлози апликација"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Све апликације"</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_accept" msgid="1611544083278999837">"Приказуј предлоге апликација"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не, хвала"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Подешавања"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Овде се приказују најчешће коришћене апликације и мењају се у зависности од употребе"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Превуците апликације из доњег реда да бисте добили предлоге"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Предлози апликација се додају на празно место"</string>
- <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="action_share" msgid="2648470652637092375">"Дели"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Снимак екрана"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Апликација или организација не дозвољавају ову радњу"</string>
</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index 8252fec..ba7ebcd 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Delad skärm"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Fäst"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Fritt format"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Översikt"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Listan med de senaste åtgärderna är tom"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Stäng"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Inställningar för appanvändning"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Rensa alla"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Appförslag"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Alla appar"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Föreslagna appar"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Få appförslag på den nedersta raden på startskärmen"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"De appar som används mest visas här och ändras efter dina rutiner"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Dra appar till den nedersta raden om du vill få appförslag"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Appförslag har lagts till på tom yta"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Appförslag har aktiverats"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Appförslag har inaktiverats"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Appförslag: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Dela"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Skärmdump"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Appen eller organisationen tillåter inte den här åtgärden"</string>
</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 778d558..24db429 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Gawa skrini"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Bandika"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Muundo huru"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Muhtasari"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Hakuna vipengee vya hivi karibuni"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Funga"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mipangilio ya matumizi ya programu"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Ondoa zote"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Programu za hivi karibuni"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Mapendekezo ya programu"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Programu zote"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Programu zako zinazopendekezwa"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Pata mapendekezo ya programu kwenye sehemu ya chini ya Skrini yako ya kwanza"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Programu zinazotumiwa zaidi huonekana hapa na hubadilika kulingana na ratiba"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Buruta programu kutoka kwenye safu ya chini ili upate mapendekezo ya programu"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Mapendekezo ya programu yamewekwa kwenye nafasi isiyo na kitu"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Mapendekezo ya programu yamewashwa"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Umezima mapendekezo ya programu"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Programu iliyotabiriwa: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Shiriki"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Picha ya skrini"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Kitendo hiki hakiruhusiwi na programu au shirika lako"</string>
</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index 5c1ef8f..97d51cd 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"திரைப் பிரிப்பு"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"பின் செய்தல்"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"குறிப்பிட்ட வடிவமில்லாத பயன்முறை"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"மேலோட்டப் பார்வை"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"சமீபத்தியவை எதுவுமில்லை"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"மூடும்"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ஆப்ஸ் உபயோக அமைப்புகள்"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"எல்லாம் அழி"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"சமீபத்திய ஆப்ஸ்"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"ஆப்ஸ் பரிந்துரைகள்"</string>
<string name="all_apps_label" msgid="8542784161730910663">"அனைத்து ஆப்ஸும்"</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_accept" msgid="1611544083278999837">"ஆப்ஸ் பரிந்துரைகளைப் பெறுக"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"வேண்டாம்"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"அமைப்புகள்"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"அதிகமாகப் பயன்படுத்திய ஆப்ஸ் இங்கே தோன்றும், வழக்கங்களின் அடிப்படையில் அவை மாறும்"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ஆப்ஸ் பரிந்துரைகளைப் பெற கடைசி வரிசையிலிருந்து ஆப்ஸை இழுக்கவும்"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ஆப்ஸ் பரிந்துரைகள் காலி இடத்தில் சேர்க்கப்பட்டன"</string>
- <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="action_share" msgid="2648470652637092375">"பகிர்"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ஸ்கிரீன்ஷாட்"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ஆப்ஸோ உங்கள் நிறுவனமோ இந்த செயலை அனுமதிப்பதில்லை"</string>
</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index b381af9..24b37f7 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"స్క్రీన్ని విభజించు"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"పిన్ చేయి"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"సంప్రదాయేతర"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"అవలోకనం"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"మూసివేయండి"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"యాప్ వినియోగ సెట్టింగ్లు"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"అన్నీ తీసివేయండి"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"ఇటీవలి యాప్లు"</string>
<string name="task_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 నిమిషం"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"యాప్ సూచనలు"</string>
<string name="all_apps_label" msgid="8542784161730910663">"అన్ని యాప్లు"</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_accept" msgid="1611544083278999837">"యాప్ సూచనలను పొందండి"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"వద్దు"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"సెట్టింగ్లు"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"ఎక్కువగా ఉపయోగించిన యాప్లు ఇక్కడ కనిపిస్తాయి, అవి రోజువారీ కార్యకలాపాలను బట్టి మారుతూ ఉంటాయి"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"యాప్ సలహాలను పొందడానికి దిగువ వరుస నుండి యాప్లను లాగండి"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"యాప్ సూచనలు ఖాళీ స్పేస్కు జోడించబడ్డాయి"</string>
- <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="action_share" msgid="2648470652637092375">"షేర్ చేయండి"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"స్క్రీన్షాట్"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ఈ చర్యను యాప్ గానీ, మీ సంస్థ గానీ అనుమతించవు"</string>
</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index 42e0c58..0f6821b 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"แยกหน้าจอ"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"ตรึง"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"รูปแบบอิสระ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ภาพรวม"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"ปิด"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"การตั้งค่าการใช้แอป"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"ล้างทั้งหมด"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"แอปล่าสุด"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"คำแนะนำเกี่ยวกับแอป"</string>
<string name="all_apps_label" msgid="8542784161730910663">"แอปทั้งหมด"</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_accept" msgid="1611544083278999837">"ดูแอปแนะนำ"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"ไม่เป็นไร"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"การตั้งค่า"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"แอปที่ใช้มากที่สุดจะปรากฎที่นี่และจะเปลี่ยนไปตามการใช้งานประจำ"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ลากแอปออกจากแถวล่างเพื่อดูแอปแนะนำ"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"เพิ่มคำแนะนำเกี่ยวกับแอปในพื้นที่ว่างแล้ว"</string>
- <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="action_share" msgid="2648470652637092375">"แชร์"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"ภาพหน้าจอ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"แอปหรือองค์กรของคุณไม่อนุญาตการดำเนินการนี้"</string>
</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 54aa564..491bac5 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Hatiin ang screen"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"I-pin"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Walang kamakailang item"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Isara"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Mga setting ng paggamit ng app"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"I-clear lahat"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Mga kamakailang app"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Mga iminumungkahing app"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Lahat ng app"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Iyong mga nahulaang app"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Makakuha ng mga suhestyon sa app sa ibabang row ng iyong Home screen"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Ipinapakita ang mga pinakaginagamit na app dito, at nababago ito batay sa mga routine"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"I-drag ang mga app mula sa ibabang row para makakuha ng mga suhestyon sa app"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Idinagdag sa bakanteng espasyo ang mga iminumungkahing app"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Naka-enable ang mga iminumungkahing app"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Naka-disable ang mga iminumungkahing app"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Hinulaang app: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Ibahagi"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Hindi pinapayagan ng app o ng iyong organisasyon ang pagkilos na ito"</string>
</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 2f9ef31..ec6d884 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Bölünmüş ekran"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Sabitle"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Serbest çalışma"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Genel bakış"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Yeni öğe yok"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Kapat"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Uygulama kullanım ayarları"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Tümünü temizle"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Son uygulamalar"</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 dk."</string>
<string name="time_left_for_app" msgid="3111996412933644358">"Bugün <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
- <string name="title_app_suggestions" msgid="4185902664111965088">"Önerilen uygulamalar"</string>
+ <string name="title_app_suggestions" msgid="4185902664111965088">"Uygulama önerileri"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Tüm uygulamalar"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Tahmin edilen uygulamalarınız"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Ana ekranınızın alt satırında uygulama önerileri alın"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"En çok kullanılan uygulamalar burada görünür ve rutinlere dayalı olarak değişir"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Önerilen uygulamaları almak için alt satırdaki uygulamaları dışarı sürükleyin"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Önerilen uygulamalar boş alana eklendi"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Uygulama önerileri etkinleştirildi"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Uygulama önerileri devre dışı bırakıldı"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Tahmin edilen uygulama: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Paylaş"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Ekran görüntüsü"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Uygulamanız veya kuruluşunuz bu işleme izin vermiyor"</string>
</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 7f12d8d..7736062 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Розділити екран"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Закріпити"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Довільна форма"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Огляд"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Немає нещодавніх додатків"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Закрити"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Налаштування використання додатка"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Очистити все"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Нещодавні додатки"</string>
<string name="task_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 хв"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"Пропозиції додатків"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Усі додатки"</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_accept" msgid="1611544083278999837">"Показувати рекомендації"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"Не потрібно"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"Налаштування"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Тут з\'являються найвикористовуваніші додатки, список яких змінюється залежно від ваших дій"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Щоб побачити рекомендовані додатки, перетягніть наявні з нижнього рядка"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Рекомендовані додатки додано у вільну область"</string>
- <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="action_share" msgid="2648470652637092375">"Поділитися"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Знімок екрана"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Ця дія заборонена додатком або адміністратором організації"</string>
</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 09cd4ae..87b303f 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"اسپلٹ اسکرین وضع"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"پن کریں"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"فری فارم"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"مجموعی جائزہ"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"کوئی حالیہ آئٹم نہیں"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"بند کریں"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"ایپ کے استعمال کی ترتیبات"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"سبھی کو صاف کریں"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"حالیہ ایپس"</string>
<string name="task_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 منٹ"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"ایپ کی تجاویز"</string>
<string name="all_apps_label" msgid="8542784161730910663">"تمام ایپس"</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_accept" msgid="1611544083278999837">"ایپس کی تجاویز حاصل کریں"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"نہیں شکریہ"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"ترتیبات"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"سب سے زیادہ مستعمل ایپس یہاں ظاہر ہوتی ہیں، اور روٹینز کی بنیاد پر تبدیل ہوتی ہیں"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"ایپس کی تجاویز حاصل کرنے کیلئے ایپس کو نچلی قطار سے نیچے گھسیٹیں"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"ایپس کی تجاویز کو خالی اسپیس میں شامل کر دیا گیا"</string>
- <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="action_share" msgid="2648470652637092375">"اشتراک کریں"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"اسکرین شاٹ"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"ایپ یا آپ کی تنظیم کی جانب سے اس کارروائی کی اجازت نہیں ہے"</string>
</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 737bd20..67c8e91 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Ekranni ikkiga ajratish"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Mahkamlash"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Erkin shakl"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Nazar"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Yopish"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Ilovadan foydalanish sozlamalari"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Hammasini tozalash"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Yaqinda ishlatilgan ilovalar"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Tavsiya etiladigan ilovalar"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Barcha ilovalar"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Taklif qilingan ilovalaringiz"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Tavsiya etiladigan ilovalar bosh ekran pastidagi qatorda chiqadi"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Faol ishlatiladigan ilovalar bu yerda chiqadi va oxirgi faoliyatingiz asosida almashadi"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Tavsiyalar olish uchun ilovalarni pastki qatordan tashqariga oling"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Tavsiya etilgan ilovalarni ochiq joylarga kiriting"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Ilova tavsiyalari yoqildi"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Endi ilova takliflari chiqmaydi"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Taklif etilgan ilova: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Ulashish"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Skrinshot"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Bu amal ilova yoki tashkilotingiz tomonidan taqiqlangan"</string>
</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 1d5fc42..34c89ef 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Chia đôi màn hình"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Ghim"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"Dạng tự do"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Tổng quan"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Không có mục gần đây nào"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Đóng"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Cài đặt mức sử dụng ứng dụng"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Xóa tất cả"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Ứng dụng gần đây"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Các ứng dụng đề xuất"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Tất cả ứng dụng"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Các ứng dụng gợi ý của bạn"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Nhận các ứng dụng đề xuất ở cuối Màn hình chính"</string>
- <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">"Ngay từ Màn hình chính, bạn có thể dễ dàng truy cập vào những ứng dụng mà mình dùng thường xuyên nhất. 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">"Ngay trên Màn hình chính, bạn có thể dễ dàng mở những ứng dụng mà mình dùng thường xuyên nhất. 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">"Ngay từ Màn hình chính, bạn có thể dễ dàng truy cập vào những ứng dụng mà mình dùng thường xuyên nhất. 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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Các ứng dụng dùng thường xuyên nhất sẽ hiển thị ở đây và thay đổi dựa trên thói quen"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Kéo ứng dụng ra khỏi hàng dưới cùng để xem ứng dụng đề xuất"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Thêm ứng dụng đề xuất vào không gian trống"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Đã bật tính năng Ứng dụng đề xuất"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Tính năng Ứng dụng đề xuất bị tắt"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Ứng dụng dự đoán: <xliff:g id="TITLE">%1$s</xliff:g>"</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="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>
</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index ed65978..0e83977 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -22,31 +22,16 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"分屏"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由窗口"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概览"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"近期没有任何内容"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"关闭"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"应用使用设置"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近用过的应用"</string>
<string name="task_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 分钟"</string>
<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="title_app_suggestions" msgid="4185902664111965088">"应用推荐"</string>
<string name="all_apps_label" msgid="8542784161730910663">"所有应用"</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_accept" msgid="1611544083278999837">"获取应用建议"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"设置"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"最常用的应用会显示在此处,显示的项目会根据日常安排而发生变化"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"将应用拖离底部,以获取应用建议"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"应用建议已添加到空白区域"</string>
- <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="action_share" msgid="2648470652637092375">"分享"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"该应用或您所在的单位不允许执行此操作"</string>
</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index d3fd7ee..ac7e8e9 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割畫面"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概覽"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"關閉"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string>
<string name="all_apps_label" msgid="8542784161730910663">"所有應用程式"</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_accept" msgid="1611544083278999837">"取得應用程式建議"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了,謝謝"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"最常用的應用程式會在這裡顯示,並會根據日常安排變更"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"將應用程式從底列拖曳出來,即可獲取應用程式建議"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"系統會將應用程式建議新增至空白位置"</string>
- <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="action_share" msgid="2648470652637092375">"分享"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"螢幕截圖"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"應用程式或您的機構不允許此操作"</string>
</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 3545a16..3323bfd 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"分割畫面"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"自由形式"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"總覽"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"關閉"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"應用程式使用情況設定"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"全部清除"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"應用程式建議"</string>
<string name="all_apps_label" msgid="8542784161730910663">"所有應用程式"</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_accept" msgid="1611544083278999837">"取得應用程式建議"</string>
- <string name="hotseat_edu_dismiss" msgid="2781161822780201689">"不用了,謝謝"</string>
- <string name="hotseat_prediction_settings" msgid="6246554993566070818">"設定"</string>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"最常使用的應用程式會顯示在這裡,顯示的項目會根據日常安排有所不同"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"將應用程式從底部列向外拖曳,即可取得應用程式建議"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"應用程式建議已新增到空白位置"</string>
- <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="action_share" msgid="2648470652637092375">"分享"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"擷取螢幕畫面"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"這個應用程式或貴機構不允許執行這個動作"</string>
</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index 43ed289..0f1d99d 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -22,7 +22,9 @@
<string name="recent_task_option_split_screen" msgid="5353188922202653570">"Hlukanisa isikrini"</string>
<string name="recent_task_option_pin" msgid="7929860679018978258">"Phina"</string>
<string name="recent_task_option_freeform" msgid="48863056265284071">"I-Freeform"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Buka konke"</string>
<string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
+ <string name="accessibility_close_task" msgid="5354563209433803643">"Vala"</string>
<string name="accessibility_app_usage_settings" msgid="6312864233673544149">"Izilungiselelo zokusetshenziswa kohlelo lokusebenza"</string>
<string name="recents_clear_all" msgid="5328176793634888831">"Sula konke"</string>
<string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
@@ -32,21 +34,4 @@
<string name="title_app_suggestions" msgid="4185902664111965088">"Iziphakamiso zohlelo lokusebenza"</string>
<string name="all_apps_label" msgid="8542784161730910663">"Zonke izinhlelo zokusebenza"</string>
<string name="all_apps_prediction_tip" msgid="2672336544844936186">"Izinhlelo zakho zokusebenza eziqagelwe"</string>
- <string name="hotseat_edu_title_migrate" msgid="306578144424489980">"Thola iziphakamiso ze-app emgqeni ongezansi wesikrini sakho sasekhaya"</string>
- <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>
- <string name="hotseat_auto_enrolled" msgid="522100018967146807">"Izinhlelo zokusebenza ezisetshenziswa kakhulu zivela lapha, kanye noshintsho olususelwe kwimijikelezo"</string>
- <string name="hotseat_tip_no_empty_slots" msgid="1325212677738179185">"Hudula izinhlelo zokusebenza kusuka emgqeni ongezansi ukuthola iziphakamiso zohlelo lokusebenza"</string>
- <string name="hotseat_tip_gaps_filled" msgid="3035673010274223538">"Iziphakamiso zohlelo lokusebenza ezengezwe esikhaleni esingenalutho"</string>
- <string name="hotsaet_tip_prediction_enabled" msgid="2233554377501347650">"Iziphakamiso zohlelo lokusebenza zinikwe amandla"</string>
- <string name="hotsaet_tip_prediction_disabled" msgid="1506426298884658491">"Iziphakamiso zohlelo lokusebenza zikhutshaziwe"</string>
- <string name="hotseat_prediction_content_description" msgid="4582028296938078419">"Uhlelo lokusebenza olubikezelwe: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
- <string name="action_share" msgid="2648470652637092375">"Yabelana"</string>
- <string name="action_screenshot" msgid="8171125848358142917">"Isithombe-skrini"</string>
- <string name="blocked_by_policy" msgid="2071401072261365546">"Lesi senzo asivunyelwanga uhlelo lokusebenza noma inhlangano yakho"</string>
</resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 40da136..54730f1 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -16,4 +16,16 @@
<resources>
<color name="back_arrow_color_light">#FFFFFFFF</color>
<color name="back_arrow_color_dark">#99000000</color>
+
+ <color name="chip_hint_foreground_color">#fff</color>
+ <color name="chip_scrim_start_color">#39000000</color>
+
+ <color name="all_apps_label_text">#61000000</color>
+ <color name="all_apps_label_text_dark">#61FFFFFF</color>
+ <color name="all_apps_prediction_row_separator">#3c000000</color>
+ <color name="all_apps_prediction_row_separator_dark">#3cffffff</color>
+
+ <!-- Taskbar -->
+ <color name="taskbar_background">#101010</color>
+ <color name="taskbar_divider">#C0C0C0</color>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index a57112b..be66104 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -14,8 +14,6 @@
limitations under the License.
-->
<resources>
- <string name="task_overlay_factory_class" translatable="false"/>
-
<string name="overscroll_plugin_factory_class" translatable="false" />
<!-- Activities which block home gesture -->
@@ -37,4 +35,6 @@
<integer name="assistant_gesture_corner_deg_threshold">20</integer>
<string name="wellbeing_provider_pkg" translatable="false"/>
+
+ <integer name="max_depth_blur_radius">150</integer>
</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 9d70316..9773366 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -19,6 +19,7 @@
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_half_top_margin">12dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
+ <dimen name="task_icon_top_margin">-16dp</dimen>
<!-- For screens without rounded corners -->
<dimen name="task_corner_radius_small">2dp</dimen>
@@ -28,41 +29,37 @@
<dimen name="overview_actions_bottom_margin_three_button">8dp</dimen>
<dimen name="overview_actions_horizontal_margin">16dp</dimen>
- <dimen name="recents_page_spacing">10dp</dimen>
+ <dimen name="recents_row_spacing">48dp</dimen>
+ <dimen name="recents_page_spacing">16dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
- <dimen name="overview_peek_distance">96dp</dimen>
<!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
loading full resolution screenshots. -->
<dimen name="recents_fast_fling_velocity">600dp</dimen>
- <!-- These velocities are in dp / s -->
- <dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
- <dimen name="quickstep_fling_min_velocity">250dp</dimen>
-
<!-- These speeds are in dp / ms -->
<dimen name="motion_pause_detector_speed_very_slow">0.0285dp</dimen>
<dimen name="motion_pause_detector_speed_slow">0.15dp</dimen>
<dimen name="motion_pause_detector_speed_somewhat_fast">0.285dp</dimen>
<dimen name="motion_pause_detector_speed_fast">1.4dp</dimen>
<dimen name="motion_pause_detector_min_displacement_from_app">36dp</dimen>
+ <dimen name="quickstep_fling_threshold_speed">0.5dp</dimen>
<!-- Launcher app transition -->
<dimen name="content_trans_y">50dp</dimen>
- <dimen name="workspace_trans_y">50dp</dimen>
<dimen name="closing_window_trans_y">115dp</dimen>
<dimen name="recents_empty_message_text_size">16sp</dimen>
<dimen name="recents_empty_message_text_padding">16dp</dimen>
+ <dimen name="max_shadow_radius">5dp</dimen>
+
<!-- Total space (start + end) between the task card and the edge of the screen
in various configurations -->
- <dimen name="task_card_vert_space">40dp</dimen>
<dimen name="task_card_menu_option_vertical_padding">8dp</dimen>
<dimen name="task_card_menu_shadow_height">3dp</dimen>
<dimen name="task_card_menu_horizontal_padding">0dp</dimen>
- <dimen name="portrait_task_card_horz_space">136dp</dimen>
- <dimen name="portrait_task_card_horz_space_big_overview">96dp</dimen>
+ <dimen name="portrait_task_card_horz_space_big_overview">132dp</dimen>
<dimen name="portrait_modal_task_card_horz_space">60dp</dimen>
<dimen name="landscape_task_card_horz_space">200dp</dimen>
<dimen name="multi_window_task_card_horz_space">100dp</dimen>
@@ -76,6 +73,10 @@
<dimen name="gestures_assistant_drag_threshold">55dp</dimen>
<dimen name="gestures_assistant_fling_threshold">55dp</dimen>
+ <!-- One-Handed Mode -->
+ <!-- Threshold for draging distance to enable one-handed mode -->
+ <dimen name="gestures_onehanded_drag_threshold">20dp</dimen>
+
<!-- Distance to move elements when swiping up to go home from launcher -->
<dimen name="home_pullback_distance">28dp</dimen>
@@ -98,4 +99,37 @@
<dimen name="swipe_edu_circle_size">64dp</dimen>
<dimen name="swipe_edu_width">80dp</dimen>
<dimen name="swipe_edu_max_height">184dp</dimen>
+
+ <dimen name="chip_hint_border_width">1dp</dimen>
+ <dimen name="chip_hint_corner_radius">20dp</dimen>
+ <dimen name="chip_hint_outer_padding">20dp</dimen>
+ <dimen name="chip_hint_start_padding">10dp</dimen>
+ <dimen name="chip_hint_end_padding">12dp</dimen>
+ <dimen name="chip_hint_horizontal_margin">20dp</dimen>
+ <dimen name="chip_hint_vertical_offset">16dp</dimen>
+ <dimen name="chip_hint_elevation">2dp</dimen>
+ <dimen name="chip_icon_size">16dp</dimen>
+ <dimen name="chip_text_height">26dp</dimen>
+ <dimen name="chip_text_top_padding">4dp</dimen>
+ <dimen name="chip_text_start_padding">10dp</dimen>
+ <dimen name="chip_text_size">14sp</dimen>
+
+ <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
+ <dimen name="all_apps_label_top_padding">16dp</dimen>
+ <dimen name="all_apps_label_bottom_padding">8dp</dimen>
+ <dimen name="all_apps_label_text_size">14sp</dimen>
+
+ <!-- Minimum distance to swipe to trigger accessibility gesture -->
+ <dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
+
+ <!-- Taskbar -->
+ <dimen name="taskbar_size">60dp</dimen>
+ <dimen name="taskbar_icon_size">44dp</dimen>
+ <dimen name="taskbar_icon_touch_size">48dp</dimen>
+ <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
+ <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+ <dimen name="taskbar_icon_spacing">8dp</dimen>
+ <dimen name="taskbar_divider_thickness">1dp</dimen>
+ <dimen name="taskbar_divider_height">32dp</dimen>
+ <dimen name="taskbar_folder_margin">16dp</dimen>
</resources>
diff --git a/quickstep/recents_ui_overrides/res/values/override.xml b/quickstep/res/values/override.xml
similarity index 82%
rename from quickstep/recents_ui_overrides/res/values/override.xml
rename to quickstep/res/values/override.xml
index 6aa9619..605774d 100644
--- a/quickstep/recents_ui_overrides/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -25,8 +25,6 @@
<string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
- <string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension</string>
+ <string name="model_delegate_class" translatable="false">com.android.launcher3.model.QuickstepModelDelegate</string>
- <string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
</resources>
-
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 769d298..9a4487c 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -93,6 +93,15 @@
<!-- content description for hotseat items -->
<string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>
+ <!-- primary educational text shown for first time search users -->
+ <string name="search_edu_primary">Search your phone for apps, people, settings and more!</string>
+ <!-- secondary educational text shown for first time search users -->
+ <string name="search_edu_secondary">Tap keyboard search button to launch the first search
+ result.</string>
+
+ <!-- Dismiss button string for search education view -->
+ <string name="search_edu_dismiss">Got it.</string>
+
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
<string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>
<!-- Subtitle shown during interactive parts of Back gesture tutorial for right edge. [CHAR LIMIT=60] -->
@@ -149,6 +158,21 @@
<!-- Feedback shown during interactive parts of Assistant gesture tutorial when the gesture doesn't go far enough. [CHAR LIMIT=100] -->
<string name="assistant_gesture_feedback_swipe_not_long_enough" translatable="false">Try swiping further</string>
+ <!-- Title shown in sandbox mode part of gesture tutorial. [CHAR LIMIT=30] -->
+ <string name="sandbox_mode_title" translatable="false">Sandbox Mode</string>
+ <!-- Subtitle shown in sandbox mode part of gesture tutorial. [CHAR LIMIT=60] -->
+ <string name="sandbox_mode_subtitle" translatable="false">Try any navigation gesture</string>
+ <!-- Feedback shown in sandbox mode when the back gesture is successfully issued. [CHAR LIMIT=60] -->
+ <string name="sandbox_mode_back_gesture_feedback_successful" translatable="false">Back gesture successful</string>
+ <!-- Feedback shown in sandbox mode when the assistant gesture is a successfully issued. [CHAR LIMIT=60] -->
+ <string name="sandbox_mode_assistant_gesture_feedback_successful" translatable="false">Assistant gesture successful</string>
+ <!-- Feedback shown in sandbox mode when the home gesture is a successfully issued. [CHAR LIMIT=60] -->
+ <string name="sandbox_mode_home_gesture_feedback_successful" translatable="false">Home gesture successful</string>
+ <!-- Feedback shown in sandbox mode when the overview gesture is a successfully issued. [CHAR LIMIT=60] -->
+ <string name="sandbox_mode_overview_gesture_feedback_successful" translatable="false">Overview gesture successful</string>
+ <!-- Feedback shown in sandbox mode when the back gesture swipe is too far from the edge. [CHAR LIMIT=60] -->
+ <string name="sandbox_mode_back_gesture_feedback_swipe_too_far_from_edge" translatable="false">Make sure you swipe from the left/right edge of the screen</string>
+
<!-- Title shown on the confirmation screen after successful gesture. [CHAR LIMIT=30] -->
<string name="gesture_tutorial_confirm_title" translatable="false">All set</string>
<!-- Button text shown on a button on the confirm screen to leave the tutorial. [CHAR LIMIT=14] -->
@@ -163,4 +187,4 @@
<string name="action_screenshot">Screenshot</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>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 8d054b4..df089f6 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -85,4 +85,9 @@
<item name="android:drawablePadding">8dp</item>
<item name="android:textAllCaps">false</item>
</style>
+
+ <!-- Icon displayed on the taskbar -->
+ <style name="BaseIcon.Workspace.Taskbar" >
+ <item name="iconDisplay">taskbar</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/xml/overview_file_provider_paths.xml b/quickstep/res/xml/overview_file_provider_paths.xml
index 14d7459..07e3236 100644
--- a/quickstep/res/xml/overview_file_provider_paths.xml
+++ b/quickstep/res/xml/overview_file_provider_paths.xml
@@ -2,4 +2,5 @@
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="shared_images" path="/" />
<files-path name="log_files" path="/" />
+ <root-path name="apps" path="/" />
</paths>
\ No newline at end of file
diff --git a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
new file mode 100644
index 0000000..70a143e
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -0,0 +1,263 @@
+/*
+ * 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;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetId;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Process;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
+import com.android.launcher3.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.util.ComponentKey;
+import com.android.launcher3.util.IntArray;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowAppWidgetManager;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsPredicationUpdateTaskTest {
+
+ private AppWidgetProviderInfo mApp1Provider1 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp1Provider2 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp2Provider1 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp4Provider1 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp4Provider2 = new AppWidgetProviderInfo();
+ private AppWidgetProviderInfo mApp5Provider1 = new AppWidgetProviderInfo();
+
+ private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
+ private Context mContext;
+ private LauncherModelHelper mModelHelper;
+ private UserHandle mUserHandle;
+ private InvariantDeviceProfile mTestProfile;
+
+ @Mock
+ private IconCache mIconCache;
+
+ @Before
+ public void setup() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doAnswer(invocation -> {
+ ComponentWithLabel componentWithLabel = invocation.getArgument(0);
+ return componentWithLabel.getComponent().getShortClassName();
+ }).when(mIconCache).getTitleNoCache(any());
+
+ mContext = RuntimeEnvironment.application;
+ mModelHelper = new LauncherModelHelper();
+ mUserHandle = Process.myUserHandle();
+ mTestProfile = new InvariantDeviceProfile();
+ // 2 widgets, app4/provider1 & app5/provider1, have already been added to the workspace.
+ mModelHelper.initializeData("/widgets_predication_update_task_data.txt");
+
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ mApp1Provider1.provider = ComponentName.createRelative("app1", "provider1");
+ ReflectionHelpers.setField(mApp1Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp1Provider1.provider));
+ mApp1Provider2.provider = ComponentName.createRelative("app1", "provider2");
+ ReflectionHelpers.setField(mApp1Provider2, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp1Provider2.provider));
+ mApp2Provider1.provider = ComponentName.createRelative("app2", "provider1");
+ ReflectionHelpers.setField(mApp2Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp2Provider1.provider));
+ mApp4Provider1.provider = ComponentName.createRelative("app4", "provider1");
+ ReflectionHelpers.setField(mApp4Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp4Provider1.provider));
+ mApp4Provider2.provider = ComponentName.createRelative("app4", ".provider2");
+ ReflectionHelpers.setField(mApp4Provider2, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp4Provider2.provider));
+ mApp5Provider1.provider = ComponentName.createRelative("app5", "provider1");
+ ReflectionHelpers.setField(mApp5Provider1, "providerInfo",
+ packageManager.addReceiverIfNotPresent(mApp5Provider1.provider));
+
+ ShadowAppWidgetManager shadowAppWidgetManager =
+ shadowOf(mContext.getSystemService(AppWidgetManager.class));
+ shadowAppWidgetManager.addInstalledProvider(mApp1Provider1);
+ shadowAppWidgetManager.addInstalledProvider(mApp1Provider2);
+ shadowAppWidgetManager.addInstalledProvider(mApp2Provider1);
+ shadowAppWidgetManager.addInstalledProvider(mApp4Provider1);
+ shadowAppWidgetManager.addInstalledProvider(mApp4Provider2);
+ shadowAppWidgetManager.addInstalledProvider(mApp5Provider1);
+
+ mModelHelper.getModel().addCallbacks(mCallback);
+
+ MODEL_EXECUTOR.post(() -> mModelHelper.getBgDataModel().widgetsModel.update(
+ LauncherAppState.getInstance(mContext), /* packageUser= */ null));
+ waitUntilIdle();
+ }
+
+
+ @Test
+ public void widgetsRecommendationRan_shouldOnlyReturnNotAddedWidgetsInAppPredictionOrder()
+ throws Exception {
+ // WHEN newPredicationTask is executed with app predication of 5 apps.
+ AppTarget app1 = new AppTarget(new AppTargetId("app1"), "app1", "className",
+ mUserHandle);
+ AppTarget app2 = new AppTarget(new AppTargetId("app2"), "app2", "className",
+ mUserHandle);
+ AppTarget app3 = new AppTarget(new AppTargetId("app3"), "app3", "className",
+ mUserHandle);
+ AppTarget app4 = new AppTarget(new AppTargetId("app4"), "app4", "className",
+ mUserHandle);
+ AppTarget app5 = new AppTarget(new AppTargetId("app5"), "app5", "className",
+ mUserHandle);
+ mModelHelper.executeTaskForTest(
+ newWidgetsPredicationTask(List.of(app5, app3, app2, app4, app1)))
+ .forEach(Runnable::run);
+
+ // THEN only 3 widgets are returned because
+ // 1. app5/provider1 & app4/provider1 have already been added to workspace. They are
+ // excluded from the result.
+ // 2. app3 doesn't have a widget.
+ // 3. only 1 widget is picked from app1 because we only want to promote one widget per app.
+ List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+ .stream()
+ .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+ .collect(Collectors.toList());
+ assertThat(recommendedWidgets).hasSize(3);
+ assertWidgetInfo(recommendedWidgets.get(0).info, mApp2Provider1);
+ assertWidgetInfo(recommendedWidgets.get(1).info, mApp4Provider2);
+ assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+ }
+
+ private void assertWidgetInfo(
+ LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
+ assertThat(actual.provider).isEqualTo(expected.provider);
+ assertThat(actual.getUser()).isEqualTo(expected.getProfile());
+ }
+
+ private void waitUntilIdle() {
+ shadowOf(MODEL_EXECUTOR.getLooper()).idle();
+ shadowOf(MAIN_EXECUTOR.getLooper()).idle();
+ }
+
+ private WidgetsPredictionUpdateTask newWidgetsPredicationTask(List<AppTarget> appTargets) {
+ return new WidgetsPredictionUpdateTask(
+ new PredictorState(CONTAINER_WIDGETS_PREDICTION, "test_widgets_prediction"),
+ appTargets);
+ }
+
+ private final class FakeBgDataModelCallback implements BgDataModel.Callbacks {
+
+ private FixedContainerItems mRecommendedWidgets = null;
+
+ @Override
+ public void bindExtraContainerItems(FixedContainerItems item) {
+ mRecommendedWidgets = item;
+ }
+
+ @Override
+ public int getPageToBindSynchronously() {
+ return 0;
+ }
+
+ @Override
+ public void clearPendingBinds() { }
+
+ @Override
+ public void startBinding() { }
+
+ @Override
+ public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
+
+ @Override
+ public void bindScreens(IntArray orderedScreenIds) { }
+
+ @Override
+ public void finishFirstPageBind(ViewOnDrawExecutor executor) { }
+
+ @Override
+ public void finishBindingItems(int pageBoundFirst) { }
+
+ @Override
+ public void preAddApps() { }
+
+ @Override
+ public void bindAppsAdded(IntArray newScreens, ArrayList<ItemInfo> addNotAnimated,
+ ArrayList<ItemInfo> addAnimated) { }
+
+ @Override
+ public void bindIncrementalDownloadProgressUpdated(AppInfo app) { }
+
+ @Override
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
+
+ @Override
+ public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
+
+ @Override
+ public void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
+
+ @Override
+ public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
+
+ @Override
+ public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
+
+ @Override
+ public void onPageBoundSynchronously(int page) { }
+
+ @Override
+ public void executeOnNextDraw(ViewOnDrawExecutor executor) { }
+
+ @Override
+ public void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap) { }
+
+ @Override
+ public void bindAllApplications(AppInfo[] apps, int flags) { }
+ }
+}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
index 22d205a..eca27b5 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/OrientationTouchTransformerTest.java
@@ -32,7 +32,7 @@
import android.view.Surface;
import com.android.launcher3.ResourceUtils;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
import org.junit.Before;
import org.junit.Ignore;
@@ -43,14 +43,24 @@
@RunWith(RobolectricTestRunner.class)
public class OrientationTouchTransformerTest {
- private static final int SIZE_WIDTH = 1080;
- private static final int SIZE_HEIGHT = 2280;
+ static class ScreenSize {
+ int mHeight;
+ int mWidth;
+
+ ScreenSize(int height, int width) {
+ mHeight = height;
+ mWidth = width;
+ }
+ }
+
+ private static final ScreenSize NORMAL_SCREEN_SIZE = new ScreenSize(2280, 1080);
+ private static final ScreenSize LARGE_SCREEN_SIZE = new ScreenSize(3280, 1080);
private static final float DENSITY_DISPLAY_METRICS = 3.0f;
private OrientationTouchTransformer mTouchTransformer;
Resources mResources;
- private DefaultDisplay.Info mInfo;
+ private DisplayController.Info mInfo;
@Before
@@ -63,14 +73,16 @@
DisplayMetrics mockDisplayMetrics = new DisplayMetrics();
mockDisplayMetrics.density = DENSITY_DISPLAY_METRICS;
when(mResources.getDisplayMetrics()).thenReturn(mockDisplayMetrics);
- mInfo = createDisplayInfo(Surface.ROTATION_0);
+ mInfo = createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_0);
mTouchTransformer = new OrientationTouchTransformer(mResources, NO_BUTTON, () -> 0);
}
@Test
public void disabledMultipleRegions_shouldOverrideFirstRegion() {
- float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
- float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+ float portraitRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+ float landscapeRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertTrue(100, portraitRegionY,
@@ -83,7 +95,8 @@
event -> mTouchTransformer.touchInAssistantRegion(event));
// Override region
- mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+ mTouchTransformer
+ .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
tapAndAssertFalse(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(100, landscapeRegionY,
@@ -107,10 +120,13 @@
@Test
public void enableMultipleRegions_shouldOverrideFirstRegion() {
- float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
- float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+ float portraitRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+ float landscapeRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
- mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+ mTouchTransformer
+ .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
tapAndAssertFalse(100, portraitRegionY,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
tapAndAssertTrue(100, landscapeRegionY,
@@ -136,11 +152,14 @@
@Test
public void enableMultipleRegions_assistantTriggersInMostRecent() {
- float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
- float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+ float portraitRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+ float landscapeRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer.enableMultipleRegions(true, mInfo);
- mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+ mTouchTransformer
+ .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
@@ -150,12 +169,15 @@
@Test
public void enableMultipleRegions_assistantTriggersInCurrentOrientationAfterDisable() {
- float portraitRegionY = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
- float landscapeRegionY = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+ float portraitRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+ float landscapeRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
mTouchTransformer.enableMultipleRegions(true, mInfo);
mTouchTransformer.createOrAddTouchRegion(mInfo);
- mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+ mTouchTransformer
+ .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
mTouchTransformer.enableMultipleRegions(false, mInfo);
tapAndAssertTrue(0, portraitRegionY,
event -> mTouchTransformer.touchInAssistantRegion(event));
@@ -164,6 +186,26 @@
}
@Test
+ public void assistantTriggersInCurrentScreenAfterScreenSizeChange() {
+ float smallerScreenPortraitRegionY =
+ generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+ float largerScreenPortraitRegionY =
+ generateTouchRegionHeight(LARGE_SCREEN_SIZE, Surface.ROTATION_0) + 1;
+
+ mTouchTransformer.enableMultipleRegions(false,
+ createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_0));
+ tapAndAssertTrue(0, smallerScreenPortraitRegionY,
+ event -> mTouchTransformer.touchInAssistantRegion(event));
+
+ mTouchTransformer
+ .enableMultipleRegions(false, createDisplayInfo(LARGE_SCREEN_SIZE, Surface.ROTATION_0));
+ tapAndAssertTrue(0, largerScreenPortraitRegionY,
+ event -> mTouchTransformer.touchInAssistantRegion(event));
+ tapAndAssertFalse(0, smallerScreenPortraitRegionY,
+ event -> mTouchTransformer.touchInAssistantRegion(event));
+ }
+
+ @Test
public void applyTransform_taskNotFrozen_notInRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
tapAndAssertFalse(100, 100,
@@ -182,7 +224,7 @@
public void applyTransform_taskFrozen_noRotate_inRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
- float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
+ float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
tapAndAssertTrue(100, y,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@@ -190,15 +232,16 @@
@Test
public void applyTransform_taskNotFrozen_noRotate_inDefaultRegion() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
- float y = generateTouchRegionHeight(Surface.ROTATION_0) + 1;
+ float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_0) + 1;
tapAndAssertTrue(100, y,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@Test
public void applyTransform_taskNotFrozen_90Rotate_inRegion() {
- mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
- float y = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+ mTouchTransformer
+ .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
+ float y = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
tapAndAssertTrue(100, y,
event -> mTouchTransformer.touchInValidSwipeRegions(event.getX(), event.getY()));
}
@@ -210,9 +253,10 @@
public void applyTransform_taskNotFrozen_90Rotate_inTwoRegions() {
mTouchTransformer.createOrAddTouchRegion(mInfo);
mTouchTransformer.enableMultipleRegions(true, mInfo);
- mTouchTransformer.createOrAddTouchRegion(createDisplayInfo(Surface.ROTATION_90));
+ mTouchTransformer
+ .createOrAddTouchRegion(createDisplayInfo(NORMAL_SCREEN_SIZE, Surface.ROTATION_90));
// Landscape point
- float y1 = generateTouchRegionHeight(Surface.ROTATION_90) + 1;
+ float y1 = generateTouchRegionHeight(NORMAL_SCREEN_SIZE, Surface.ROTATION_90) + 1;
MotionEvent inRegion1_down = generateMotionEvent(MotionEvent.ACTION_DOWN, 10, y1);
MotionEvent inRegion1_up = generateMotionEvent(MotionEvent.ACTION_UP, 10, y1);
// Portrait point in landscape orientation axis
@@ -231,18 +275,18 @@
assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY()));
}
- private DefaultDisplay.Info createDisplayInfo(int rotation) {
- Point p = new Point(SIZE_WIDTH, SIZE_HEIGHT);
+ private DisplayController.Info createDisplayInfo(ScreenSize screenSize, int rotation) {
+ Point p = new Point(screenSize.mWidth, screenSize.mHeight);
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
- p = new Point(SIZE_HEIGHT, SIZE_WIDTH);
+ p = new Point(screenSize.mHeight, screenSize.mWidth);
}
- return new DefaultDisplay.Info(0, rotation, 0, p, p, p, null);
+ return new DisplayController.Info(0, rotation, 0, p, p, p, null);
}
- private float generateTouchRegionHeight(int rotation) {
- float height = SIZE_HEIGHT;
+ private float generateTouchRegionHeight(ScreenSize screenSize, int rotation) {
+ float height = screenSize.mHeight;
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
- height = SIZE_WIDTH;
+ height = screenSize.mWidth;
}
return height - ResourceUtils.DEFAULT_NAVBAR_VALUE * DENSITY_DISPLAY_METRICS;
}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index c148a4b..7049af0 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -60,9 +60,9 @@
FallbackRecentsView frv = activity.getOverviewPanel();
- RunningTaskInfo dummyTask = new RunningTaskInfo();
- dummyTask.taskId = 22;
- frv.showCurrentTask(dummyTask);
+ RunningTaskInfo placeholderTask = new RunningTaskInfo();
+ placeholderTask.taskId = 22;
+ frv.showCurrentTask(placeholderTask);
doLayout(activity);
ThumbnailData thumbnailData = new ThumbnailData();
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
new file mode 100644
index 0000000..656379f
--- /dev/null
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/RecentsOrientedStateTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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 static android.view.Surface.ROTATION_90;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.content.Context;
+
+import com.android.quickstep.FallbackActivityInterface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
+
+/**
+ * Tests for {@link RecentsOrientedState}
+ */
+@RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
+public class RecentsOrientedStateTest {
+
+ private RecentsOrientedState mR1, mR2;
+
+ @Before
+ public void setup() {
+ Context context = RuntimeEnvironment.application;
+ mR1 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
+ mR2 = new RecentsOrientedState(context, FallbackActivityInterface.INSTANCE, i -> { });
+ assertEquals(mR1.getStateId(), mR2.getStateId());
+ }
+
+ @Test
+ public void stateId_changesWithFlags() {
+ mR1.setGestureActive(true);
+ mR2.setGestureActive(false);
+ assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+ mR2.setGestureActive(true);
+ assertEquals(mR1.getStateId(), mR2.getStateId());
+ }
+
+ @Test
+ public void stateId_changesWithRecentsRotation() {
+ mR1.setRecentsRotation(ROTATION_90);
+ mR2.setRecentsRotation(ROTATION_180);
+ assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+ mR2.setRecentsRotation(ROTATION_90);
+ assertEquals(mR1.getStateId(), mR2.getStateId());
+ }
+
+ @Test
+ public void stateId_changesWithDisplayRotation() {
+ mR1.update(ROTATION_0, ROTATION_90);
+ mR2.update(ROTATION_0, ROTATION_180);
+ assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+ mR2.update(ROTATION_90, ROTATION_90);
+ assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+ mR2.update(ROTATION_90, ROTATION_0);
+ assertNotEquals(mR1.getStateId(), mR2.getStateId());
+
+ mR2.update(ROTATION_0, ROTATION_90);
+ assertEquals(mR1.getStateId(), mR2.getStateId());
+ }
+}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index a31ba21..688f323 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -27,7 +27,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.shadows.LShadowDisplay;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.LauncherActivityInterface;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
@@ -144,11 +144,11 @@
LauncherActivityInterface.INSTANCE);
tvs.setDp(mDeviceProfile);
- int launcherRotation = DefaultDisplay.INSTANCE.get(mContext).getInfo().rotation;
+ int launcherRotation = DisplayController.getDefaultDisplay(mContext).getInfo().rotation;
if (mAppRotation < 0) {
mAppRotation = launcherRotation;
}
- tvs.setLayoutRotation(launcherRotation, mAppRotation);
+ tvs.getOrientationState().update(launcherRotation, mAppRotation);
if (mAppInsets == null) {
mAppInsets = new Rect(mLauncherInsets);
}
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 235df42..641e108 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -19,19 +19,23 @@
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.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_SIZE;
+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.ActivityOptions;
import android.content.Intent;
import android.content.IntentSender;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.view.View;
+
+import androidx.annotation.Nullable;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
@@ -39,23 +43,27 @@
import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateHandler;
+import com.android.launcher3.taskbar.TaskbarActivityContext;
+import com.android.launcher3.taskbar.TaskbarController;
+import com.android.launcher3.taskbar.TaskbarStateHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
-import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.util.QuickstepOnboardingPrefs;
+import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
-import com.android.quickstep.util.ShelfPeekAnim;
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 java.util.List;
import java.util.stream.Stream;
/**
@@ -73,10 +81,12 @@
(context, arg1, arg2) -> SystemUiProxy.INSTANCE.get(context).setBackButtonAlpha(
Float.intBitsToFloat(arg1), arg2 != 0);
- private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
-
private OverviewActionsView mActionsView;
- protected HotseatPredictionController mHotseatPredictionController;
+
+ private @Nullable TaskbarController mTaskbarController;
+ private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
+ // Will be updated when dragging from taskbar.
+ private @Nullable DragOptions mNextWorkspaceDragOptions = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -89,13 +99,18 @@
@Override
public void onDestroy() {
SysUINavigationMode.INSTANCE.get(this).removeModeChangeListener(this);
+
+ if (mTaskbarController != null) {
+ mTaskbarController.cleanup();
+ }
+
super.onDestroy();
}
@Override
public void onNavigationModeChanged(Mode newMode) {
getDragLayer().recreateControllers();
- if (mActionsView != null && isOverviewActionsEnabled()) {
+ if (mActionsView != null) {
mActionsView.updateVerticalMargin(newMode);
}
}
@@ -126,7 +141,8 @@
@Override
protected void onUiChangedWhileSleeping() {
// Remove the snapshot because the content view may have obvious changes.
- ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this);
+ UI_HELPER_EXECUTOR.execute(
+ () -> ActivityManagerWrapper.getInstance().invalidateHomeTaskSnapshot(this));
}
@Override
@@ -168,9 +184,14 @@
}
@Override
- protected void handlePendingActivityRequest() {
- super.handlePendingActivityRequest();
- if (mPendingActivityRequestCode != -1 && isInState(NORMAL)) {
+ 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
@@ -186,17 +207,31 @@
SysUINavigationMode.INSTANCE.get(this).updateMode();
mActionsView = findViewById(R.id.overview_actions_view);
((RecentsView) getOverviewPanel()).init(mActionsView);
+ mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
- if (isOverviewActionsEnabled()) {
- // Overview is above all other launcher elements, including qsb, so move it to the top.
- getOverviewPanel().bringToFront();
- mActionsView.bringToFront();
- mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
+ addTaskbarIfNecessary();
+ addOnDeviceProfileChangeListener(newDp -> addTaskbarIfNecessary());
+ }
+
+ @Override
+ public void onDisplayInfoChanged(DisplayController.Info info, int flags) {
+ super.onDisplayInfoChanged(info, flags);
+ if ((flags & CHANGE_SIZE) != 0) {
+ addTaskbarIfNecessary();
}
}
- private boolean isOverviewActionsEnabled() {
- return FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this);
+ private void addTaskbarIfNecessary() {
+ if (mTaskbarController != null) {
+ mTaskbarController.cleanup();
+ mTaskbarController = null;
+ }
+ if (mDeviceProfile.isTaskbarPresent) {
+ TaskbarActivityContext taskbarActivityContext = new TaskbarActivityContext(this);
+ mTaskbarController = new TaskbarController(this,
+ taskbarActivityContext.getTaskbarContainerView());
+ mTaskbarController.init();
+ }
}
public <T extends OverviewActionsView> T getActionsView() {
@@ -206,27 +241,47 @@
@Override
protected void closeOpenViews(boolean animate) {
super.closeOpenViews(animate);
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
}
@Override
- protected StateHandler<LauncherState>[] createStateHandlers() {
- return new StateHandler[] {
- getAllAppsController(),
- getWorkspace(),
- getDepthController(),
- new RecentsViewStateController(this),
- new BackButtonAlphaHandler(this)};
+ protected void collectStateHandlers(List<StateHandler> out) {
+ super.collectStateHandlers(out);
+ out.add(getDepthController());
+ out.add(new RecentsViewStateController(this));
+ out.add(new BackButtonAlphaHandler(this));
+ out.add(getTaskbarStateHandler());
}
public DepthController getDepthController() {
return mDepthController;
}
+ public @Nullable TaskbarController getTaskbarController() {
+ return mTaskbarController;
+ }
+
+ public TaskbarStateHandler getTaskbarStateHandler() {
+ return mTaskbarStateHandler;
+ }
+
@Override
- protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
- return new QuickstepOnboardingPrefs(this, sharedPrefs);
+ public boolean isViewInTaskbar(View v) {
+ return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
+ }
+
+ @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
@@ -253,7 +308,7 @@
@Override
public float[] getNormalOverviewScaleAndOffset() {
- return SysUINavigationMode.getMode(this) == Mode.NO_BUTTON
+ return SysUINavigationMode.getMode(this).hasGestures
? new float[] {1, 1} : new float[] {1.1f, 0};
}
@@ -273,6 +328,12 @@
mDepthController.setActivityStarted(isStarted());
}
+ if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+ if (mTaskbarController != null) {
+ mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
+ }
+ }
+
super.onActivityFlagsChanged(changeBits);
}
@@ -317,15 +378,13 @@
Stream.of(WellbeingModel.SHORTCUT_FACTORY));
}
- public ShelfPeekAnim getShelfPeekAnim() {
- return mShelfPeekAnim;
- }
-
- /**
- * Returns Prediction controller for hybrid hotseat
- */
- public HotseatPredictionController getHotseatPredictionController() {
- return mHotseatPredictionController;
+ @Override
+ public ActivityOptions getActivityLaunchOptions(View v) {
+ ActivityOptions activityOptions = super.getActivityLaunchOptions(v);
+ if (activityOptions != null && mLastTouchUpTime > 0) {
+ ActivityOptionsCompat.setLauncherSourceInfo(activityOptions, mLastTouchUpTime);
+ }
+ return activityOptions;
}
public void setHintUserWillBeActive() {
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 02769c6..588d676 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -16,7 +16,8 @@
package com.android.launcher3;
import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
import android.animation.Animator;
@@ -34,8 +35,7 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat,
- WrappedAnimationRunnerImpl {
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
private static final String TAG = "LauncherAnimationRunner";
@@ -56,17 +56,22 @@
return mHandler;
}
- // Called only in R+ platform
+ // Called only in S+ platform
@BinderThread
- public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+ public void onAnimationStart(
+ int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ Runnable runnable) {
Runnable r = () -> {
finishExistingAnimation();
mAnimationResult = new AnimationResult(() -> {
- runnable.run();
+ UI_HELPER_EXECUTOR.execute(runnable);
mAnimationResult = null;
});
- onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
+ onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+ mAnimationResult);
};
if (mStartAtFrontOfQueue) {
postAtFrontOfQueueAsynchronously(mHandler, r);
@@ -75,6 +80,14 @@
}
}
+ // 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
@@ -88,8 +101,11 @@
*/
@UiThread
public abstract void onCreateAnimation(
+ int transit,
RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ AnimationResult result);
@UiThread
private void finishExistingAnimation() {
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..ae4bd96
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * A {@link QuickstepAppTransitionManagerImpl} that also implements recents transitions from
+ * {@link RecentsView}.
+ */
+public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransitionManagerImpl {
+
+ public LauncherAppTransitionManagerImpl(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected boolean isLaunchingFromRecents(@NonNull View v,
+ @Nullable RemoteAnimationTargetCompat[] targets) {
+ return mLauncher.getStateManager().getState().overviewUi
+ && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
+ }
+
+ @Override
+ protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
+ TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
+ launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
+ mLauncher.getDepthController());
+ }
+
+ @Override
+ protected Runnable composeViewContentAnimator(@NonNull AnimatorSet anim, float[] alphas,
+ float[] trans) {
+ RecentsView overview = mLauncher.getOverviewPanel();
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(overview,
+ RecentsView.CONTENT_ALPHA, alphas);
+ alpha.setDuration(CONTENT_ALPHA_DURATION);
+ alpha.setInterpolator(LINEAR);
+ anim.play(alpha);
+ overview.setFreezeViewVisibility(true);
+
+ ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+ transY.setInterpolator(AGGRESSIVE_EASE);
+ transY.setDuration(CONTENT_TRANSLATION_DURATION);
+ anim.play(transY);
+
+ return () -> {
+ overview.setFreezeViewVisibility(false);
+ overview.setTranslationY(0);
+ mLauncher.getStateManager().reapplyState();
+ };
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
new file mode 100644
index 0000000..96559cb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/QuickstepAccessibilityDelegate.java
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+
+import java.util.List;
+
+public class QuickstepAccessibilityDelegate extends LauncherAccessibilityDelegate {
+
+ public QuickstepAccessibilityDelegate(QuickstepLauncher launcher) {
+ super(launcher);
+ mActions.put(PIN_PREDICTION, new LauncherAction(
+ PIN_PREDICTION, R.string.pin_prediction, KeyEvent.KEYCODE_P));
+ }
+
+ @Override
+ protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
+ if (host instanceof PredictedAppIcon && !((PredictedAppIcon) host).isPinned()) {
+ out.add(new LauncherAction(PIN_PREDICTION, R.string.pin_prediction,
+ KeyEvent.KEYCODE_P));
+ }
+ super.getSupportedActions(host, item, out);
+ }
+
+ @Override
+ protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
+ QuickstepLauncher launcher = (QuickstepLauncher) mLauncher;
+ if (action == PIN_PREDICTION) {
+ if (launcher.getHotseatPredictionController() == null) {
+ return false;
+ }
+ launcher.getHotseatPredictionController().pinPrediction(item);
+ return true;
+ }
+ return super.performAction(host, item, action, fromKeyboard);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 2d096d1..f14c5bf 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE;
+import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+
import static com.android.launcher3.BaseActivity.INVISIBLE_ALL;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_PENDING_FLAGS;
@@ -33,7 +36,6 @@
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
@@ -45,7 +47,6 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -55,10 +56,10 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemProperties;
import android.util.Pair;
import android.view.View;
@@ -67,6 +68,7 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -76,31 +78,39 @@
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
+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 java.util.LinkedHashMap;
+
/**
* {@link LauncherAppTransitionManager} with Quickstep-specific app transitions for launching from
* home and/or all-apps. Not used for 3p launchers.
*/
-@TargetApi(Build.VERSION_CODES.O)
@SuppressWarnings("unused")
public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTransitionManager
implements OnDeviceProfileChangeListener {
private static final String TAG = "QuickstepTransition";
+ private static final boolean ENABLE_SHELL_STARTING_SURFACE =
+ SystemProperties.getBoolean("persist.debug.shell_starting_surface", false);
+
/** Duration of status bar animations. */
public static final int STATUS_BAR_TRANSITION_DURATION = 120;
@@ -135,12 +145,14 @@
private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 250;
- protected static final int CONTENT_ALPHA_DURATION = 217;
+ public static final int CONTENT_ALPHA_DURATION = 217;
protected static final int CONTENT_TRANSLATION_DURATION = 350;
// Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
+ private static final int MAX_NUM_TASKS = 5;
+
protected final BaseQuickstepLauncher mLauncher;
private final DragLayer mDragLayer;
@@ -150,8 +162,8 @@
private final boolean mIsRtl;
private final float mContentTransY;
- private final float mWorkspaceTransY;
private final float mClosingWindowTransY;
+ private final float mMaxShadowRadius;
private DeviceProfile mDeviceProfile;
@@ -161,6 +173,9 @@
private WrappedAnimationRunnerImpl mAppLaunchRunner;
private WrappedAnimationRunnerImpl mKeyguardGoingAwayRunner;
+ private WrappedAnimationRunnerImpl mWallpaperOpenTransitionRunner;
+ private RemoteTransitionCompat mLauncherOpenTransition;
+
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -173,6 +188,9 @@
}
};
+ // Will never be larger than MAX_NUM_TASKS
+ private LinkedHashMap<Integer, Integer> mTypeForTaskId;
+
public QuickstepAppTransitionManagerImpl(Context context) {
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mDragLayer = mLauncher.getDragLayer();
@@ -183,10 +201,27 @@
Resources res = mLauncher.getResources();
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
- mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
+ mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
mLauncher.addOnDeviceProfileChangeListener(this);
+
+ if (supportsSSplashScreen()) {
+ mTypeForTaskId = new LinkedHashMap<Integer, Integer>(MAX_NUM_TASKS) {
+ @Override
+ protected boolean removeEldestEntry(Entry<Integer, Integer> entry) {
+ return size() > MAX_NUM_TASKS;
+ }
+ };
+
+ SystemUiProxy.INSTANCE.get(mLauncher).setStartingWindowListener(
+ new IStartingWindowListener.Stub() {
+ @Override
+ public void onTaskLaunching(int taskId, int supportedType) {
+ mTypeForTaskId.put(taskId, supportedType);
+ }
+ });
+ }
}
@Override
@@ -195,9 +230,10 @@
}
@Override
- public boolean supportsAdaptiveIconAnimation() {
+ public boolean supportsAdaptiveIconAnimation(View clickedView) {
return hasControlRemoteAppTransitionPermission()
- && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
+ && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
+ && !mLauncher.isViewInTaskbar(clickedView);
}
/**
@@ -251,6 +287,18 @@
@NonNull RemoteAnimationTargetCompat[] appTargets,
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing);
+ private boolean areAllTargetsTranslucent(@NonNull RemoteAnimationTargetCompat[] targets) {
+ boolean isAllOpeningTargetTrs = true;
+ for (int i = 0; i < targets.length; i++) {
+ RemoteAnimationTargetCompat target = targets[i];
+ if (target.mode == MODE_OPENING) {
+ isAllOpeningTargetTrs &= target.isTranslucent;
+ }
+ if (!isAllOpeningTargetTrs) break;
+ }
+ return isAllOpeningTargetTrs;
+ }
+
/**
* Compose the animations for a launch from the app icon.
*
@@ -268,16 +316,8 @@
mLauncher.getStateManager().setCurrentAnimation(anim);
Rect windowTargetBounds = getWindowTargetBounds(appTargets);
- boolean isAllOpeningTargetTrs = true;
- for (int i = 0; i < appTargets.length; i++) {
- RemoteAnimationTargetCompat target = appTargets[i];
- if (target.mode == MODE_OPENING) {
- isAllOpeningTargetTrs &= target.isTranslucent;
- }
- if (!isAllOpeningTargetTrs) break;
- }
anim.play(getOpeningWindowAnimators(v, appTargets, wallpaperTargets, windowTargetBounds,
- !isAllOpeningTargetTrs));
+ areAllTargetsTranslucent(appTargets)));
if (launcherClosing) {
Pair<AnimatorSet, Runnable> launcherContentAnimator =
getLauncherContentAnimator(true /* isAppOpening */,
@@ -324,6 +364,15 @@
return bounds;
}
+ private int getOpeningTaskId(RemoteAnimationTargetCompat[] appTargets) {
+ for (RemoteAnimationTargetCompat target : appTargets) {
+ if (target.mode == MODE_OPENING) {
+ return target.taskId;
+ }
+ }
+ return -1;
+ }
+
public void setRemoteAnimationProvider(final RemoteAnimationProvider animationProvider,
CancellationSignal cancellationSignal) {
mRemoteAnimationProvider = animationProvider;
@@ -443,10 +492,10 @@
private Animator getOpeningWindowAnimators(View v,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
- Rect windowTargetBounds, boolean toggleVisibility) {
- RectF bounds = new RectF();
+ Rect windowTargetBounds, boolean appTargetsAreTranslucent) {
+ RectF launcherIconBounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
- toggleVisibility, bounds, true /* isOpening */);
+ !appTargetsAreTranslucent, launcherIconBounds, true /* isOpening */);
Rect crop = new Rect();
Matrix matrix = new Matrix();
@@ -456,43 +505,32 @@
new SurfaceTransactionApplier(floatingView);
openingTargets.addReleaseCheck(surfaceApplier);
- // Scale the app icon to take up the entire screen. This simplifies the math when
- // animating the app window position / scale.
- float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
- float maxScaleX = smallestSize / bounds.width();
- float maxScaleY = smallestSize / bounds.height();
- float scale = Math.max(maxScaleX, maxScaleY);
- float startScale = 1f;
- if (v instanceof BubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
- Drawable dr = ((BubbleTextView) v).getIcon();
- if (dr instanceof FastBitmapDrawable) {
- startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
- }
- }
- final float initialStartScale = startScale;
-
int[] dragLayerBounds = new int[2];
mDragLayer.getLocationOnScreen(dragLayerBounds);
- // Animate the app icon to the center of the window bounds in screen coordinates.
- float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
- float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
+ final boolean hasSplashScreen;
+ if (supportsSSplashScreen()) {
+ int taskId = getOpeningTaskId(appTargets);
+ int type = mTypeForTaskId.getOrDefault(taskId, STARTING_WINDOW_TYPE_NONE);
+ mTypeForTaskId.remove(taskId);
+ hasSplashScreen = type == STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+ } else {
+ hasSplashScreen = false;
+ }
- float dX = centerX - bounds.centerX();
- float dY = centerY - bounds.centerY();
-
- boolean useUpwardAnimation = bounds.top > centerY
- || Math.abs(dY) < mLauncher.getDeviceProfile().cellHeightPx;
- final long xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
- : APP_LAUNCH_DOWN_DURATION;
- final long yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
- : APP_LAUNCH_DOWN_CURVED_DURATION;
- final long alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
- : APP_LAUNCH_ALPHA_DOWN_DURATION;
+ AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
+ windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1],
+ hasSplashScreen);
+ int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2);
+ int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2);
+ int right = (int) (left + prop.cropWidthStart);
+ int bottom = (int) (top + prop.cropHeightStart);
+ // Set the crop here so we can calculate the corner radius below.
+ crop.set(left, top, right, bottom);
RectF targetBounds = new RectF(windowTargetBounds);
- RectF iconBounds = new RectF();
- RectF temp = new RectF();
+ RectF floatingIconBounds = new RectF();
+ RectF tmpRectF = new RectF();
Point tmpPos = new Point();
AnimatorSet animatorSet = new AnimatorSet();
@@ -510,81 +548,82 @@
}
});
- float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION;
-
- final float startCrop;
- final float endCrop;
- if (mDeviceProfile.isVerticalBarLayout()) {
- startCrop = windowTargetBounds.height();
- endCrop = windowTargetBounds.width();
- } else {
- startCrop = windowTargetBounds.width();
- endCrop = windowTargetBounds.height();
- }
-
final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
- ? startCrop / 2f : 0f;
- final float windowRadius = mDeviceProfile.isMultiWindowMode
+ ? Math.max(crop.width(), crop.height()) / 2f
+ : 0f;
+ final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
+ final float finalShadowRadius = appTargetsAreTranslucent ? 0 : mMaxShadowRadius;
+
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
- FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
- FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
- FloatProp mScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
- EXAGGERATED_EASE);
- FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
- alphaDuration, LINEAR);
- FloatProp mCroppedSize = new FloatProp(startCrop, endCrop, 0, CROP_DURATION,
- EXAGGERATED_EASE);
- FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
+ FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
+ FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
+
+ FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
+ prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+ FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
+ APP_LAUNCH_ALPHA_START_DELAY, prop.alphaDuration, LINEAR);
+
+ FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
RADIUS_DURATION, EXAGGERATED_EASE);
+ FloatProp mShadowRadius = new FloatProp(0, finalShadowRadius, 0,
+ APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+
+ FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
+ 0, CROP_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
+ 0, CROP_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
+ CROP_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
+ CROP_DURATION, EXAGGERATED_EASE);
@Override
public void onUpdate(float percent) {
- // Calculate the size.
- float width = bounds.width() * mScale.value;
- float height = bounds.height() * mScale.value;
+ // Calculate the size of the scaled icon.
+ float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
+ float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
- // Animate the crop so that it starts off as a square.
- final int cropWidth;
- final int cropHeight;
- if (mDeviceProfile.isVerticalBarLayout()) {
- cropWidth = (int) mCroppedSize.value;
- cropHeight = windowTargetBounds.height();
- } else {
- cropWidth = windowTargetBounds.width();
- cropHeight = (int) mCroppedSize.value;
- }
- crop.set(0, 0, cropWidth, cropHeight);
+ int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);
+ int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);
+ int right = (int) (left + mCropRectWidth.value);
+ int bottom = (int) (top + mCropRectHeight.value);
+ crop.set(left, top, right, bottom);
- // Scale the size to match the crop.
- float scaleX = width / cropWidth;
- float scaleY = height / cropHeight;
+ final int windowCropWidth = crop.width();
+ final int windowCropHeight = crop.height();
+
+ // Scale the size of the icon to match the size of the window crop.
+ float scaleX = iconWidth / windowCropWidth;
+ float scaleY = iconHeight / windowCropHeight;
float scale = Math.min(1f, Math.max(scaleX, scaleY));
- float scaledCropWidth = cropWidth * scale;
- float scaledCropHeight = cropHeight * scale;
- float offsetX = (scaledCropWidth - width) / 2;
- float offsetY = (scaledCropHeight - height) / 2;
+ float scaledCropWidth = windowCropWidth * scale;
+ float scaledCropHeight = windowCropHeight * scale;
+ float offsetX = (scaledCropWidth - iconWidth) / 2;
+ float offsetY = (scaledCropHeight - iconHeight) / 2;
- // Calculate the window position.
- temp.set(bounds);
- temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
- temp.offset(mDx.value, mDy.value);
- Utilities.scaleRectFAboutCenter(temp, mScale.value);
- float windowTransX0 = temp.left - offsetX;
- float windowTransY0 = temp.top - offsetY;
+ // Calculate the window position to match the icon position.
+ tmpRectF.set(launcherIconBounds);
+ tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
+ tmpRectF.offset(mDx.value, mDy.value);
+ Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
+ float windowTransX0 = tmpRectF.left - offsetX;
+ float windowTransY0 = tmpRectF.top - offsetY;
+ if (hasSplashScreen) {
+ windowTransX0 -= crop.left * scale;
+ windowTransY0 -= crop.top * scale;
+ }
// Calculate the icon position.
- iconBounds.set(bounds);
- iconBounds.offset(mDx.value, mDy.value);
- Utilities.scaleRectFAboutCenter(iconBounds, mScale.value);
- iconBounds.left -= offsetX;
- iconBounds.top -= offsetY;
- iconBounds.right += offsetX;
- iconBounds.bottom += offsetY;
+ floatingIconBounds.set(launcherIconBounds);
+ floatingIconBounds.offset(mDx.value, mDy.value);
+ Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);
+ floatingIconBounds.left -= offsetX;
+ floatingIconBounds.top -= offsetY;
+ floatingIconBounds.right += offsetX;
+ floatingIconBounds.bottom += offsetY;
- float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
- float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
SurfaceParams[] params = new SurfaceParams[appTargets.length];
for (int i = appTargets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = appTargets[i];
@@ -594,17 +633,19 @@
matrix.setScale(scale, scale);
matrix.postTranslate(windowTransX0, windowTransY0);
- floatingView.update(iconBounds, mIconAlpha.value, percent, 0f,
+ floatingView.update(floatingIconBounds, mIconAlpha.value, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
builder.withMatrix(matrix)
.withWindowCrop(crop)
.withAlpha(1f - mIconAlpha.value)
- .withCornerRadius(mWindowRadius.value);
+ .withCornerRadius(mWindowRadius.value)
+ .withShadowRadius(mShadowRadius.value);
} else {
- tmpPos.set(target.position.x, target.position.y);
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);
}
matrix.setTranslate(tmpPos.x, tmpPos.y);
@@ -676,6 +717,32 @@
}
/**
+ * Registers remote animations used when closing apps to home screen.
+ */
+ @Override
+ public void registerRemoteTransitions() {
+ if (SEPARATE_RECENTS_ACTIVITY.get()) {
+ return;
+ }
+ if (hasControlRemoteAppTransitionPermission()) {
+ mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
+ mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
+ new WrappedLauncherAnimationRunner<>(mWallpaperOpenTransitionRunner,
+ false /* startAtFrontOfQueue */));
+ mLauncherOpenTransition.addHomeOpenCheck();
+ SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
+ }
+ }
+
+ @Override
+ public void onActivityDestroyed() {
+ super.onActivityDestroyed();
+ unregisterRemoteAnimations();
+ unregisterRemoteTransitions();
+ SystemUiProxy.INSTANCE.getNoCreate().setStartingWindowListener(null);
+ }
+
+ /**
* Unregisters all remote animations.
*/
@Override
@@ -694,6 +761,20 @@
}
}
+ @Override
+ public void unregisterRemoteTransitions() {
+ if (SEPARATE_RECENTS_ACTIVITY.get()) {
+ return;
+ }
+ if (hasControlRemoteAppTransitionPermission()) {
+ if (mLauncherOpenTransition == null) return;
+ SystemUiProxy.INSTANCE.getNoCreate().unregisterRemoteTransition(
+ mLauncherOpenTransition);
+ mLauncherOpenTransition = null;
+ mWallpaperOpenTransitionRunner = null;
+ }
+ }
+
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
}
@@ -746,11 +827,14 @@
int duration = CLOSING_TRANSITION_DURATION_MS;
float windowCornerRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
+ float startShadowRadius = areAllTargetsTranslucent(appTargets) ? 0 : mMaxShadowRadius;
closingAnimator.setDuration(duration);
closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
+ FloatProp mShadowRadius = new FloatProp(startShadowRadius, 0, 0, duration,
+ DEACCEL_1_7);
@Override
public void onUpdate(float percent) {
@@ -759,9 +843,10 @@
RemoteAnimationTargetCompat target = appTargets[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
- tmpPos.set(target.position.x, target.position.y);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
+ } else {
+ tmpPos.set(target.position.x, target.position.y);
}
if (target.mode == MODE_CLOSING) {
@@ -772,7 +857,8 @@
matrix.postTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
.withAlpha(mAlpha.value)
- .withCornerRadius(windowCornerRadius);
+ .withCornerRadius(windowCornerRadius)
+ .withShadowRadius(mShadowRadius.value);
} else {
matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix)
@@ -791,11 +877,38 @@
return closingAnimator;
}
+ private boolean supportsSSplashScreen() {
+ return hasControlRemoteAppTransitionPermission()
+ && Utilities.ATLEAST_S
+ && ENABLE_SHELL_STARTING_SURFACE;
+ }
+
private boolean hasControlRemoteAppTransitionPermission() {
return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
== PackageManager.PERMISSION_GRANTED;
}
+ private void addCujInstrumentation(Animator anim, int cuj) {
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ InteractionJankMonitorWrapper.begin(mDragLayer, cuj);
+ super.onAnimationStart(animation);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ InteractionJankMonitorWrapper.cancel(cuj);
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ InteractionJankMonitorWrapper.end(cuj);
+ }
+ });
+ }
+
/**
* Remote animation runner for animation from the app to Launcher, including recents.
*/
@@ -815,8 +928,10 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
if (mLauncher.isDestroyed()) {
AnimatorSet anim = new AnimatorSet();
@@ -829,7 +944,8 @@
// If launcher is not resumed, wait until new async-frame after resume
mLauncher.addOnResumeCallback(() ->
postAsyncCallback(mHandler, () ->
- onCreateAnimation(appTargets, wallpaperTargets, result)));
+ onCreateAnimation(transit, appTargets, wallpaperTargets,
+ nonAppTargets, result)));
return;
}
@@ -860,6 +976,8 @@
// is initialized.
if (launcherIsATargetWithMode(appTargets, MODE_OPENING)
|| mLauncher.isForceInvisible()) {
+ addCujInstrumentation(
+ anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
// Only register the content animation for cancellation when state changes
mLauncher.getStateManager().setCurrentAnimation(anim);
@@ -894,6 +1012,9 @@
*/
private class AppLaunchAnimationRunner implements WrappedAnimationRunnerImpl {
+ private static final String TRANSITION_LAUNCH_FROM_RECENTS = "transition:LaunchFromRecents";
+ private static final String TRANSITION_LAUNCH_FROM_ICON = "transition:LaunchFromIcon";
+
private final Handler mHandler;
private final View mV;
@@ -908,22 +1029,33 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
launcherIsATargetWithMode(appTargets, MODE_CLOSING);
- if (isLaunchingFromRecents(mV, appTargets)) {
+ final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
+ final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
+ if (launchingFromRecents) {
composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
launcherClosing);
+ } else if (launchingFromTaskbar) {
+ // TODO
} else {
composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
launcherClosing);
}
+ addCujInstrumentation(anim,
+ launchingFromRecents
+ ? InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_RECENTS
+ : InteractionJankMonitorWrapper.CUJ_APP_LAUNCH_FROM_ICON);
+
if (launcherClosing) {
anim.addListener(mForceInvisibleListener);
}
@@ -931,4 +1063,94 @@
result.setAnimation(anim, mLauncher);
}
}
+
+ /**
+ * Class that holds all the variables for the app open animation.
+ */
+ static class AnimOpenProperties {
+
+ public final int cropCenterXStart;
+ public final int cropCenterYStart;
+ public final int cropWidthStart;
+ public final int cropHeightStart;
+
+ public final int cropCenterXEnd;
+ public final int cropCenterYEnd;
+ public final int cropWidthEnd;
+ public final int cropHeightEnd;
+
+ public final float dX;
+ public final float dY;
+
+ public final long xDuration;
+ public final long yDuration;
+ public final long alphaDuration;
+
+ public final float initialAppIconScale;
+ public final float finalAppIconScale;
+
+ public final float iconAlphaStart;
+
+ AnimOpenProperties(Resources r, DeviceProfile dp, Rect windowTargetBounds,
+ RectF launcherIconBounds, View view, int dragLayerLeft, int dragLayerTop,
+ boolean hasSplashScreen) {
+ // Scale the app icon to take up the entire screen. This simplifies the math when
+ // animating the app window position / scale.
+ float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
+ float maxScaleX = smallestSize / launcherIconBounds.width();
+ float maxScaleY = smallestSize / launcherIconBounds.height();
+ float iconStartScale = 1f;
+ if (view instanceof BubbleTextView && !(view.getParent() instanceof DeepShortcutView)) {
+ Drawable dr = ((BubbleTextView) view).getIcon();
+ if (dr instanceof FastBitmapDrawable) {
+ iconStartScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+ }
+ }
+
+ initialAppIconScale = iconStartScale;
+ finalAppIconScale = Math.max(maxScaleX, maxScaleY);
+
+ // Animate the app icon to the center of the window bounds in screen coordinates.
+ float centerX = windowTargetBounds.centerX() - dragLayerLeft;
+ float centerY = windowTargetBounds.centerY() - dragLayerTop;
+
+ dX = centerX - launcherIconBounds.centerX();
+ dY = centerY - launcherIconBounds.centerY();
+
+ boolean useUpwardAnimation = launcherIconBounds.top > centerY
+ || Math.abs(dY) < dp.cellHeightPx;
+ xDuration = useUpwardAnimation ? APP_LAUNCH_CURVED_DURATION
+ : APP_LAUNCH_DOWN_DURATION;
+ yDuration = useUpwardAnimation ? APP_LAUNCH_DURATION
+ : APP_LAUNCH_DOWN_CURVED_DURATION;
+ alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
+ : APP_LAUNCH_ALPHA_DOWN_DURATION;
+
+ if (hasSplashScreen) {
+ iconAlphaStart = 0;
+
+ // TOOD: Share value from shell when available.
+ final float windowIconSize = Utilities.pxFromSp(108, r.getDisplayMetrics());
+
+ cropCenterXStart = windowTargetBounds.centerX();
+ cropCenterYStart = windowTargetBounds.centerY();
+
+ cropWidthStart = (int) windowIconSize;
+ cropHeightStart = (int) windowIconSize;
+ } else {
+ iconAlphaStart = 1;
+
+ cropWidthStart = cropHeightStart =
+ Math.min(windowTargetBounds.width(), windowTargetBounds.height());
+ cropCenterXStart = cropCenterYStart =
+ Math.min(windowTargetBounds.centerX(), windowTargetBounds.centerY());
+ }
+
+ cropWidthEnd = windowTargetBounds.width();
+ cropHeightEnd = windowTargetBounds.height();
+
+ cropCenterXEnd = windowTargetBounds.centerX();
+ cropCenterYEnd = windowTargetBounds.centerY();
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
index da2aee4..03cc28e 100644
--- a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -26,7 +26,9 @@
*/
public interface WrappedAnimationRunnerImpl {
Handler getHandler();
- void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+ void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
LauncherAnimationRunner.AnimationResult result);
}
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
index 1753b62..1e1631b 100644
--- a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -46,11 +46,15 @@
}
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ AnimationResult result) {
R animationRunnerImpl = mImpl.get();
if (animationRunnerImpl != null) {
- animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
+ animationRunnerImpl.onCreateAnimation(transit, appTargets, wallpaperTargets,
+ nonAppTargets, result);
}
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java b/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java
similarity index 91%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
rename to quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java
index 8477b10..98bf483 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AllAppsTipView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AllAppsTipView.java
@@ -19,7 +19,6 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
import static com.android.launcher3.AbstractFloatingView.TYPE_ON_BOARD_POPUP;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APPS_PREDICTION_TIPS;
import android.os.UserManager;
@@ -31,7 +30,6 @@
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.views.ArrowTipView;
-import com.android.systemui.shared.system.LauncherEventUtil;
/**
* ArrowTip helper aligned just above prediction apps, shown to users that enter all apps for the
@@ -57,8 +55,7 @@
floatingHeaderView.findFixedRowByType(PredictionRowView.class).getLocationOnScreen(coords);
ArrowTipView arrowTipView = new ArrowTipView(launcher).setOnClosedCallback(() -> {
launcher.getSharedPrefs().edit().putBoolean(ALL_APPS_TIP_SEEN, true).apply();
- launcher.getUserEventDispatcher().logActionTip(LauncherEventUtil.DISMISS,
- ALL_APPS_PREDICTION_TIPS);
+ // TODO: add log to WW
});
arrowTipView.show(launcher.getString(R.string.all_apps_prediction_tip), coords[1]);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
similarity index 97%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
rename to quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 914d9e9..66b1a86 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -90,7 +90,8 @@
mLauncher = Launcher.getLauncher(context);
boolean isMainColorDark = Themes.getAttrBoolean(context, R.attr.isMainColorDark);
- mPaint.setStrokeWidth(getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
+ mPaint.setStrokeWidth(
+ getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
mStrokeColor = ContextCompat.getColor(context, isMainColorDark
? R.color.all_apps_prediction_row_separator_dark
@@ -134,7 +135,7 @@
if (row == this) {
break;
} else if (row.shouldDraw()) {
- sectionCount ++;
+ sectionCount++;
}
}
@@ -303,4 +304,9 @@
public Class<AppsDividerView> getTypeClass() {
return AppsDividerView.class;
}
+
+ @Override
+ public View getFocusedChild() {
+ return null;
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java b/quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
rename to quickstep/src/com/android/launcher3/appprediction/InstantAppItemInfo.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
similarity index 72%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
rename to quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 44691d3..83234e3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -16,11 +16,8 @@
package com.android.launcher3.appprediction;
-import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
import android.annotation.TargetApi;
import android.content.Context;
@@ -30,7 +27,6 @@
import android.os.Build;
import android.util.AttributeSet;
import android.util.IntProperty;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.animation.Interpolator;
@@ -44,10 +40,7 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
@@ -55,27 +48,20 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Themes;
import com.android.quickstep.AnimatedFloat;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
@TargetApi(Build.VERSION_CODES.P)
public class PredictionRowView extends LinearLayout implements
- LogContainerProvider, OnDeviceProfileChangeListener, FloatingHeaderRow {
-
- private static final String TAG = "PredictionRowView";
+ OnDeviceProfileChangeListener, FloatingHeaderRow {
private static final IntProperty<PredictionRowView> TEXT_ALPHA =
new IntProperty<PredictionRowView>("textAlpha") {
@@ -93,20 +79,15 @@
private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
(t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
- private static final OnClickListener PREDICTION_CLICK_LISTENER =
- ItemClickHandler.getInstance(AppLaunchTracker.CONTAINER_PREDICTIONS);
-
private final Launcher mLauncher;
- private final PredictionUiStateManager mPredictionUiStateManager;
private int mNumPredictedAppsPerRow;
- // The set of predicted app component names
- private final List<ComponentKeyMapper> mPredictedAppComponents = new ArrayList<>();
- // The set of predicted apps resolved from the component names and the current set of apps
- private final ArrayList<ItemInfoWithIcon> mPredictedApps = new ArrayList<>();
// Helper to drawing the focus indicator.
private final FocusIndicatorHelper mFocusHelper;
+ // The set of predicted apps resolved from the component names and the current set of apps
+ private final List<WorkspaceItemInfo> mPredictedApps = new ArrayList<>();
+
private final int mIconTextColor;
private final int mIconFullTextAlpha;
private int mIconLastSetTextAlpha;
@@ -124,6 +105,9 @@
private boolean mPredictionsEnabled = false;
+ @Nullable
+ private List<ItemInfo> mPendingPredictedItems;
+
public PredictionRowView(@NonNull Context context) {
this(context, null);
}
@@ -138,8 +122,6 @@
mLauncher = Launcher.getLauncher(context);
mLauncher.addOnDeviceProfileChangeListener(this);
- mPredictionUiStateManager = PredictionUiStateManager.INSTANCE.get(context);
-
mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary);
mIconFullTextAlpha = Color.alpha(mIconTextColor);
mIconCurrentTextAlpha = mIconFullTextAlpha;
@@ -150,24 +132,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
-
- mPredictionUiStateManager.setTargetAppsView(mLauncher.getAppsView());
- getAppsStore().registerIconContainer(this);
AllAppsTipView.scheduleShowIfNeeded(mLauncher);
}
- private AllAppsStore getAppsStore() {
- return mLauncher.getAppsView().getAppsStore();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- mPredictionUiStateManager.setTargetAppsView(null);
- getAppsStore().unregisterIconContainer(this);
- }
-
public void setup(FloatingHeaderView parent, FloatingHeaderRow[] rows, boolean tabsHidden) {
mParent = parent;
}
@@ -205,11 +172,16 @@
return mPredictionsEnabled;
}
+ @Override
+ public boolean isVisible() {
+ return getVisibility() == VISIBLE;
+ }
+
/**
* Returns the predicted apps.
*/
public List<ItemInfoWithIcon> getPredictedApps() {
- return mPredictedApps;
+ return new ArrayList<>(mPredictedApps);
}
/**
@@ -221,12 +193,25 @@
* If the number of predicted apps is the same as the previous list of predicted apps,
* we can optimize by swapping them in place.
*/
- public void setPredictedApps(List<ComponentKeyMapper> apps) {
- mPredictedAppComponents.clear();
- mPredictedAppComponents.addAll(apps);
+ public void setPredictedApps(List<ItemInfo> items) {
+ if (!FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
+ && !mLauncher.isWorkspaceLoading()
+ && isShown()
+ && getWindowVisibility() == View.VISIBLE) {
+ mPendingPredictedItems = items;
+ return;
+ }
+ applyPredictedApps(items);
+ }
+
+ private void applyPredictedApps(List<ItemInfo> items) {
+ mPendingPredictedItems = null;
mPredictedApps.clear();
- mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
+ items.stream()
+ .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
+ .map(itemInfo -> (WorkspaceItemInfo) itemInfo)
+ .forEach(mPredictedApps::add);
applyPredictionApps();
}
@@ -246,7 +231,7 @@
while (getChildCount() < mNumPredictedAppsPerRow) {
BubbleTextView icon = (BubbleTextView) inflater.inflate(
R.layout.all_apps_icon, this, false);
- icon.setOnClickListener(PREDICTION_CLICK_LISTENER);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mFocusHelper);
@@ -268,11 +253,7 @@
icon.reset();
if (predictionCount > i) {
icon.setVisibility(View.VISIBLE);
- if (mPredictedApps.get(i) instanceof AppInfo) {
- icon.applyFromApplicationInfo((AppInfo) mPredictedApps.get(i));
- } else if (mPredictedApps.get(i) instanceof WorkspaceItemInfo) {
- icon.applyFromWorkspaceItem((WorkspaceItemInfo) mPredictedApps.get(i));
- }
+ icon.applyFromWorkspaceItem(mPredictedApps.get(i));
icon.setTextColor(iconColor);
} else {
icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
@@ -288,56 +269,6 @@
mParent.onHeightUpdated();
}
- private List<ItemInfoWithIcon> processPredictedAppComponents(
- List<ComponentKeyMapper> components) {
- if (getAppsStore().getApps().length == 0) {
- // Apps have not been bound yet.
- return Collections.emptyList();
- }
-
- List<ItemInfoWithIcon> predictedApps = new ArrayList<>();
- for (ComponentKeyMapper mapper : components) {
- ItemInfoWithIcon info = mapper.getApp(getAppsStore());
- if (info != null) {
- ItemInfoWithIcon predictedApp = info.clone();
- predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
- predictedApps.add(predictedApp);
- } else {
- if (FeatureFlags.IS_STUDIO_BUILD) {
- Log.e(TAG, "Predicted app not found: " + mapper);
- }
- }
- // Stop at the number of predicted apps
- if (predictedApps.size() == mNumPredictedAppsPerRow) {
- break;
- }
- }
- return predictedApps;
- }
-
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
- ArrayList<LauncherLogProto.Target> parents) {
- for (int i = 0; i < mPredictedApps.size(); i++) {
- ItemInfoWithIcon appInfo = mPredictedApps.get(i);
- if (appInfo == childInfo) {
- child.predictedRank = i;
- break;
- }
- }
- parents.add(newContainerTarget(LauncherLogProto.ContainerType.PREDICTION));
-
- // include where the prediction is coming this used to be Launcher#modifyUserEvent
- LauncherLogProto.Target parent = newTarget(LauncherLogProto.Target.Type.CONTAINER);
- LauncherState state = mLauncher.getStateManager().getState();
- if (state == LauncherState.ALL_APPS) {
- parent.containerType = LauncherLogProto.ContainerType.ALLAPPS;
- } else if (state == OVERVIEW) {
- parent.containerType = LauncherLogProto.ContainerType.TASKSWITCHER;
- }
- parents.add(parent);
- }
-
public void setTextAlpha(int textAlpha) {
mIconLastSetTextAlpha = textAlpha;
if (getAlpha() < 1 && textAlpha > 0) {
@@ -409,4 +340,18 @@
public Class<PredictionRowView> getTypeClass() {
return PredictionRowView.class;
}
+
+ @Override
+ public View getFocusedChild() {
+ return getChildAt(0);
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+
+ if (mPendingPredictedItems != null && !isVisible) {
+ applyPredictedApps(mPendingPredictedItems);
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
index c968de9..3a1a2f7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
@@ -29,7 +29,6 @@
*/
public class HotseatEduActivity extends Activity {
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -49,7 +48,7 @@
@Override
public boolean init(BaseActivity activity, boolean alreadyOnHome) {
QuickstepLauncher launcher = (QuickstepLauncher) activity;
- if (launcher != null && launcher.getHotseatPredictionController() != null) {
+ if (launcher != null) {
launcher.getHotseatPredictionController().showEdu();
}
return false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index 4f95254..4451e7a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -47,35 +47,27 @@
*/
public class HotseatEduController {
- public static final String HOTSEAT_EDU_ACTION =
- "com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
public static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
private final Launcher mLauncher;
private final Hotseat mHotseat;
- private HotseatRestoreHelper mRestoreHelper;
private List<WorkspaceItemInfo> mPredictedApps;
private HotseatEduDialog mActiveDialog;
private ArrayList<ItemInfo> mNewItems = new ArrayList<>();
private IntArray mNewScreens = null;
- private Runnable mOnOnboardingComplete;
- HotseatEduController(Launcher launcher, HotseatRestoreHelper restoreHelper, Runnable runnable) {
+ HotseatEduController(Launcher launcher) {
mLauncher = launcher;
mHotseat = launcher.getHotseat();
- mRestoreHelper = restoreHelper;
- mOnOnboardingComplete = runnable;
}
/**
* Checks what type of migration should be used and migrates hotseat
*/
void migrate() {
- if (mRestoreHelper != null) {
- mRestoreHelper.createBackup();
- }
+ HotseatRestoreHelper.createBackup(mLauncher);
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
migrateToFolder();
} else {
@@ -227,7 +219,7 @@
}
void finishOnboarding() {
- mOnOnboardingComplete.run();
+ mLauncher.getModel().onWorkspaceUiChanged();
}
void showDimissTip() {
@@ -284,4 +276,3 @@
return new Intent(SETTINGS_ACTION).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
}
-
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
similarity index 96%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 4b8e434..c3677ea 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -40,7 +40,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.AbstractSlideInView;
import java.util.List;
@@ -122,16 +121,6 @@
}
@Override
- public void logActionCommand(int command) {
- // Since this is on-boarding popup, it is not a user controlled action.
- }
-
- @Override
- public int getLogContainerType() {
- return LauncherLogProto.ContainerType.TIP;
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_ON_BOARD_POPUP) != 0;
}
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
new file mode 100644
index 0000000..f297343
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -0,0 +1,517 @@
+/*
+ * 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.hybridhotseat;
+
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.ComponentName;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.PredictedHotseatContainer;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+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.ItemLongClickListener;
+import com.android.launcher3.uioverrides.PredictedAppIcon;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.launcher3.util.OnboardingPrefs;
+import com.android.launcher3.views.ArrowTipView;
+import com.android.launcher3.views.Snackbar;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Provides prediction ability for the hotseat. Fills gaps in hotseat with predicted items, allows
+ * pinning of predicted apps and manages replacement of predicted apps with user drag.
+ */
+public class HotseatPredictionController implements DragController.DragListener,
+ SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
+ DragSource, ViewGroup.OnHierarchyChangeListener {
+
+ private static final int FLAG_UPDATE_PAUSED = 1 << 0;
+ private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
+ private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
+ private static final int FLAG_REMOVING_PREDICTED_ICON = 1 << 3;
+
+ private int mHotSeatItemsCount;
+
+ private QuickstepLauncher mLauncher;
+ private final Hotseat mHotseat;
+ private final Runnable mUpdateFillIfNotLoading = this::updateFillIfNotLoading;
+
+ private List<ItemInfo> mPredictedItems = Collections.emptyList();
+
+ private AnimatorSet mIconRemoveAnimators;
+ private int mPauseFlags = 0;
+
+ private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
+
+ private final View.OnLongClickListener mPredictionLongClickListener = v -> {
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
+ if (mLauncher.getWorkspace().isSwitchingState()) return false;
+ if (!mLauncher.getOnboardingPrefs().getBoolean(
+ OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN)) {
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(getSettingsIntent()));
+ mLauncher.getOnboardingPrefs().markChecked(OnboardingPrefs.HOTSEAT_LONGPRESS_TIP_SEEN);
+ mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+ return true;
+ }
+
+ // Start the drag
+ // Use a new itemInfo so that the original predicted item is stable
+ WorkspaceItemInfo dragItem = new WorkspaceItemInfo((WorkspaceItemInfo) v.getTag());
+ v.setVisibility(View.INVISIBLE);
+ mLauncher.getWorkspace().beginDragShared(
+ v, null, this, dragItem, new DragPreviewProvider(v),
+ mLauncher.getDefaultWorkspaceDragOptions());
+ return true;
+ };
+
+ public HotseatPredictionController(QuickstepLauncher launcher) {
+ mLauncher = launcher;
+ mHotseat = launcher.getHotseat();
+ mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+ mLauncher.getDragController().addDragListener(this);
+
+ launcher.getDeviceProfile().inv.addOnChangeListener(this);
+ mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ onHotseatHierarchyChanged();
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ onHotseatHierarchyChanged();
+ }
+
+ private void onHotseatHierarchyChanged() {
+ if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+ // Post update after a single frame to avoid layout within layout
+ MAIN_EXECUTOR.getHandler().removeCallbacks(mUpdateFillIfNotLoading);
+ MAIN_EXECUTOR.getHandler().post(mUpdateFillIfNotLoading);
+ }
+ }
+
+ private void updateFillIfNotLoading() {
+ if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+ fillGapsWithPrediction(true);
+ }
+ }
+
+ /**
+ * Shows appropriate hotseat education based on prediction enabled and migration states.
+ */
+ public void showEdu() {
+ mLauncher.getStateManager().goToState(NORMAL, true, () -> {
+ if (mPredictedItems.isEmpty()) {
+ // launcher has empty predictions set
+ Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(getSettingsIntent()));
+ } else if (getPredictedIcons().size() >= (mHotSeatItemsCount + 1) / 2) {
+ showDiscoveryTip();
+ } else {
+ HotseatEduController eduController = new HotseatEduController(mLauncher);
+ eduController.setPredictedApps(mPredictedItems.stream()
+ .map(i -> (WorkspaceItemInfo) i)
+ .collect(Collectors.toList()));
+ eduController.showEdu();
+ }
+ });
+ }
+
+ /**
+ * Shows educational tip for hotseat if user does not go through Tips app.
+ */
+ private void showDiscoveryTip() {
+ if (getPredictedIcons().isEmpty()) {
+ new ArrowTipView(mLauncher).show(
+ mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
+ } else {
+ Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
+ R.string.hotseat_prediction_settings, null,
+ () -> mLauncher.startActivity(getSettingsIntent()));
+ }
+ }
+
+ /**
+ * Returns if hotseat client has predictions
+ */
+ public boolean hasPredictions() {
+ return !mPredictedItems.isEmpty();
+ }
+
+ private void fillGapsWithPrediction() {
+ fillGapsWithPrediction(false);
+ }
+
+ private void fillGapsWithPrediction(boolean animate) {
+ if (mPauseFlags != 0) {
+ return;
+ }
+
+ int predictionIndex = 0;
+ ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
+ // make sure predicted icon removal and filling predictions don't step on each other
+ if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
+ mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ fillGapsWithPrediction(animate);
+ mIconRemoveAnimators.removeListener(this);
+ }
+ });
+ return;
+ }
+
+ mPauseFlags |= FLAG_FILL_IN_PROGRESS;
+ for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
+ View child = mHotseat.getChildAt(
+ mHotseat.getCellXFromOrder(rank),
+ mHotseat.getCellYFromOrder(rank));
+
+ if (child != null && !isPredictedIcon(child)) {
+ continue;
+ }
+ if (mPredictedItems.size() <= predictionIndex) {
+ // Remove predicted apps from the past
+ if (isPredictedIcon(child)) {
+ mHotseat.removeView(child);
+ }
+ continue;
+ }
+ WorkspaceItemInfo predictedItem =
+ (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
+ if (isPredictedIcon(child) && child.isEnabled()) {
+ PredictedAppIcon icon = (PredictedAppIcon) child;
+ icon.applyFromWorkspaceItem(predictedItem);
+ icon.finishBinding(mPredictionLongClickListener);
+ } else {
+ newItems.add(predictedItem);
+ }
+ preparePredictionInfo(predictedItem, rank);
+ }
+ bindItems(newItems, animate);
+
+ mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
+ }
+
+ private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
+ AnimatorSet animationSet = new AnimatorSet();
+ for (WorkspaceItemInfo item : itemsToAdd) {
+ PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
+ mLauncher.getWorkspace().addInScreenFromBind(icon, item);
+ icon.finishBinding(mPredictionLongClickListener);
+ if (animate) {
+ animationSet.play(ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0.2f, 1));
+ }
+ }
+ if (animate) {
+ animationSet.addListener(AnimationSuccessListener
+ .forRunnable(this::removeOutlineDrawings));
+ animationSet.start();
+ } else {
+ removeOutlineDrawings();
+ }
+
+ if (mLauncher.getTaskbarController() != null) {
+ mLauncher.getTaskbarController().onHotseatUpdated();
+ }
+ }
+
+ private void removeOutlineDrawings() {
+ if (mOutlineDrawings.isEmpty()) return;
+ for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+ mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+ }
+ mHotseat.invalidate();
+ mOutlineDrawings.clear();
+ }
+
+
+ /**
+ * Unregisters callbacks and frees resources
+ */
+ public void destroy() {
+ mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
+ }
+
+ /**
+ * start and pauses predicted apps update on the hotseat
+ */
+ public void setPauseUIUpdate(boolean paused) {
+ mPauseFlags = paused
+ ? (mPauseFlags | FLAG_UPDATE_PAUSED)
+ : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
+ if (!paused) {
+ fillGapsWithPrediction();
+ }
+ }
+
+ /**
+ * Sets or updates the predicted items
+ */
+ public void setPredictedItems(FixedContainerItems items) {
+ boolean shouldIgnoreVisibility = FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
+ || mLauncher.isWorkspaceLoading()
+ || mPredictedItems.equals(items.items)
+ || mHotseat.getShortcutsAndWidgets().getChildCount() < mHotSeatItemsCount;
+ if (!shouldIgnoreVisibility
+ && mHotseat.isShown()
+ && mHotseat.getWindowVisibility() == View.VISIBLE) {
+ mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
+ if (isVisible) {
+ return;
+ }
+ mHotseat.setOnVisibilityAggregatedCallback(null);
+
+ applyPredictedItems(items);
+ });
+ } else {
+ mHotseat.setOnVisibilityAggregatedCallback(null);
+
+ applyPredictedItems(items);
+ }
+ }
+
+ /**
+ * Sets or updates the predicted items only once the hotseat becomes hidden to the user
+ */
+ private void applyPredictedItems(FixedContainerItems items) {
+ mPredictedItems = items.items;
+ if (mPredictedItems.isEmpty()) {
+ HotseatRestoreHelper.restoreBackup(mLauncher);
+ }
+ fillGapsWithPrediction();
+ }
+
+ /**
+ * Pins a predicted app icon into place.
+ */
+ public void pinPrediction(ItemInfo info) {
+ PredictedAppIcon icon = (PredictedAppIcon) mHotseat.getChildAt(
+ mHotseat.getCellXFromOrder(info.rank),
+ mHotseat.getCellYFromOrder(info.rank));
+ if (icon == null) {
+ return;
+ }
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo((WorkspaceItemInfo) info);
+ mLauncher.getModelWriter().addItemToDatabase(workspaceItemInfo,
+ LauncherSettings.Favorites.CONTAINER_HOTSEAT, workspaceItemInfo.screenId,
+ workspaceItemInfo.cellX, workspaceItemInfo.cellY);
+ ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
+ icon.pin(workspaceItemInfo);
+ mLauncher.getStatsLogManager().logger()
+ .withItemInfo(workspaceItemInfo)
+ .log(LAUNCHER_HOTSEAT_PREDICTION_PINNED);
+ }
+
+ private List<PredictedAppIcon> getPredictedIcons() {
+ List<PredictedAppIcon> icons = new ArrayList<>();
+ ViewGroup vg = mHotseat.getShortcutsAndWidgets();
+ for (int i = 0; i < vg.getChildCount(); i++) {
+ View child = vg.getChildAt(i);
+ if (isPredictedIcon(child)) {
+ icons.add((PredictedAppIcon) child);
+ }
+ }
+ return icons;
+ }
+
+ private void removePredictedApps(List<PredictedAppIcon.PredictedIconOutlineDrawing> outlines,
+ DropTarget.DragObject dragObject) {
+ if (mIconRemoveAnimators != null) {
+ mIconRemoveAnimators.end();
+ }
+ mIconRemoveAnimators = new AnimatorSet();
+ removeOutlineDrawings();
+ for (PredictedAppIcon icon : getPredictedIcons()) {
+ if (!icon.isEnabled()) {
+ continue;
+ }
+ if (dragObject.dragSource == this && icon.equals(dragObject.originalView)) {
+ removeIconWithoutNotify(icon);
+ continue;
+ }
+ int rank = ((WorkspaceItemInfo) icon.getTag()).rank;
+ outlines.add(new PredictedAppIcon.PredictedIconOutlineDrawing(
+ mHotseat.getCellXFromOrder(rank), mHotseat.getCellYFromOrder(rank), icon));
+ icon.setEnabled(false);
+ ObjectAnimator animator = ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0);
+ animator.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (icon.getParent() != null) {
+ removeIconWithoutNotify(icon);
+ }
+ }
+ });
+ mIconRemoveAnimators.play(animator);
+ }
+ mIconRemoveAnimators.start();
+ }
+
+ /**
+ * Removes icon while suppressing any extra tasks performed on view-hierarchy changes.
+ * This avoids recursive/redundant updates as the control updates the UI anyway after
+ * it's animation.
+ */
+ private void removeIconWithoutNotify(PredictedAppIcon icon) {
+ mPauseFlags |= FLAG_REMOVING_PREDICTED_ICON;
+ mHotseat.removeView(icon);
+ mPauseFlags &= ~FLAG_REMOVING_PREDICTED_ICON;
+ }
+
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ removePredictedApps(mOutlineDrawings, dragObject);
+ if (mOutlineDrawings.isEmpty()) return;
+ for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+ mHotseat.addDelegatedCellDrawing(outlineDrawing);
+ }
+ mPauseFlags |= FLAG_DRAG_IN_PROGRESS;
+ mHotseat.invalidate();
+ }
+
+ @Override
+ public void onDragEnd() {
+ mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS;
+ fillGapsWithPrediction(true);
+ }
+
+ @Nullable
+ @Override
+ public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
+ ItemInfo itemInfo) {
+ if (itemInfo.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ return null;
+ }
+ return new PinPrediction(activity, itemInfo);
+ }
+
+ private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
+ itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+ itemInfo.rank = rank;
+ itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
+ itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
+ itemInfo.screenId = rank;
+ }
+
+ @Override
+ public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+ this.mHotSeatItemsCount = profile.numHotseatIcons;
+ }
+
+ @Override
+ public void onDropCompleted(View target, DropTarget.DragObject d, boolean success) {
+ //Does nothing
+ }
+
+ /**
+ * Logs rank info based on current list of predicted items
+ */
+ public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
+ ComponentName targetCN = itemInfo.getTargetComponent();
+ if (targetCN == null) {
+ return;
+ }
+ int rank = -1;
+ for (int i = mPredictedItems.size() - 1; i >= 0; i--) {
+ ItemInfo info = mPredictedItems.get(i);
+ if (targetCN.equals(info.getTargetComponent()) && itemInfo.user.equals(info.user)) {
+ rank = i;
+ break;
+ }
+ }
+ if (rank < 0) {
+ return;
+ }
+
+ int cardinality = 0;
+ for (PredictedAppIcon icon : getPredictedIcons()) {
+ ItemInfo info = (ItemInfo) icon.getTag();
+ cardinality |= 1 << info.screenId;
+ }
+
+ PredictedHotseatContainer.Builder containerBuilder = PredictedHotseatContainer.newBuilder();
+ containerBuilder.setCardinality(cardinality);
+ if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ containerBuilder.setIndex(rank);
+ }
+ mLauncher.getStatsLogManager().logger()
+ .withInstanceId(instanceId)
+ .withRank(rank)
+ .withContainerInfo(ContainerInfo.newBuilder()
+ .setPredictedHotseatContainer(containerBuilder)
+ .build())
+ .log(LAUNCHER_HOTSEAT_RANKED);
+ }
+
+ private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
+
+ private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {
+ super(R.drawable.ic_pin, R.string.pin_prediction, target,
+ itemInfo);
+ }
+
+ @Override
+ public void onClick(View view) {
+ dismissTaskMenuView(mTarget);
+ pinPrediction(mItemInfo);
+ }
+ }
+
+ private static boolean isPredictedIcon(View view) {
+ return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
+ && ((WorkspaceItemInfo) view.getTag()).container
+ == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
similarity index 66%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
index 5a038d2..080633a 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionModel.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3.hybridhotseat;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
@@ -24,13 +24,10 @@
import android.content.Context;
import android.os.Bundle;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
-import com.android.launcher3.model.AllAppsList;
-import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.PredictionModel;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -38,55 +35,47 @@
import java.util.ArrayList;
import java.util.Locale;
-import java.util.function.Consumer;
/**
* Model helper for app predictions in workspace
*/
-public class HotseatPredictionModel extends PredictionModel {
+public class HotseatPredictionModel {
private static final String APP_LOCATION_HOTSEAT = "hotseat";
private static final String APP_LOCATION_WORKSPACE = "workspace";
private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
-
- public HotseatPredictionModel(Context context) { }
-
/**
- * Creates and returns bundle using workspace items and cached items
+ * Creates and returns bundle using workspace items
*/
- public void createBundle(Consumer<Bundle> cb) {
- LauncherAppState appState = LauncherAppState.getInstance(mContext);
- appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
- @Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- Bundle bundle = new Bundle();
- ArrayList<AppTargetEvent> events = new ArrayList<>();
- ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
- workspaceItems.addAll(dataModel.appWidgets);
- for (ItemInfo item : workspaceItems) {
- AppTarget target = getAppTargetFromInfo(item);
- if (target != null && !isTrackedForPrediction(item)) continue;
- events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
- }
- ArrayList<AppTarget> currentTargets = new ArrayList<>();
- for (ItemInfo itemInfo : dataModel.cachedPredictedItems) {
- AppTarget target = getAppTargetFromInfo(itemInfo);
- if (target != null) currentTargets.add(target);
- }
- bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
- bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
- MAIN_EXECUTOR.execute(() -> cb.accept(bundle));
+ public static Bundle convertDataModelToAppTargetBundle(Context context, BgDataModel dataModel) {
+ Bundle bundle = new Bundle();
+ ArrayList<AppTargetEvent> events = new ArrayList<>();
+ ArrayList<ItemInfo> workspaceItems = dataModel.getAllWorkspaceItems();
+ for (ItemInfo item : workspaceItems) {
+ AppTarget target = getAppTargetFromInfo(context, item);
+ if (target != null && !isTrackedForPrediction(item)) continue;
+ events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
+ }
+ ArrayList<AppTarget> currentTargets = new ArrayList<>();
+ FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
+ if (hotseatItems != null) {
+ for (ItemInfo itemInfo : hotseatItems.items) {
+ AppTarget target = getAppTargetFromInfo(context, itemInfo);
+ if (target != null) currentTargets.add(target);
}
- });
+ }
+ bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
+ bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
+ return bundle;
}
/**
* Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
* if item is not supported prediction
*/
- public AppTarget getAppTargetFromInfo(ItemInfo info) {
+ public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) {
if (info == null) return null;
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
&& info instanceof LauncherAppWidgetInfo
@@ -107,17 +96,17 @@
shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
- mContext.getPackageName(), info.user).build();
+ context.getPackageName(), info.user).build();
}
return null;
}
-
/**
* Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
* location using {@link ItemInfo}
*/
- public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
+ public static AppTargetEvent wrapAppTargetWithLocation(
+ AppTarget target, int action, ItemInfo info) {
String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
similarity index 81%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
rename to quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
index 9e7c9fb..90f762e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatRestoreHelper.java
@@ -19,8 +19,10 @@
import static com.android.launcher3.provider.LauncherDbUtils.tableExists;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.content.Context;
+
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.GridBackupTable;
import com.android.launcher3.provider.LauncherDbUtils;
@@ -29,29 +31,24 @@
* A helper class to manage migration revert restoration for hybrid hotseat
*/
public class HotseatRestoreHelper {
- private final Launcher mLauncher;
-
- HotseatRestoreHelper(Launcher context) {
- mLauncher = context;
- }
/**
* Creates a snapshot backup of Favorite table for future restoration use.
*/
- public void createBackup() {
+ public static void createBackup(Context context) {
MODEL_EXECUTOR.execute(() -> {
try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
LauncherSettings.Settings.call(
- mLauncher.getContentResolver(),
+ context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
.getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
- InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
- GridBackupTable backupTable = new GridBackupTable(mLauncher,
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ GridBackupTable backupTable = new GridBackupTable(context,
transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
idp.numRows);
backupTable.createCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE);
transaction.commit();
- LauncherSettings.Settings.call(mLauncher.getContentResolver(),
+ LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_REFRESH_HOTSEAT_RESTORE_TABLE);
}
});
@@ -60,23 +57,23 @@
/**
* Finds and restores a previously saved snapshow of Favorites table
*/
- public void restoreBackup() {
+ public static void restoreBackup(Context context) {
MODEL_EXECUTOR.execute(() -> {
try (LauncherDbUtils.SQLiteTransaction transaction = (LauncherDbUtils.SQLiteTransaction)
LauncherSettings.Settings.call(
- mLauncher.getContentResolver(),
+ context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_TRANSACTION)
.getBinder(LauncherSettings.Settings.EXTRA_VALUE)) {
if (!tableExists(transaction.getDb(), HYBRID_HOTSEAT_BACKUP_TABLE)) {
return;
}
- InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
- GridBackupTable backupTable = new GridBackupTable(mLauncher,
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ GridBackupTable backupTable = new GridBackupTable(context,
transaction.getDb(), idp.numHotseatIcons, idp.numColumns,
idp.numRows);
backupTable.restoreFromCustomBackupTable(HYBRID_HOTSEAT_BACKUP_TABLE, true);
transaction.commit();
- mLauncher.getModel().forceReload();
+ LauncherAppState.getInstance(context).getModel().forceReload();
}
});
}
diff --git a/quickstep/src/com/android/launcher3/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
new file mode 100644
index 0000000..9944270
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -0,0 +1,298 @@
+/*
+ * 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.model;
+
+import static android.app.prediction.AppTargetEvent.ACTION_DISMISS;
+import static android.app.prediction.AppTargetEvent.ACTION_LAUNCH;
+import static android.app.prediction.AppTargetEvent.ACTION_PIN;
+import static android.app.prediction.AppTargetEvent.ACTION_UNPIN;
+
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED;
+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.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.annotation.TargetApi;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+import android.app.prediction.AppTargetId;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import androidx.annotation.AnyThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FolderContainer;
+import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
+import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
+import com.android.launcher3.logging.StatsLogManager.EventEnum;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.pm.UserCache;
+import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
+
+import java.util.Locale;
+import java.util.function.ObjIntConsumer;
+import java.util.function.Predicate;
+
+/**
+ * Utility class to track stats log and emit corresponding app events
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public class AppEventProducer implements StatsLogConsumer {
+
+ private static final int MSG_LAUNCH = 0;
+
+ private final Context mContext;
+ private final Handler mMessageHandler;
+ private final ObjIntConsumer<AppTargetEvent> mCallback;
+
+ private LauncherAtom.ItemInfo mLastDragItem;
+
+ public AppEventProducer(Context context, ObjIntConsumer<AppTargetEvent> callback) {
+ mContext = context;
+ mMessageHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleMessage);
+ mCallback = callback;
+ }
+
+ @WorkerThread
+ private boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_LAUNCH: {
+ mCallback.accept((AppTargetEvent) msg.obj, msg.arg1);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @AnyThread
+ private void sendEvent(LauncherAtom.ItemInfo atomInfo, int eventId, int targetPredictor) {
+ sendEvent(toAppTarget(atomInfo), atomInfo, eventId, targetPredictor);
+ }
+
+ @AnyThread
+ private void sendEvent(AppTarget target, LauncherAtom.ItemInfo locationInfo, int eventId,
+ int targetPredictor) {
+ if (target != null) {
+ AppTargetEvent event = new AppTargetEvent.Builder(target, eventId)
+ .setLaunchLocation(getContainer(locationInfo))
+ .build();
+ mMessageHandler.obtainMessage(MSG_LAUNCH, targetPredictor, 0, event).sendToTarget();
+ }
+ }
+
+ @Override
+ public void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
+ if (event == LAUNCHER_APP_LAUNCH_TAP
+ || event == LAUNCHER_TASK_LAUNCH_SWIPE_DOWN
+ || event == LAUNCHER_TASK_LAUNCH_TAP
+ || event == LAUNCHER_QUICKSWITCH_RIGHT
+ || event == LAUNCHER_QUICKSWITCH_LEFT) {
+ sendEvent(atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
+ } else if (event == LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST) {
+ sendEvent(atomInfo, ACTION_DISMISS, CONTAINER_PREDICTION);
+ } else if (event == LAUNCHER_ITEM_DRAG_STARTED) {
+ mLastDragItem = atomInfo;
+ } else if (event == LAUNCHER_ITEM_DROP_COMPLETED) {
+ if (mLastDragItem == null) {
+ return;
+ }
+ if (isTrackedForHotseatPrediction(atomInfo)) {
+ sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+ }
+ if (isTrackedForHotseatPrediction(mLastDragItem)) {
+ sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+ }
+ mLastDragItem = null;
+ } else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) {
+ if (isTrackedForHotseatPrediction(atomInfo)) {
+ sendEvent(createTempFolderTarget(), atomInfo, ACTION_PIN,
+ CONTAINER_HOTSEAT_PREDICTION);
+ sendEvent(atomInfo, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+ }
+ } else if (event == LAUNCHER_FOLDER_CONVERTED_TO_ICON) {
+ if (isTrackedForHotseatPrediction(atomInfo)) {
+ sendEvent(createTempFolderTarget(), atomInfo, ACTION_UNPIN,
+ CONTAINER_HOTSEAT_PREDICTION);
+ sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+ }
+ } else if (event == LAUNCHER_ITEM_DROPPED_ON_REMOVE) {
+ if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) {
+ sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
+ }
+ } else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) {
+ if (isTrackedForHotseatPrediction(atomInfo)) {
+ sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
+ }
+ }
+ }
+
+ @Nullable
+ private AppTarget toAppTarget(LauncherAtom.ItemInfo info) {
+ UserHandle userHandle = Process.myUserHandle();
+ if (info.getIsWork()) {
+ userHandle = UserCache.INSTANCE.get(mContext).getUserProfiles().stream()
+ .filter(((Predicate<UserHandle>) userHandle::equals).negate())
+ .findAny()
+ .orElse(null);
+ }
+ if (userHandle == null) {
+ return null;
+ }
+ ComponentName cn = null;
+ String id = null;
+
+ switch (info.getItemCase()) {
+ case APPLICATION: {
+ LauncherAtom.Application app = info.getApplication();
+ if ((cn = parseNullable(app.getComponentName())) != null) {
+ id = "app:" + cn.getPackageName();
+ }
+ break;
+ }
+ case SHORTCUT: {
+ LauncherAtom.Shortcut si = info.getShortcut();
+ if (!TextUtils.isEmpty(si.getShortcutId())
+ && (cn = parseNullable(si.getShortcutName())) != null) {
+ id = "shortcut:" + si.getShortcutId();
+ }
+ break;
+ }
+ case WIDGET: {
+ LauncherAtom.Widget widget = info.getWidget();
+ if ((cn = parseNullable(widget.getComponentName())) != null) {
+ id = "widget:" + cn.getPackageName();
+ }
+ break;
+ }
+ case TASK: {
+ LauncherAtom.Task task = info.getTask();
+ if ((cn = parseNullable(task.getComponentName())) != null) {
+ id = "app:" + cn.getPackageName();
+ }
+ break;
+ }
+ case FOLDER_ICON:
+ return createTempFolderTarget();
+ }
+ if (id != null && cn != null) {
+ return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
+ .setClassName(cn.getClassName())
+ .build();
+ }
+ return null;
+ }
+
+ private AppTarget createTempFolderTarget() {
+ return new AppTarget.Builder(new AppTargetId("folder:" + SystemClock.uptimeMillis()),
+ mContext.getPackageName(), Process.myUserHandle())
+ .build();
+ }
+
+ private String getContainer(LauncherAtom.ItemInfo info) {
+ ContainerInfo ci = info.getContainerInfo();
+ switch (ci.getContainerCase()) {
+ case WORKSPACE: {
+ // In case the item type is not widgets, the spaceX and spanY default to 1.
+ int spanX = info.getWidget().getSpanX();
+ int spanY = info.getWidget().getSpanY();
+ return getWorkspaceContainerString(ci.getWorkspace(), spanX, spanY);
+ }
+ case HOTSEAT: {
+ return getHotseatContainerString(ci.getHotseat());
+ }
+ case TASK_SWITCHER_CONTAINER: {
+ return "task-switcher";
+ }
+ case ALL_APPS_CONTAINER: {
+ return "all-apps";
+ }
+ case SEARCH_RESULT_CONTAINER: {
+ return "search-results";
+ }
+ case PREDICTED_HOTSEAT_CONTAINER: {
+ return "predictions/hotseat";
+ }
+ case PREDICTION_CONTAINER: {
+ return "predictions";
+ }
+ case SHORTCUTS_CONTAINER: {
+ return "deep-shortcuts";
+ }
+ case FOLDER: {
+ FolderContainer fc = ci.getFolder();
+ switch (fc.getParentContainerCase()) {
+ case WORKSPACE:
+ return "folder/" + getWorkspaceContainerString(fc.getWorkspace(), 1, 1);
+ case HOTSEAT:
+ return "folder/" + getHotseatContainerString(fc.getHotseat());
+ }
+ return "folder";
+ }
+ }
+ return "";
+ }
+
+ private static String getWorkspaceContainerString(WorkspaceContainer wc, int spanX, int spanY) {
+ return String.format(Locale.ENGLISH, "workspace/%d/[%d,%d]/[%d,%d]",
+ wc.getPageIndex(), wc.getGridX(), wc.getGridY(), spanX, spanY);
+ }
+
+ private static String getHotseatContainerString(HotseatContainer hc) {
+ return String.format(Locale.ENGLISH, "hotseat/%1$d/[%1$d,0]/[1,1]", hc.getIndex());
+ }
+
+ private static ComponentName parseNullable(String componentNameString) {
+ return TextUtils.isEmpty(componentNameString)
+ ? null : ComponentName.unflattenFromString(componentNameString);
+ }
+
+ /**
+ * Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
+ */
+ private static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
+ ContainerInfo ci = info.getContainerInfo();
+ switch (ci.getContainerCase()) {
+ case HOTSEAT:
+ return true;
+ case WORKSPACE:
+ return ci.getWorkspace().getPageIndex() == 0;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
new file mode 100644
index 0000000..b0fba3d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -0,0 +1,119 @@
+/*
+ * 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.launcher3.model;
+
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
+import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
+
+import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+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.WorkspaceItemInfo;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Task to update model as a result of predicted apps update
+ */
+public class PredictionUpdateTask extends BaseModelUpdateTask {
+
+ private final List<AppTarget> mTargets;
+ private final PredictorState mPredictorState;
+
+ PredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
+ mPredictorState = predictorState;
+ mTargets = targets;
+ }
+
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ Context context = app.getContext();
+
+ // TODO: remove this
+ Utilities.getDevicePrefs(context).edit()
+ .putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
+
+ FixedContainerItems fci = mPredictorState.items;
+ Set<UserHandle> usersForChangedShortcuts = new HashSet<>(fci.items.stream()
+ .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
+ .map(info -> info.user)
+ .collect(Collectors.toSet()));
+ fci.items.clear();
+
+ for (AppTarget target : mTargets) {
+ WorkspaceItemInfo itemInfo;
+ ShortcutInfo si = target.getShortcutInfo();
+ if (si != null) {
+ usersForChangedShortcuts.add(si.getUserHandle());
+ itemInfo = new WorkspaceItemInfo(si, context);
+ app.getIconCache().getShortcutIcon(itemInfo, si);
+ } else {
+ String className = target.getClassName();
+ if (COMPONENT_CLASS_MARKER.equals(className)) {
+ // TODO: Implement this
+ continue;
+ }
+ ComponentName cn = new ComponentName(target.getPackageName(), className);
+ UserHandle user = target.getUser();
+ itemInfo = apps.data.stream()
+ .filter(info -> user.equals(info.user) && cn.equals(info.componentName))
+ .map(ai -> {
+ app.getIconCache().getTitleAndIcon(ai, false);
+ return ai.makeWorkspaceItem();
+ })
+ .findAny()
+ .orElseGet(() -> {
+ LauncherActivityInfo lai = context.getSystemService(LauncherApps.class)
+ .resolveActivity(AppInfo.makeLaunchIntent(cn), user);
+ if (lai == null) {
+ return null;
+ }
+ AppInfo ai = new AppInfo(context, lai, user);
+ app.getIconCache().getTitleAndIcon(ai, lai, false);
+ return ai.makeWorkspaceItem();
+ });
+
+ if (itemInfo == null) {
+ continue;
+ }
+ }
+
+ itemInfo.container = fci.containerId;
+ fci.items.add(itemInfo);
+ }
+
+ bindExtraContainerItems(fci);
+ usersForChangedShortcuts.forEach(
+ u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
+
+ // Save to disk
+ mPredictorState.storage.write(context, fci.items);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
new file mode 100644
index 0000000..b1b4d70
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -0,0 +1,378 @@
+/*
+ * 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.launcher3.model;
+
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.formatElapsedTime;
+
+import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
+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 android.app.prediction.AppPredictionContext;
+import android.app.prediction.AppPredictionManager;
+import android.app.prediction.AppPredictor;
+import android.app.prediction.AppTarget;
+import android.app.prediction.AppTargetEvent;
+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.ShortcutInfo;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.Executors;
+import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.PersistedItemArray;
+import com.android.quickstep.logging.StatsLogCompatManager;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.IntStream;
+
+/**
+ * Model delegate which loads prediction items
+ */
+public class QuickstepModelDelegate extends ModelDelegate implements OnIDPChangeListener {
+
+ public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
+ private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
+
+ private static final boolean IS_DEBUG = false;
+ private static final String TAG = "QuickstepModelDelegate";
+
+ private final PredictorState mAllAppsState =
+ new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
+ private final PredictorState mHotseatState =
+ new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions");
+ private final PredictorState mWidgetsRecommendationState =
+ new PredictorState(CONTAINER_WIDGETS_PREDICTION, "widgets_prediction");
+
+ private final InvariantDeviceProfile mIDP;
+ private final AppEventProducer mAppEventProducer;
+
+ protected boolean mActive = false;
+
+ public QuickstepModelDelegate(Context context) {
+ mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent);
+
+ mIDP = InvariantDeviceProfile.INSTANCE.get(context);
+ mIDP.addOnChangeListener(this);
+ StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer);
+ }
+
+ @Override
+ @WorkerThread
+ public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) {
+ // TODO: Implement caching and preloading
+ super.loadItems(ums, pinnedShortcuts);
+
+ WorkspaceItemFactory allAppsFactory =
+ new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns);
+ mAllAppsState.items.setItems(
+ mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
+ mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
+
+ WorkspaceItemFactory hotseatFactory =
+ new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numHotseatIcons);
+ mHotseatState.items.setItems(
+ mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
+ mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
+
+ // Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
+ mDataModel.extraItems.put(CONTAINER_WIDGETS_PREDICTION, mWidgetsRecommendationState.items);
+ mActive = true;
+ }
+
+ @Override
+ public void workspaceLoadComplete() {
+ super.workspaceLoadComplete();
+ recreatePredictors();
+ }
+
+ @Override
+ @WorkerThread
+ public void modelLoadComplete() {
+ super.modelLoadComplete();
+
+ // Log snapshot of the model
+ SharedPreferences prefs = getDevicePrefs(mApp.getContext());
+ long lastSnapshotTimeMillis = prefs.getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
+ // Log snapshot only if previous snapshot was older than a day
+ long now = System.currentTimeMillis();
+ if (now - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
+ if (IS_DEBUG) {
+ String elapsedTime = formatElapsedTime((now - lastSnapshotTimeMillis) / 1000);
+ Log.d(TAG, String.format(
+ "Skipped snapshot logging since previous snapshot was %s old.",
+ elapsedTime));
+ }
+ } else {
+ IntSparseArrayMap<ItemInfo> itemsIdMap;
+ synchronized (mDataModel) {
+ itemsIdMap = mDataModel.itemsIdMap.clone();
+ }
+ InstanceId instanceId = new InstanceIdSequence().newInstanceId();
+ for (ItemInfo info : itemsIdMap) {
+ FolderInfo parent = info.container > 0
+ ? (FolderInfo) itemsIdMap.get(info.container) : null;
+ StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId);
+ }
+ additionalSnapshotEvents(instanceId);
+ prefs.edit().putLong(LAST_SNAPSHOT_TIME_MILLIS, now).apply();
+ }
+ }
+
+ protected void additionalSnapshotEvents(InstanceId snapshotInstanceId){}
+
+ @Override
+ public void validateData() {
+ super.validateData();
+ if (mAllAppsState.predictor != null) {
+ mAllAppsState.predictor.requestPredictionUpdate();
+ }
+ if (mWidgetsRecommendationState.predictor != null) {
+ mWidgetsRecommendationState.predictor.requestPredictionUpdate();
+ }
+ }
+
+ @Override
+ public void destroy() {
+ super.destroy();
+ mActive = false;
+ StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer);
+
+ destroyPredictors();
+ mIDP.removeOnChangeListener(this);
+ }
+
+ private void destroyPredictors() {
+ mAllAppsState.destroyPredictor();
+ mHotseatState.destroyPredictor();
+ mWidgetsRecommendationState.destroyPredictor();
+ }
+
+ @WorkerThread
+ private void recreatePredictors() {
+ destroyPredictors();
+ if (!mActive) {
+ return;
+ }
+ Context context = mApp.getContext();
+ AppPredictionManager apm = context.getSystemService(AppPredictionManager.class);
+ if (apm == null) {
+ return;
+ }
+
+ registerPredictor(mAllAppsState, apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(context)
+ .setUiSurface("home")
+ .setPredictedTargetCount(mIDP.numAllAppsColumns)
+ .build()));
+
+ // TODO: get bundle
+ registerPredictor(mHotseatState, apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(context)
+ .setUiSurface("hotseat")
+ .setPredictedTargetCount(mIDP.numHotseatIcons)
+ .setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
+ .build()));
+
+ registerWidgetsPredictor(apm.createAppPredictionSession(
+ new AppPredictionContext.Builder(context)
+ .setUiSurface("widgets")
+ .setPredictedTargetCount(mIDP.numColumns)
+ .build()));
+ }
+
+ private void registerPredictor(PredictorState state, AppPredictor predictor) {
+ state.predictor = predictor;
+ state.predictor.registerPredictionUpdates(
+ Executors.MODEL_EXECUTOR, t -> handleUpdate(state, t));
+ state.predictor.requestPredictionUpdate();
+ }
+
+ private void handleUpdate(PredictorState state, List<AppTarget> targets) {
+ if (state.setTargets(targets)) {
+ // No diff, skip
+ return;
+ }
+ mApp.getModel().enqueueModelUpdateTask(new PredictionUpdateTask(state, targets));
+ }
+
+ private void registerWidgetsPredictor(AppPredictor predictor) {
+ mWidgetsRecommendationState.predictor = predictor;
+ mWidgetsRecommendationState.predictor.registerPredictionUpdates(
+ Executors.MODEL_EXECUTOR, targets -> {
+ if (mWidgetsRecommendationState.setTargets(targets)) {
+ // No diff, skip
+ return;
+ }
+ mApp.getModel().enqueueModelUpdateTask(
+ new WidgetsPredictionUpdateTask(mWidgetsRecommendationState, targets));
+ });
+ mWidgetsRecommendationState.predictor.requestPredictionUpdate();
+ }
+
+ @Override
+ public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
+ if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
+ // Reinitialize everything
+ Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
+ }
+ }
+
+ private void onAppTargetEvent(AppTargetEvent event, int client) {
+ PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState;
+ if (state.predictor != null) {
+ state.predictor.notifyAppTargetEvent(event);
+ }
+ }
+
+ static class PredictorState {
+
+ public final FixedContainerItems items;
+ public final PersistedItemArray<ItemInfo> storage;
+ public AppPredictor predictor;
+
+ private List<AppTarget> mLastTargets;
+
+ PredictorState(int container, String storageName) {
+ items = new FixedContainerItems(container);
+ storage = new PersistedItemArray<>(storageName);
+ mLastTargets = Collections.emptyList();
+ }
+
+ public void destroyPredictor() {
+ if (predictor != null) {
+ predictor.destroy();
+ predictor = null;
+ }
+ }
+
+ /**
+ * Sets the new targets and returns true if it was the same as before.
+ */
+ boolean setTargets(List<AppTarget> newTargets) {
+ List<AppTarget> oldTargets = mLastTargets;
+ mLastTargets = newTargets;
+
+ int size = oldTargets.size();
+ return size == newTargets.size() && IntStream.range(0, size)
+ .allMatch(i -> areAppTargetsSame(oldTargets.get(i), newTargets.get(i)));
+ }
+ }
+
+ /**
+ * Compares two targets for the properties which we care about
+ */
+ private static boolean areAppTargetsSame(AppTarget t1, AppTarget t2) {
+ if (!Objects.equals(t1.getPackageName(), t2.getPackageName())
+ || !Objects.equals(t1.getUser(), t2.getUser())
+ || !Objects.equals(t1.getClassName(), t2.getClassName())) {
+ return false;
+ }
+
+ ShortcutInfo s1 = t1.getShortcutInfo();
+ ShortcutInfo s2 = t2.getShortcutInfo();
+ if (s1 != null) {
+ if (s2 == null || !Objects.equals(s1.getId(), s2.getId())) {
+ return false;
+ }
+ } else if (s2 != null) {
+ return false;
+ }
+ return true;
+ }
+
+ private static class WorkspaceItemFactory implements PersistedItemArray.ItemFactory<ItemInfo> {
+
+ private final LauncherAppState mAppState;
+ private final UserManagerState mUMS;
+ private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts;
+ private final int mMaxCount;
+
+ private int mReadCount = 0;
+
+ protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums,
+ Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount) {
+ mAppState = appState;
+ mUMS = ums;
+ mPinnedShortcuts = pinnedShortcuts;
+ mMaxCount = maxCount;
+ }
+
+ @Nullable
+ @Override
+ public ItemInfo createInfo(int itemType, UserHandle user, Intent intent) {
+ if (mReadCount >= mMaxCount) {
+ return null;
+ }
+ switch (itemType) {
+ case ITEM_TYPE_APPLICATION: {
+ LauncherActivityInfo lai = mAppState.getContext()
+ .getSystemService(LauncherApps.class)
+ .resolveActivity(intent, user);
+ if (lai == null) {
+ return null;
+ }
+ AppInfo info = new AppInfo(lai, user, mUMS.isUserQuiet(user));
+ mAppState.getIconCache().getTitleAndIcon(info, lai, false);
+ mReadCount++;
+ return info.makeWorkspaceItem();
+ }
+ case ITEM_TYPE_DEEP_SHORTCUT: {
+ ShortcutKey key = ShortcutKey.fromIntent(intent, user);
+ if (key == null) {
+ return null;
+ }
+ ShortcutInfo si = mPinnedShortcuts.get(key);
+ if (si == null) {
+ return null;
+ }
+ WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext());
+ mAppState.getIconCache().getShortcutIcon(wii, si);
+ mReadCount++;
+ return wii;
+ }
+ }
+ return null;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 810f4e3..995c4b0 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -18,8 +18,7 @@
import static android.content.ContentResolver.SCHEME_CONTENT;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
+import static com.android.launcher3.Utilities.newContentObserver;
import android.annotation.TargetApi;
import android.app.RemoteAction;
@@ -35,7 +34,7 @@
import android.os.Bundle;
import android.os.DeadObjectException;
import android.os.Handler;
-import android.os.Message;
+import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -43,7 +42,7 @@
import android.util.Log;
import androidx.annotation.MainThread;
-import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
@@ -55,6 +54,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.BgObjectWithLooper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions;
@@ -63,21 +63,16 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
-import java.util.function.Consumer;
/**
* Data model for digital wellbeing status of apps.
*/
@TargetApi(Build.VERSION_CODES.Q)
-public final class WellbeingModel {
+public final class WellbeingModel extends BgObjectWithLooper {
private static final String TAG = "WellbeingModel";
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
private static final boolean DEBUG = false;
- private static final int MSG_PACKAGE_ADDED = 1;
- private static final int MSG_PACKAGE_REMOVED = 2;
- private static final int MSG_FULL_REFRESH = 3;
-
private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
private static final int IN_MINIMAL_DEVICE = 2;
@@ -99,9 +94,9 @@
private final Context mContext;
private final String mWellbeingProviderPkg;
- private final Handler mWorkerHandler;
- private final ContentObserver mContentObserver;
+ private Handler mWorkerHandler;
+ private ContentObserver mContentObserver;
private final Object mModelLock = new Object();
// Maps the action Id to the corresponding RemoteAction
@@ -112,60 +107,73 @@
private WellbeingModel(final Context context) {
mContext = context;
- mWorkerHandler =
- new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
-
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
- mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
- + selfChange + "], uri = [" + uri + "]");
- }
- Preconditions.assertUIThread();
+ initializeInBackground("WellbeingHandler");
+ }
- if (uri.getPath().contains(PATH_ACTIONS)) {
- // Wellbeing reports that app actions have changed.
- updateWellbeingData();
- } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
- // Wellbeing reports that minimal device state or config is changed.
- updateLauncherModel(context);
- }
- }
- };
- FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
- updateLauncherModel(context));
-
+ @Override
+ protected void onInitialized(Looper looper) {
+ mWorkerHandler = new Handler(looper);
+ mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
- context.registerReceiver(
- new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
+ mContext.registerReceiver(
+ new SimpleBroadcastReceiver(t -> restartObserver()),
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
- Intent.ACTION_PACKAGE_RESTARTED));
+ Intent.ACTION_PACKAGE_RESTARTED),
+ null, mWorkerHandler);
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
- context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
- filter);
+ mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
+ filter, null, mWorkerHandler);
restartObserver();
}
}
+ @WorkerThread
+ private void onWellbeingUriChanged(Uri uri) {
+ Preconditions.assertNonUiThread();
+ if (DEBUG || mIsInTest) {
+ Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
+ }
+ if (uri.getPath().contains(PATH_ACTIONS)) {
+ // Wellbeing reports that app actions have changed.
+ updateAllPackages();
+ } else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
+ // Wellbeing reports that minimal device state or config is changed.
+ if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
+ return;
+ }
+
+ // Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when
+ // minimal device is enabled. We always want to reload the configuration from Wellbeing
+ // since the layout configuration might have changed.
+ mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE);
+
+ final Bundle extras = new Bundle();
+ String dbFile;
+ if (isInMinimalDeviceMode()) {
+ dbFile = DB_NAME_MINIMAL_DEVICE;
+ extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
+ mWellbeingProviderPkg + ".api");
+ } else {
+ dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
+ }
+ LauncherSettings.Settings.call(mContext.getContentResolver(),
+ LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
+ dbFile, extras);
+ }
+ }
+
public void setInTest(boolean inTest) {
mIsInTest = inTest;
}
- protected void onWellbeingProviderChanged(Intent intent) {
- if (DEBUG || mIsInTest) {
- Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
- }
- restartObserver();
- }
-
+ @WorkerThread
private void restartObserver() {
final ContentResolver resolver = mContext.getContentResolver();
resolver.unregisterContentObserver(mContentObserver);
@@ -180,7 +188,7 @@
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
- updateWellbeingData();
+ updateAllPackages();
}
@MainThread
@@ -213,74 +221,40 @@
}
}
- private void updateWellbeingData() {
- mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
- }
-
- private void updateLauncherModel(@NonNull final Context context) {
- if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
- reloadLauncherInNormalMode(context);
- return;
- }
- runWithMinimalDeviceConfigs((bundle) -> {
- if (bundle.getInt(EXTRA_MINIMAL_DEVICE_STATE, UNKNOWN_MINIMAL_DEVICE_STATE)
- == IN_MINIMAL_DEVICE) {
- reloadLauncherInMinimalMode(context);
- } else {
- reloadLauncherInNormalMode(context);
- }
- });
- }
-
- private void reloadLauncherInNormalMode(@NonNull final Context context) {
- LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
- InvariantDeviceProfile.INSTANCE.get(context).dbFile);
- }
-
- private void reloadLauncherInMinimalMode(@NonNull final Context context) {
- final Bundle extras = new Bundle();
- extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
- mWellbeingProviderPkg + ".api");
- LauncherSettings.Settings.call(context.getContentResolver(),
- LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
- DB_NAME_MINIMAL_DEVICE, extras);
- }
-
private Uri.Builder apiBuilder() {
return new Uri.Builder()
.scheme(SCHEME_CONTENT)
.authority(mWellbeingProviderPkg + ".api");
}
- /**
- * Fetch most up-to-date minimal device config.
- */
@WorkerThread
- private void runWithMinimalDeviceConfigs(Consumer<Bundle> consumer) {
+ private boolean isInMinimalDeviceMode() {
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
- return;
+ return false;
}
if (DEBUG || mIsInTest) {
- Log.d(TAG, "runWithMinimalDeviceConfigs() called");
+ Log.d(TAG, "isInMinimalDeviceMode() called");
}
Preconditions.assertNonUiThread();
final Uri contentUri = apiBuilder().build();
- final Bundle remoteBundle;
try (ContentProviderClient client = mContext.getContentResolver()
.acquireUnstableContentProviderClient(contentUri)) {
- remoteBundle = client.call(
+ final Bundle remoteBundle = client == null ? null : client.call(
METHOD_GET_MINIMAL_DEVICE_CONFIG, null /* args */, null /* extras */);
- consumer.accept(remoteBundle);
+ return remoteBundle != null
+ && remoteBundle.getInt(EXTRA_MINIMAL_DEVICE_STATE,
+ UNKNOWN_MINIMAL_DEVICE_STATE) == IN_MINIMAL_DEVICE;
} catch (Exception e) {
Log.e(TAG, "Failed to retrieve data from " + contentUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e);
}
- if (DEBUG || mIsInTest) Log.i(TAG, "runWithMinimalDeviceConfigs(): finished");
+ if (DEBUG || mIsInTest) Log.i(TAG, "isInMinimalDeviceMode(): finished");
+ return false;
}
- private boolean updateActions(String... packageNames) {
+ @WorkerThread
+ private boolean updateActions(String[] packageNames) {
if (packageNames.length == 0) {
return true;
}
@@ -343,68 +317,51 @@
return true;
}
- private boolean handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_PACKAGE_REMOVED: {
- String packageName = (String) msg.obj;
- mWorkerHandler.removeCallbacksAndMessages(packageName);
- synchronized (mModelLock) {
- mPackageToActionId.remove(packageName);
- }
- return true;
- }
- case MSG_PACKAGE_ADDED: {
- String packageName = (String) msg.obj;
- mWorkerHandler.removeCallbacksAndMessages(packageName);
- if (!updateActions(packageName)) {
- scheduleRefreshRetry(msg);
- }
- return true;
- }
+ @WorkerThread
+ private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
+ String[] packageNames = TextUtils.isEmpty(packageName)
+ ? mContext.getSystemService(LauncherApps.class)
+ .getActivityList(null, Process.myUserHandle()).stream()
+ .map(li -> li.getApplicationInfo().packageName).distinct()
+ .toArray(String[]::new)
+ : new String[] { packageName };
- case MSG_FULL_REFRESH: {
- // Remove all existing messages
- mWorkerHandler.removeCallbacksAndMessages(null);
- final String[] packageNames = mContext.getSystemService(LauncherApps.class)
- .getActivityList(null, Process.myUserHandle()).stream()
- .map(li -> li.getApplicationInfo().packageName).distinct()
- .toArray(String[]::new);
- if (!updateActions(packageNames)) {
- scheduleRefreshRetry(msg);
- }
- return true;
- }
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ if (updateActions(packageNames)) {
+ return;
}
- return false;
- }
-
- private void scheduleRefreshRetry(Message originalMsg) {
- int retryCount = originalMsg.arg1;
if (retryCount >= RETRY_TIMES_MS.length) {
// To many retries, skip
return;
}
-
- Message msg = Message.obtain(originalMsg);
- msg.arg1 = retryCount + 1;
- mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
+ mWorkerHandler.postDelayed(
+ () -> updateActionsWithRetry(retryCount + 1, packageName),
+ packageName, RETRY_TIMES_MS[retryCount]);
}
+ @WorkerThread
+ private void updateAllPackages() {
+ updateActionsWithRetry(0, null);
+ }
+
+ @WorkerThread
private void onAppPackageChanged(Intent intent) {
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
- Preconditions.assertUIThread();
+ Preconditions.assertNonUiThread();
final String packageName = intent.getData().getSchemeSpecificPart();
if (packageName == null || packageName.length() == 0) {
// they sent us a bad intent
return;
}
-
final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
+ mWorkerHandler.removeCallbacksAndMessages(packageName);
+ synchronized (mModelLock) {
+ mPackageToActionId.remove(packageName);
+ }
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
+ updateActionsWithRetry(0, packageName);
}
}
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
new file mode 100644
index 0000000..a29ac1a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -0,0 +1,82 @@
+/*
+ * 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;
+
+import android.app.prediction.AppTarget;
+
+import com.android.launcher3.LauncherAppState;
+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.stream.Collectors;
+
+/** Task to update model as a result of predicted widgets update */
+public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {
+ private final PredictorState mPredictorState;
+ private final List<AppTarget> mTargets;
+
+ WidgetsPredictionUpdateTask(PredictorState predictorState, List<AppTarget> targets) {
+ mPredictorState = predictorState;
+ mTargets = targets;
+ }
+
+ /**
+ * Uses the app predication result to infer widgets that the user may want to use.
+ *
+ * <p>The algorithm uses the app prediction ranking to create a widgets ranking which only
+ * includes one widget per app and excludes widgets that have already been added to the
+ * workspace.
+ */
+ @Override
+ public void execute(LauncherAppState appState, BgDataModel dataModel, AllAppsList apps) {
+ Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
+ widget -> new ComponentKey(widget.providerName, widget.user)).collect(
+ Collectors.toSet());
+ Map<PackageUserKey, List<WidgetItem>> allWidgets =
+ dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
+
+ ArrayList<ItemInfo> recommendedWidgetsInDescendingOrder = new ArrayList<>();
+ 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.
+ recommendedWidgetsInDescendingOrder.add(
+ new PendingAddWidgetInfo(notAddedWidgets.get(0).widgetInfo));
+ }
+ }
+ }
+ FixedContainerItems fixedContainerItems = mPredictorState.items;
+ fixedContainerItems.items.clear();
+ fixedContainerItems.items.addAll(recommendedWidgetsInDescendingOrder);
+ bindExtraContainerItems(fixedContainerItems);
+
+ // Don't store widgets prediction to disk because it is not used frequently.
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
index e302b4f..4d7cc85 100644
--- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -17,6 +17,7 @@
package com.android.launcher3.proxy;
import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender.SendIntentException;
@@ -48,19 +49,20 @@
return;
}
- if (mParams.intent != null) {
- startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
- return;
- } else if (mParams.intentSender != null) {
- try {
+ try {
+ if (mParams.intent != null) {
+ startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+ return;
+ } else if (mParams.intentSender != null) {
startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
mParams.extraFlags,
mParams.options);
return;
- } catch (SendIntentException e) {
- mParams.deliverResult(this, RESULT_CANCELED, null);
}
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException
+ | SendIntentException e) {
+ mParams.deliverResult(this, RESULT_CANCELED, null);
}
finishAndRemoveTask();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
new file mode 100644
index 0000000..8312b82
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -0,0 +1,86 @@
+/*
+ * 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.taskbar;
+
+import android.content.ContextWrapper;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * The {@link ActivityContext} with which we inflate Taskbar-related Views. This allows UI elements
+ * that are used by both Launcher and Taskbar (such as Folder) to reference a generic
+ * ActivityContext and BaseDragLayer instead of the Launcher activity and its DragLayer.
+ */
+public class TaskbarActivityContext extends ContextWrapper implements ActivityContext {
+
+ private final DeviceProfile mDeviceProfile;
+ private final LayoutInflater mLayoutInflater;
+ private final TaskbarContainerView mTaskbarContainerView;
+ private final float mIconScale;
+
+ public TaskbarActivityContext(BaseQuickstepLauncher launcher) {
+ super(launcher);
+ mDeviceProfile = launcher.getDeviceProfile().copy(this);
+ float taskbarIconSize = getResources().getDimension(R.dimen.taskbar_icon_size);
+ mIconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
+ mDeviceProfile.updateIconSize(mIconScale, getResources());
+
+ mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+
+ mTaskbarContainerView = (TaskbarContainerView) mLayoutInflater
+ .inflate(R.layout.taskbar, null, false);
+ }
+
+ public TaskbarContainerView getTaskbarContainerView() {
+ return mTaskbarContainerView;
+ }
+
+ /**
+ * @return A LayoutInflater to use in this Context. Views inflated with this LayoutInflater will
+ * be able to access this TaskbarActivityContext via ActivityContext.lookupContext().
+ */
+ public LayoutInflater getLayoutInflater() {
+ return mLayoutInflater;
+ }
+
+ @Override
+ public BaseDragLayer<TaskbarActivityContext> getDragLayer() {
+ return mTaskbarContainerView;
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mDeviceProfile;
+ }
+
+ @Override
+ public Rect getFolderBoundingBox() {
+ return mTaskbarContainerView.getFolderBoundingBox();
+ }
+
+ /**
+ * @return The ratio of taskbar icon size vs normal workspace/hotseat icon size.
+ */
+ public float getTaskbarIconScale() {
+ return mIconScale;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
new file mode 100644
index 0000000..528f43e
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
@@ -0,0 +1,162 @@
+/*
+ * 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.taskbar;
+
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper;
+
+/**
+ * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
+ */
+public class TaskbarContainerView extends BaseDragLayer<TaskbarActivityContext> {
+
+ private final int[] mTempLoc = new int[2];
+ private final int mFolderMargin;
+
+ // Initialized in TaskbarController constructor.
+ private TaskbarController.TaskbarContainerViewCallbacks mControllerCallbacks;
+
+ // Initialized in init.
+ private TaskbarView mTaskbarView;
+ private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
+
+ public TaskbarContainerView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TaskbarContainerView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr, int defStyleRes) {
+ super(context, attrs, 1 /* alphaChannelCount */);
+ mFolderMargin = getResources().getDimensionPixelSize(R.dimen.taskbar_folder_margin);
+ }
+
+ protected void construct(TaskbarController.TaskbarContainerViewCallbacks callbacks) {
+ mControllerCallbacks = callbacks;
+ }
+
+ protected void init(TaskbarView taskbarView) {
+ mTaskbarView = taskbarView;
+ mTaskbarInsetsComputer = createTaskbarInsetsComputer();
+ recreateControllers();
+ }
+
+ @Override
+ public void recreateControllers() {
+ mControllers = new TouchController[0];
+ }
+
+ private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
+ return insetsInfo -> {
+ if (getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD
+ || mTaskbarView.isDraggingItem()) {
+ // We're invisible or dragging out of taskbar, let touches pass through us.
+ insetsInfo.touchableRegion.setEmpty();
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+ // TODO(b/182234653): Shouldn't need to do this, but for the meantime, reporting
+ // that visibleInsets is empty allows DragEvents through. Setting them as completely
+ // empty reverts to default behavior, so set 1 px instead.
+ insetsInfo.visibleInsets.set(0, 0, 0, 1);
+ } else {
+ // We're visible again, accept touches anywhere in our bounds.
+ insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+ }
+
+ // TaskbarContainerView provides insets to other apps based on contentInsets. These
+ // insets should stay consistent even if we expand TaskbarContainerView's bounds, e.g.
+ // to show a floating view like Folder. Thus, we set the contentInsets to be where
+ // mTaskbarView is, since its position never changes and insets rather than overlays.
+ int[] loc = mTempLoc;
+ float scale = mTaskbarView.getScaleX();
+ mTaskbarView.setScaleX(1);
+ mTaskbarView.setScaleY(1);
+ mTaskbarView.getLocationInWindow(loc);
+ mTaskbarView.setScaleX(scale);
+ mTaskbarView.setScaleY(scale);
+ insetsInfo.contentInsets.left = loc[0];
+ insetsInfo.contentInsets.top = loc[1];
+ insetsInfo.contentInsets.right = getWidth() - (loc[0] + mTaskbarView.getWidth());
+ insetsInfo.contentInsets.bottom = getHeight() - (loc[1] + mTaskbarView.getHeight());
+ };
+ }
+
+ protected void cleanup() {
+ ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
+ mTaskbarInsetsComputer);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ cleanup();
+ }
+
+ @Override
+ protected boolean canFindActiveController() {
+ // Unlike super class, we want to be able to find controllers when touches occur in the
+ // gesture area. For example, this allows Folder to close itself when touching the Taskbar.
+ return true;
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ mControllerCallbacks.onViewRemoved();
+ }
+
+ /**
+ * @return Bounds (in our coordinates) where an opened Folder can display.
+ */
+ protected Rect getFolderBoundingBox() {
+ Rect boundingBox = new Rect(0, 0, getWidth(), getHeight() - mTaskbarView.getHeight());
+ boundingBox.inset(mFolderMargin, mFolderMargin);
+ return boundingBox;
+ }
+
+ protected TaskbarActivityContext getTaskbarActivityContext() {
+ return mActivity;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
new file mode 100644
index 0000000..fb7d85f
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -0,0 +1,519 @@
+/*
+ * 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.taskbar;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+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.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
+import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.data.FolderInfo;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
+ */
+public class TaskbarController {
+
+ private static final String WINDOW_TITLE = "Taskbar";
+
+ private final TaskbarContainerView mTaskbarContainerView;
+ private final TaskbarView mTaskbarView;
+ private final BaseQuickstepLauncher mLauncher;
+ private final WindowManager mWindowManager;
+ // Layout width and height of the Taskbar in the default state.
+ private final Point mTaskbarSize;
+ private final TaskbarStateHandler mTaskbarStateHandler;
+ private final TaskbarVisibilityController mTaskbarVisibilityController;
+ private final TaskbarHotseatController mHotseatController;
+ private final TaskbarRecentsController mRecentsController;
+ private final TaskbarDragController mDragController;
+
+ // Initialized in init().
+ private WindowManager.LayoutParams mWindowLayoutParams;
+
+ // Contains all loaded Tasks, not yet deduped from Hotseat items.
+ private List<Task> mLatestLoadedRecentTasks;
+ // Contains all loaded Hotseat items.
+ private ItemInfo[] mLatestLoadedHotseatItems;
+
+ private @Nullable Animator mAnimator;
+ private boolean mIsAnimatingToLauncher;
+
+ public TaskbarController(BaseQuickstepLauncher launcher,
+ TaskbarContainerView taskbarContainerView) {
+ mLauncher = launcher;
+ mTaskbarContainerView = taskbarContainerView;
+ mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
+ mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
+ mTaskbarView.construct(createTaskbarViewCallbacks());
+ mWindowManager = mLauncher.getWindowManager();
+ mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
+ mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
+ mTaskbarVisibilityController = new TaskbarVisibilityController(mLauncher,
+ createTaskbarVisibilityControllerCallbacks());
+ mHotseatController = new TaskbarHotseatController(mLauncher,
+ createTaskbarHotseatControllerCallbacks());
+ mRecentsController = new TaskbarRecentsController(mLauncher,
+ createTaskbarRecentsControllerCallbacks());
+ mDragController = new TaskbarDragController(mLauncher);
+ }
+
+ private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() {
+ return new TaskbarVisibilityControllerCallbacks() {
+ @Override
+ public void updateTaskbarBackgroundAlpha(float alpha) {
+ mTaskbarView.setBackgroundAlpha(alpha);
+ }
+
+ @Override
+ public void updateTaskbarVisibilityAlpha(float alpha) {
+ mTaskbarContainerView.setAlpha(alpha);
+ }
+ };
+ }
+
+ private TaskbarContainerViewCallbacks createTaskbarContainerViewCallbacks() {
+ return new TaskbarContainerViewCallbacks() {
+ @Override
+ public void onViewRemoved() {
+ if (mTaskbarContainerView.getChildCount() == 1) {
+ // Only TaskbarView remains.
+ setTaskbarWindowFullscreen(false);
+ }
+ }
+ };
+ }
+
+ private TaskbarViewCallbacks createTaskbarViewCallbacks() {
+ return new TaskbarViewCallbacks() {
+ @Override
+ public View.OnClickListener getItemOnClickListener() {
+ return view -> {
+ Object tag = view.getTag();
+ if (tag instanceof Task) {
+ Task task = (Task) tag;
+ ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
+ ActivityOptions.makeBasic());
+ } else if (tag instanceof FolderInfo) {
+ FolderIcon folderIcon = (FolderIcon) view;
+ Folder folder = folderIcon.getFolder();
+
+ setTaskbarWindowFullscreen(true);
+
+ mTaskbarContainerView.post(() -> {
+ folder.animateOpen();
+
+ folder.iterateOverItems((itemInfo, itemView) -> {
+ itemView.setOnClickListener(getItemOnClickListener());
+ itemView.setOnLongClickListener(getItemOnLongClickListener());
+ // To play haptic when dragging, like other Taskbar items do.
+ itemView.setHapticFeedbackEnabled(true);
+ return false;
+ });
+ });
+ } else {
+ ItemClickHandler.INSTANCE.onClick(view);
+ }
+
+ AbstractFloatingView.closeAllOpenViews(
+ mTaskbarContainerView.getTaskbarActivityContext());
+ };
+ }
+
+ @Override
+ public View.OnLongClickListener getItemOnLongClickListener() {
+ return view -> {
+ if (mLauncher.hasBeenResumed() && view.getTag() instanceof ItemInfo) {
+ alignRealHotseatWithTaskbar();
+ return mDragController.startWorkspaceDragOnLongClick(view);
+ } else {
+ return mDragController.startSystemDragOnLongClick(view);
+ }
+ };
+ }
+
+ @Override
+ public int getEmptyHotseatViewVisibility() {
+ // When on the home screen, we want the empty hotseat views to take up their full
+ // space so that the others line up with the home screen hotseat.
+ return mLauncher.hasBeenResumed() || mIsAnimatingToLauncher
+ ? View.INVISIBLE : View.GONE;
+ }
+ };
+ }
+
+ private TaskbarHotseatControllerCallbacks createTaskbarHotseatControllerCallbacks() {
+ return new TaskbarHotseatControllerCallbacks() {
+ @Override
+ public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+ mTaskbarView.updateHotseatItems(hotseatItemInfos);
+ mLatestLoadedHotseatItems = hotseatItemInfos;
+ dedupeAndUpdateRecentItems();
+ }
+ };
+ }
+
+ private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() {
+ return new TaskbarRecentsControllerCallbacks() {
+ @Override
+ public void updateRecentItems(ArrayList<Task> recentTasks) {
+ mLatestLoadedRecentTasks = recentTasks;
+ dedupeAndUpdateRecentItems();
+ }
+
+ @Override
+ public void updateRecentTaskAtIndex(int taskIndex, Task task) {
+ mTaskbarView.updateRecentTaskAtIndex(taskIndex, task);
+ }
+ };
+ }
+
+ /**
+ * Initializes the Taskbar, including adding it to the screen.
+ */
+ public void init() {
+ mTaskbarView.init(mHotseatController.getNumHotseatIcons(),
+ mRecentsController.getNumRecentIcons());
+ mTaskbarContainerView.init(mTaskbarView);
+ addToWindowManager();
+ mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
+ mTaskbarVisibilityController.init();
+ mHotseatController.init();
+ mRecentsController.init();
+
+ SCALE_PROPERTY.set(mTaskbarView, mLauncher.hasBeenResumed() ? getTaskbarScaleOnHome() : 1f);
+ }
+
+ private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
+ return new TaskbarStateHandlerCallbacks() {
+ @Override
+ public AnimatedFloat getAlphaTarget() {
+ return mTaskbarVisibilityController.getTaskbarVisibilityForLauncherState();
+ }
+ };
+ }
+
+ /**
+ * Removes the Taskbar from the screen, and removes any obsolete listeners etc.
+ */
+ public void cleanup() {
+ if (mAnimator != null) {
+ // End this first, in case it relies on properties that are about to be cleaned up.
+ mAnimator.end();
+ }
+
+ mTaskbarView.cleanup();
+ mTaskbarContainerView.cleanup();
+ removeFromWindowManager();
+ mTaskbarStateHandler.setTaskbarCallbacks(null);
+ mTaskbarVisibilityController.cleanup();
+ mHotseatController.cleanup();
+ mRecentsController.cleanup();
+ }
+
+ private void removeFromWindowManager() {
+ mWindowManager.removeViewImmediate(mTaskbarContainerView);
+ }
+
+ private void addToWindowManager() {
+ final int gravity = Gravity.BOTTOM;
+
+ mWindowLayoutParams = new WindowManager.LayoutParams(
+ mTaskbarSize.x,
+ mTaskbarSize.y,
+ TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ mWindowLayoutParams.setTitle(WINDOW_TITLE);
+ mWindowLayoutParams.packageName = mLauncher.getPackageName();
+ mWindowLayoutParams.gravity = gravity;
+ mWindowLayoutParams.setFitInsetsTypes(0);
+ mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
+ mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ mWindowLayoutParams.setSystemApplicationOverlay(true);
+
+ WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
+ wmWrapper.setProvidesInsetsTypes(
+ mWindowLayoutParams,
+ new int[] { ITYPE_EXTRA_NAVIGATION_BAR, ITYPE_BOTTOM_TAPPABLE_ELEMENT }
+ );
+
+ TaskbarContainerView.LayoutParams taskbarLayoutParams =
+ new TaskbarContainerView.LayoutParams(mTaskbarSize.x, mTaskbarSize.y);
+ taskbarLayoutParams.gravity = gravity;
+ mTaskbarView.setLayoutParams(taskbarLayoutParams);
+
+ mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
+ }
+
+ /**
+ * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
+ */
+ public void onLauncherResumedOrPaused(boolean isResumed) {
+ long duration = QuickstepAppTransitionManagerImpl.CONTENT_ALPHA_DURATION;
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ if (isResumed) {
+ mAnimator = createAnimToLauncher(null, duration);
+ } else {
+ mAnimator = createAnimToApp(duration);
+ }
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimator = null;
+ }
+ });
+ mAnimator.start();
+ }
+
+ /**
+ * Create Taskbar animation when going from an app to Launcher.
+ * @param toState If known, the state we will end up in when reaching Launcher.
+ */
+ public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
+ PendingAnimation anim = new PendingAnimation(duration);
+ anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(0, duration));
+ if (toState != null) {
+ mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
+ }
+ anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(),
+ getTaskbarScaleOnHome(), LINEAR);
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mIsAnimatingToLauncher = true;
+ mTaskbarView.updateHotseatItemsVisibility();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mIsAnimatingToLauncher = false;
+ }
+ });
+
+ anim.addOnFrameCallback(this::alignRealHotseatWithTaskbar);
+
+ return anim.buildAnim();
+ }
+
+ private Animator createAnimToApp(long duration) {
+ PendingAnimation anim = new PendingAnimation(duration);
+ anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration));
+ anim.addFloat(mTaskbarView, SCALE_PROPERTY, mTaskbarView.getScaleX(), 1f, LINEAR);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mTaskbarView.updateHotseatItemsVisibility();
+ }
+ });
+ return anim.buildAnim();
+ }
+
+ /**
+ * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+ */
+ public void setIsImeVisible(boolean isImeVisible) {
+ mTaskbarVisibilityController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+ }
+
+ /**
+ * Should be called when one or more items in the Hotseat have changed.
+ */
+ public void onHotseatUpdated() {
+ mHotseatController.onHotseatUpdated();
+ }
+
+ /**
+ * @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 mTaskbarView.isEventOverAnyItem(ev);
+ }
+
+ public boolean isDraggingItem() {
+ return mTaskbarView.isDraggingItem();
+ }
+
+ private void dedupeAndUpdateRecentItems() {
+ if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) {
+ return;
+ }
+
+ final int numRecentIcons = mRecentsController.getNumRecentIcons();
+
+ // From most recent to least recently opened.
+ List<Task> dedupedTasksInDescendingOrder = new ArrayList<>();
+ for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) {
+ Task task = mLatestLoadedRecentTasks.get(i);
+ boolean isTaskInHotseat = false;
+ for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) {
+ if (hotseatItem == null) {
+ continue;
+ }
+ ComponentName hotseatActivity = hotseatItem.getTargetComponent();
+ if (hotseatActivity != null && task.key.sourceComponent.getPackageName()
+ .equals(hotseatActivity.getPackageName())) {
+ isTaskInHotseat = true;
+ break;
+ }
+ }
+ if (!isTaskInHotseat) {
+ dedupedTasksInDescendingOrder.add(task);
+ if (dedupedTasksInDescendingOrder.size() == numRecentIcons) {
+ break;
+ }
+ }
+ }
+
+ // TaskbarView expects an array of all the recent tasks to show, in the order to show them.
+ // So we create an array of the proper size, then fill it in such that the most recent items
+ // are at the end. If there aren't enough elements to fill the array, leave them null.
+ Task[] tasksArray = new Task[numRecentIcons];
+ for (int i = 0; i < tasksArray.length; i++) {
+ Task task = i >= dedupedTasksInDescendingOrder.size()
+ ? null
+ : dedupedTasksInDescendingOrder.get(i);
+ tasksArray[tasksArray.length - 1 - i] = task;
+ }
+
+ mTaskbarView.updateRecentTasks(tasksArray);
+ mRecentsController.loadIconsForTasks(tasksArray);
+ }
+
+ /**
+ * @return Whether the given View is in the same window as Taskbar.
+ */
+ public boolean isViewInTaskbar(View v) {
+ return mTaskbarContainerView.isAttachedToWindow()
+ && mTaskbarContainerView.getWindowId().equals(v.getWindowId());
+ }
+
+ /**
+ * Pads the Hotseat to line up exactly with Taskbar's copy of the Hotseat.
+ */
+ public void alignRealHotseatWithTaskbar() {
+ Rect hotseatBounds = new Rect();
+ mTaskbarView.getHotseatBoundsAtScale(getTaskbarScaleOnHome()).roundOut(hotseatBounds);
+ mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top,
+ mTaskbarView.getWidth() - hotseatBounds.right,
+ mTaskbarView.getHeight() - hotseatBounds.bottom);
+ }
+
+ private float getTaskbarScaleOnHome() {
+ return 1f / mTaskbarContainerView.getTaskbarActivityContext().getTaskbarIconScale();
+ }
+
+ /**
+ * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
+ */
+ private void setTaskbarWindowFullscreen(boolean fullscreen) {
+ if (fullscreen) {
+ mWindowLayoutParams.width = MATCH_PARENT;
+ mWindowLayoutParams.height = MATCH_PARENT;
+ } else {
+ mWindowLayoutParams.width = mTaskbarSize.x;
+ mWindowLayoutParams.height = mTaskbarSize.y;
+ }
+ mWindowManager.updateViewLayout(mTaskbarContainerView, mWindowLayoutParams);
+ }
+
+ /**
+ * Contains methods that TaskbarStateHandler can call to interface with TaskbarController.
+ */
+ protected interface TaskbarStateHandlerCallbacks {
+ AnimatedFloat getAlphaTarget();
+ }
+
+ /**
+ * Contains methods that TaskbarVisibilityController can call to interface with
+ * TaskbarController.
+ */
+ protected interface TaskbarVisibilityControllerCallbacks {
+ void updateTaskbarBackgroundAlpha(float alpha);
+ void updateTaskbarVisibilityAlpha(float alpha);
+ }
+
+ /**
+ * Contains methods that TaskbarContainerView can call to interface with TaskbarController.
+ */
+ protected interface TaskbarContainerViewCallbacks {
+ void onViewRemoved();
+ }
+
+ /**
+ * Contains methods that TaskbarView can call to interface with TaskbarController.
+ */
+ protected interface TaskbarViewCallbacks {
+ View.OnClickListener getItemOnClickListener();
+ View.OnLongClickListener getItemOnLongClickListener();
+ int getEmptyHotseatViewVisibility();
+ }
+
+ /**
+ * Contains methods that TaskbarHotseatController can call to interface with TaskbarController.
+ */
+ protected interface TaskbarHotseatControllerCallbacks {
+ void updateHotseatItems(ItemInfo[] hotseatItemInfos);
+ }
+
+ /**
+ * Contains methods that TaskbarRecentsController can call to interface with TaskbarController.
+ */
+ protected interface TaskbarRecentsControllerCallbacks {
+ void updateRecentItems(ArrayList<Task> recentTasks);
+ void updateRecentTaskAtIndex(int taskIndex, Task task);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
new file mode 100644
index 0000000..f51e498
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -0,0 +1,177 @@
+/*
+ * 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.taskbar;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.os.UserHandle;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ClipDescriptionCompat;
+import com.android.systemui.shared.system.LauncherAppsCompat;
+
+/**
+ * Handles long click on Taskbar items to start a system drag and drop operation.
+ */
+public class TaskbarDragController {
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final int mDragIconSize;
+
+ public TaskbarDragController(BaseQuickstepLauncher launcher) {
+ mLauncher = launcher;
+ Resources resources = mLauncher.getResources();
+ mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
+ }
+
+ /**
+ * Attempts to start a system drag and drop operation for the given View, using its tag to
+ * generate the ClipDescription and Intent.
+ * @return Whether {@link View#startDragAndDrop} started successfully.
+ */
+ protected boolean startSystemDragOnLongClick(View view) {
+ if (!(view instanceof BubbleTextView)) {
+ return false;
+ }
+
+ BubbleTextView btv = (BubbleTextView) view;
+
+ View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
+ @Override
+ public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
+ shadowSize.set(mDragIconSize, mDragIconSize);
+ // TODO: should be based on last touch point on the icon.
+ shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
+ }
+
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ canvas.save();
+ float scale = (float) mDragIconSize / btv.getIconSize();
+ canvas.scale(scale, scale);
+ btv.getIcon().draw(canvas);
+ canvas.restore();
+ }
+ };
+
+ Object tag = view.getTag();
+ ClipDescription clipDescription = null;
+ Intent intent = null;
+ if (tag instanceof WorkspaceItemInfo) {
+ WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
+ LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class);
+ clipDescription = new ClipDescription(item.title,
+ new String[] {
+ item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+ ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT
+ : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY
+ });
+ intent = new Intent();
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
+ intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
+ } else {
+ intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+ LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps,
+ item.getIntent().getComponent(), null, item.user));
+ }
+ intent.putExtra(Intent.EXTRA_USER, item.user);
+ } else if (tag instanceof Task) {
+ Task task = (Task) tag;
+ clipDescription = new ClipDescription(task.titleDescription,
+ new String[] {
+ ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK
+ });
+ intent = new Intent();
+ intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id);
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId));
+ }
+
+ if (clipDescription != null && intent != null) {
+ ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent));
+ view.setOnDragListener(getDraggedViewDragListener());
+ return view.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
+ View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE);
+ }
+ return false;
+ }
+
+ /**
+ * Starts a drag and drop operation that controls a real Workspace (Hotseat) view.
+ * @param view The Taskbar item that was long clicked.
+ * @return Whether {@link View#startDragAndDrop} started successfully.
+ */
+ protected boolean startWorkspaceDragOnLongClick(View view) {
+ View.DragShadowBuilder transparentShadowBuilder = new View.DragShadowBuilder(view) {
+ private static final int ARBITRARY_SHADOW_SIZE = 10;
+
+ @Override
+ public void onDrawShadow(Canvas canvas) {
+ }
+
+ @Override
+ public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
+ outShadowSize.set(ARBITRARY_SHADOW_SIZE, ARBITRARY_SHADOW_SIZE);
+ outShadowTouchPoint.set(ARBITRARY_SHADOW_SIZE / 2, ARBITRARY_SHADOW_SIZE / 2);
+ }
+ };
+
+ TaskbarDragListener taskbarDragListener = new TaskbarDragListener(mLauncher,
+ (ItemInfo) view.getTag());
+ if (view.startDragAndDrop(new ClipData("", new String[] {taskbarDragListener.getMimeType()},
+ new ClipData.Item("")),
+ transparentShadowBuilder, null /* localState */, View.DRAG_FLAG_GLOBAL)) {
+ view.setOnDragListener(getDraggedViewDragListener());
+ taskbarDragListener.init(mLauncher.getDragLayer());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Hide the original Taskbar item while it is being dragged.
+ */
+ private View.OnDragListener getDraggedViewDragListener() {
+ return (view, dragEvent) -> {
+ switch (dragEvent.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ view.setVisibility(INVISIBLE);
+ return true;
+ case DragEvent.ACTION_DRAG_ENDED:
+ view.setVisibility(VISIBLE);
+ view.setOnDragListener(null);
+ return true;
+ }
+ return false;
+ };
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java
new file mode 100644
index 0000000..9d203fb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragListener.java
@@ -0,0 +1,98 @@
+/*
+ * 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.taskbar;
+
+import android.content.ClipDescription;
+import android.graphics.Point;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.ItemInfo;
+
+import java.util.UUID;
+
+/**
+ * Listens to system drag and drop events initated by the Taskbar, and forwards them to Launcher's
+ * internal DragController to move Hotseat items.
+ */
+public class TaskbarDragListener implements View.OnDragListener {
+
+ private static final String MIME_TYPE_PREFIX = "com.android.launcher3.taskbar.drag_and_drop/";
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final ItemInfo mDraggedItem;
+ // Randomly generated id used to verify the drag event.
+ private final String mId;
+
+ // Initialized in init().
+ DragLayer mDragLayer;
+
+ /**
+ * @param draggedItem The info of the item that was long clicked, which we will use to find
+ * the equivalent match on Hotseat to drag internally.
+ */
+ public TaskbarDragListener(BaseQuickstepLauncher launcher, ItemInfo draggedItem) {
+ mLauncher = launcher;
+ mDraggedItem = draggedItem;
+ mId = UUID.randomUUID().toString();
+ }
+
+ protected void init(DragLayer dragLayer) {
+ mDragLayer = dragLayer;
+ mDragLayer.setOnDragListener(this);
+ }
+
+ private void cleanup() {
+ mDragLayer.setOnDragListener(null);
+ mLauncher.setNextWorkspaceDragOptions(null);
+ }
+
+ /**
+ * Returns a randomly generated id used to verify the drag event.
+ */
+ protected String getMimeType() {
+ return MIME_TYPE_PREFIX + mId;
+ }
+
+ @Override
+ public boolean onDrag(View dragLayer, DragEvent dragEvent) {
+ ClipDescription clipDescription = dragEvent.getClipDescription();
+ if (dragEvent.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+ if (clipDescription == null || !clipDescription.hasMimeType(getMimeType())) {
+ // We didn't initiate this drag, ignore.
+ cleanup();
+ return false;
+ }
+ View hotseatView = mLauncher.getHotseat().getFirstItemMatch(
+ (info, view) -> info == mDraggedItem);
+ if (hotseatView == null) {
+ cleanup();
+ return false;
+ }
+ DragOptions dragOptions = new DragOptions();
+ dragOptions.simulatedDndStartPoint = new Point((int) dragEvent.getX(),
+ (int) dragEvent.getY());
+ mLauncher.setNextWorkspaceDragOptions(dragOptions);
+ hotseatView.performLongClick();
+ } else if (dragEvent.getAction() == DragEvent.ACTION_DRAG_ENDED) {
+ cleanup();
+ }
+ return mLauncher.getDragController().onDragEvent(dragEvent);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
new file mode 100644
index 0000000..b1bafdb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -0,0 +1,94 @@
+/*
+ * 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.taskbar;
+
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's Hotseat items.
+ */
+public class TaskbarHotseatController {
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final Hotseat mHotseat;
+ private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks;
+ private final int mNumHotseatIcons;
+
+ private final DragController.DragListener mDragListener = new DragController.DragListener() {
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ }
+
+ @Override
+ public void onDragEnd() {
+ onHotseatUpdated();
+ }
+ };
+
+ public TaskbarHotseatController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) {
+ mLauncher = launcher;
+ mHotseat = mLauncher.getHotseat();
+ mTaskbarCallbacks = taskbarCallbacks;
+ mNumHotseatIcons = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+ }
+
+ protected void init() {
+ mLauncher.getDragController().addDragListener(mDragListener);
+ onHotseatUpdated();
+ }
+
+ protected void cleanup() {
+ mLauncher.getDragController().removeDragListener(mDragListener);
+ }
+
+ /**
+ * Called when any Hotseat item changes, and reports the new list of items to TaskbarController.
+ */
+ protected void onHotseatUpdated() {
+ ShortcutAndWidgetContainer shortcutsAndWidgets = mHotseat.getShortcutsAndWidgets();
+ ItemInfo[] hotseatItemInfos = new ItemInfo[mNumHotseatIcons];
+ for (int i = 0; i < shortcutsAndWidgets.getChildCount(); i++) {
+ View child = shortcutsAndWidgets.getChildAt(i);
+ Object tag = shortcutsAndWidgets.getChildAt(i).getTag();
+ if (tag instanceof ItemInfo) {
+ ItemInfo itemInfo = (ItemInfo) tag;
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ // Since the hotseat might be laid out vertically or horizontally, use whichever
+ // index is higher.
+ int index = Math.max(lp.cellX, lp.cellY);
+ if (0 <= index && index < hotseatItemInfos.length) {
+ hotseatItemInfos[index] = itemInfo;
+ }
+ }
+ }
+
+ mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos);
+ }
+
+ protected int getNumHotseatIcons() {
+ return mNumHotseatIcons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
new file mode 100644
index 0000000..4256d2b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
@@ -0,0 +1,120 @@
+/*
+ * 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.taskbar;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.util.CancellableTask;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.ArrayList;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's Recent items.
+ */
+public class TaskbarRecentsController {
+
+ private final int mNumRecentIcons = 2;
+ private final BaseQuickstepLauncher mLauncher;
+ private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks;
+ private final RecentsModel mRecentsModel;
+
+ private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskStackChanged() {
+ reloadRecentTasksIfNeeded();
+ }
+ };
+
+ // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?)
+
+ // Used to keep track of the last requested task list id, so that we do not request to load the
+ // tasks again if we have already requested it and the task list has not changed
+ private int mTaskListChangeId = -1;
+
+ // The current background requests to load the task icons
+ private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons];
+
+ private boolean mIsAlive;
+
+ public TaskbarRecentsController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) {
+ mLauncher = launcher;
+ mTaskbarCallbacks = taskbarCallbacks;
+ mRecentsModel = RecentsModel.INSTANCE.get(mLauncher);
+ }
+
+ protected void init() {
+ mIsAlive = true;
+ TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener);
+ reloadRecentTasksIfNeeded();
+ }
+
+ protected void cleanup() {
+ mIsAlive = false;
+ TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+ mTaskStackChangeListener);
+ cancelAllPendingIconLoadTasks();
+ }
+
+ private void reloadRecentTasksIfNeeded() {
+ if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) {
+ mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged);
+ }
+ }
+
+ private void cancelAllPendingIconLoadTasks() {
+ for (int i = 0; i < mIconLoadRequests.length; i++) {
+ if (mIconLoadRequests[i] != null) {
+ mIconLoadRequests[i].cancel();
+ }
+ mIconLoadRequests[i] = null;
+ }
+ }
+
+ private void onRecentTasksChanged(ArrayList<Task> tasks) {
+ if (mIsAlive) {
+ mTaskbarCallbacks.updateRecentItems(tasks);
+ }
+ }
+
+ /**
+ * For each Task, loads its icon from the cache in the background, then calls
+ * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}.
+ */
+ protected void loadIconsForTasks(Task[] tasks) {
+ cancelAllPendingIconLoadTasks();
+ for (int i = 0; i < tasks.length; i++) {
+ Task task = tasks[i];
+ if (task == null) {
+ continue;
+ }
+ final int taskIndex = i;
+ mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground(
+ task, updatedTask -> onTaskIconLoaded(task, taskIndex));
+ }
+ }
+
+ private void onTaskIconLoaded(Task task, int taskIndex) {
+ mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task);
+ }
+
+ protected int getNumRecentIcons() {
+ return mNumRecentIcons;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
new file mode 100644
index 0000000..b4b5d8b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherState.TASKBAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.AnimatedFloat;
+
+/**
+ * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
+ * isn't present (i.e. {@link #setTaskbarCallbacks} is never called).
+ */
+public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
+
+ private final BaseQuickstepLauncher mLauncher;
+
+ // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
+ private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
+
+ public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
+ mLauncher = launcher;
+ }
+
+ public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
+ mTaskbarCallbacks = callbacks;
+ }
+
+ @Override
+ public void setState(LauncherState state) {
+ if (mTaskbarCallbacks == null) {
+ return;
+ }
+
+ AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+ boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
+ alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
+ }
+
+ @Override
+ public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+ PendingAnimation animation) {
+ if (mTaskbarCallbacks == null) {
+ return;
+ }
+ if (config.hasAnimationFlag(SKIP_TASKBAR)) {
+ return;
+ }
+
+ AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+ boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
+ animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f,
+ config.getInterpolator(ANIM_TASKBAR_FADE, Interpolators.LINEAR));
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
new file mode 100644
index 0000000..a729e77
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -0,0 +1,479 @@
+/*
+ * 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.taskbar;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.LinearLayout;
+
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.folder.FolderIcon;
+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.views.ActivityContext;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
+ */
+public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent {
+
+ private final ColorDrawable mBackgroundDrawable;
+ private final int mItemMarginLeftRight;
+ private final int mIconTouchSize;
+ private final boolean mIsRtl;
+ private final int mTouchSlop;
+ private final RectF mTempDelegateBounds = new RectF();
+ private final RectF mDelegateSlopBounds = new RectF();
+ private final int[] mTempOutLocation = new int[2];
+ private final Matrix mTempMatrix = new Matrix();
+
+ // Initialized in TaskbarController constructor.
+ private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+
+ // Initialized in init().
+ private int mHotseatStartIndex;
+ private int mHotseatEndIndex;
+ private View mHotseatRecentsDivider;
+ private int mRecentsStartIndex;
+ private int mRecentsEndIndex;
+
+ // Delegate touches to the closest view if within mIconTouchSize.
+ private boolean mDelegateTargeted;
+ private View mDelegateView;
+
+ private boolean mIsDraggingItem;
+ // Only non-null when the corresponding Folder is open.
+ private @Nullable FolderIcon mLeaveBehindFolderIcon;
+
+ public TaskbarView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+
+ Resources resources = getResources();
+ mBackgroundDrawable = (ColorDrawable) getBackground();
+ mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+ mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
+ mIsRtl = Utilities.isRtl(resources);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ }
+
+ protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
+ mControllerCallbacks = taskbarViewCallbacks;
+ }
+
+ protected void init(int numHotseatIcons, int numRecentIcons) {
+ mHotseatStartIndex = 0;
+ mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
+ updateHotseatItems(new ItemInfo[numHotseatIcons]);
+
+ int dividerIndex = mHotseatEndIndex + 1;
+ mHotseatRecentsDivider = addDivider(dividerIndex);
+
+ mRecentsStartIndex = dividerIndex + 1;
+ mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1;
+ updateRecentTasks(new Task[numRecentIcons]);
+ }
+
+ protected void cleanup() {
+ removeAllViews();
+ }
+
+ /**
+ * Sets the alpha of the background color behind all the Taskbar contents.
+ * @param alpha 0 is fully transparent, 1 is fully opaque.
+ */
+ public void setBackgroundAlpha(float alpha) {
+ mBackgroundDrawable.setAlpha((int) (alpha * 255));
+ }
+
+ /**
+ * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
+ */
+ protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+ for (int i = 0; i < hotseatItemInfos.length; i++) {
+ ItemInfo hotseatItemInfo = hotseatItemInfos[!mIsRtl ? i
+ : hotseatItemInfos.length - i - 1];
+ int hotseatIndex = mHotseatStartIndex + i;
+ View hotseatView = getChildAt(hotseatIndex);
+
+ // Replace any Hotseat views with the appropriate type if it's not already that type.
+ final int expectedLayoutResId;
+ boolean isFolder = false;
+ boolean needsReinflate = false;
+ if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) {
+ expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
+ } else if (hotseatItemInfo instanceof FolderInfo) {
+ expectedLayoutResId = R.layout.folder_icon;
+ isFolder = true;
+ // Unlike for BubbleTextView, we can't reapply a new FolderInfo after inflation, so
+ // if the info changes we need to reinflate. This should only happen if a new folder
+ // is dragged to the position that another folder previously existed.
+ needsReinflate = hotseatView != null && hotseatView.getTag() != hotseatItemInfo;
+ } else {
+ expectedLayoutResId = R.layout.taskbar_app_icon;
+ }
+ if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
+ || needsReinflate) {
+ removeView(hotseatView);
+ TaskbarActivityContext activityContext =
+ ActivityContext.lookupContext(getContext());
+ if (isFolder) {
+ FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
+ FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
+ activityContext, this, folderInfo);
+ folderIcon.setTextVisible(false);
+ hotseatView = folderIcon;
+ } else {
+ hotseatView = inflate(expectedLayoutResId);
+ }
+ int iconSize = activityContext.getDeviceProfile().iconSizePx;
+ LayoutParams lp = new LayoutParams(iconSize, iconSize);
+ lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+ addView(hotseatView, hotseatIndex, lp);
+ }
+
+ // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
+ if (hotseatView instanceof BubbleTextView
+ && hotseatItemInfo instanceof WorkspaceItemInfo) {
+ ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
+ (WorkspaceItemInfo) hotseatItemInfo);
+ hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+ hotseatView.setOnLongClickListener(
+ mControllerCallbacks.getItemOnLongClickListener());
+ } else if (isFolder) {
+ hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+ hotseatView.setOnLongClickListener(
+ mControllerCallbacks.getItemOnLongClickListener());
+ } else {
+ hotseatView.setOnClickListener(null);
+ hotseatView.setOnLongClickListener(null);
+ hotseatView.setTag(null);
+ }
+ updateHotseatItemVisibility(hotseatView);
+ }
+
+ updateHotseatRecentsDividerVisibility();
+ }
+
+ protected void updateHotseatItemsVisibility() {
+ for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
+ updateHotseatItemVisibility(getChildAt(i));
+ }
+ }
+
+ private void updateHotseatItemVisibility(View hotseatView) {
+ if (hotseatView.getTag() != null) {
+ hotseatView.setVisibility(VISIBLE);
+ } else {
+ int oldVisibility = hotseatView.getVisibility();
+ int newVisibility = mControllerCallbacks.getEmptyHotseatViewVisibility();
+ hotseatView.setVisibility(newVisibility);
+ if (oldVisibility == GONE && newVisibility != GONE) {
+ // By default, the layout transition only runs when going to VISIBLE,
+ // but we want it to run when going to GONE to INVISIBLE as well.
+ getLayoutTransition().showChild(this, hotseatView, oldVisibility);
+ }
+ }
+ }
+
+ private View addDivider(int dividerIndex) {
+ View divider = inflate(R.layout.taskbar_divider);
+ addView(divider, dividerIndex);
+ return divider;
+ }
+
+ /**
+ * Inflates/binds the Recents items to show in the Taskbar given their Tasks.
+ */
+ protected void updateRecentTasks(Task[] tasks) {
+ for (int i = 0; i < tasks.length; i++) {
+ Task task = tasks[i];
+ int recentsIndex = mRecentsStartIndex + i;
+ View recentsView = getChildAt(recentsIndex);
+
+ // Inflate empty icon Views.
+ if (recentsView == null) {
+ BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon);
+ LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
+ lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+ recentsView = btv;
+ addView(recentsView, recentsIndex, lp);
+ }
+
+ // Apply the Task, or hide the view if there is none for a given index.
+ if (recentsView instanceof BubbleTextView && task != null) {
+ applyTaskToBubbleTextView((BubbleTextView) recentsView, task);
+ recentsView.setVisibility(VISIBLE);
+ recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+ recentsView.setOnLongClickListener(
+ mControllerCallbacks.getItemOnLongClickListener());
+ } else {
+ recentsView.setVisibility(GONE);
+ recentsView.setOnClickListener(null);
+ recentsView.setOnLongClickListener(null);
+ }
+ }
+
+ updateHotseatRecentsDividerVisibility();
+ }
+
+ private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) {
+ if (task.icon != null) {
+ Drawable icon = task.icon.getConstantState().newDrawable().mutate();
+ btv.applyIconAndLabel(icon, task.titleDescription);
+ }
+ btv.setTag(task);
+ }
+
+ protected void updateRecentTaskAtIndex(int taskIndex, Task task) {
+ View taskView = getChildAt(mRecentsStartIndex + taskIndex);
+ if (taskView instanceof BubbleTextView) {
+ applyTaskToBubbleTextView((BubbleTextView) taskView, task);
+ }
+ }
+
+ /**
+ * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in
+ * each, otherwise make it GONE.
+ */
+ private void updateHotseatRecentsDividerVisibility() {
+ if (mHotseatRecentsDivider == null) {
+ return;
+ }
+
+ boolean hasAtLeastOneHotseatItem = false;
+ for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
+ if (getChildAt(i).getVisibility() != GONE) {
+ hasAtLeastOneHotseatItem = true;
+ break;
+ }
+ }
+
+ boolean hasAtLeastOneRecentItem = false;
+ for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) {
+ if (getChildAt(i).getVisibility() != GONE) {
+ hasAtLeastOneRecentItem = true;
+ break;
+ }
+ }
+
+ mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem
+ ? VISIBLE : GONE);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean handled = delegateTouchIfNecessary(event);
+ return super.onTouchEvent(event) || handled;
+ }
+
+ /**
+ * User touched the Taskbar background. Determine whether the touch is close enough to a view
+ * that we should forward the touches to it.
+ * @return Whether a delegate view was chosen and it handled the touch event.
+ */
+ private boolean delegateTouchIfNecessary(MotionEvent event) {
+ final float x = event.getX();
+ final float y = event.getY();
+ if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
+ View delegateView = findDelegateView(x, y);
+ if (delegateView != null) {
+ mDelegateTargeted = true;
+ mDelegateView = delegateView;
+ mDelegateSlopBounds.set(mTempDelegateBounds);
+ mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
+ }
+ }
+
+ boolean sendToDelegate = mDelegateTargeted;
+ boolean inBounds = true;
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_MOVE:
+ inBounds = mDelegateSlopBounds.contains(x, y);
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mDelegateTargeted = false;
+ break;
+ }
+
+ boolean handled = false;
+ if (sendToDelegate) {
+ if (inBounds) {
+ // Offset event coordinates to be inside the target view
+ event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
+ } else {
+ // Offset event coordinates to be outside the target view (in case it does
+ // something like tracking pressed state)
+ event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
+ }
+ handled = mDelegateView.dispatchTouchEvent(event);
+ // Cleanup if this was the last event to send to the delegate.
+ if (!mDelegateTargeted) {
+ mDelegateView = null;
+ }
+ }
+ return handled;
+ }
+
+ /**
+ * Return an item whose touch bounds contain the given coordinates,
+ * or null if no such item exists.
+ *
+ * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view.
+ */
+ private @Nullable View findDelegateView(float x, float y) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ if (!child.isShown() || !child.isClickable()) {
+ continue;
+ }
+ int childCenterX = child.getLeft() + child.getWidth() / 2;
+ int childCenterY = child.getTop() + child.getHeight() / 2;
+ mTempDelegateBounds.set(
+ childCenterX - mIconTouchSize / 2f,
+ childCenterY - mIconTouchSize / 2f,
+ childCenterX + mIconTouchSize / 2f,
+ childCenterY + mIconTouchSize / 2f);
+ if (mTempDelegateBounds.contains(x, y)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
+ * touch bounds.
+ */
+ public boolean isEventOverAnyItem(MotionEvent ev) {
+ getLocationOnScreen(mTempOutLocation);
+ float xInOurCoordinates = ev.getX() - mTempOutLocation[0];
+ float yInOurCoorindates = ev.getY() - mTempOutLocation[1];
+ return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
+ }
+
+ @Override
+ public boolean onDragEvent(DragEvent event) {
+ switch (event.getAction()) {
+ case DragEvent.ACTION_DRAG_STARTED:
+ mIsDraggingItem = true;
+ AbstractFloatingView.closeAllOpenViews(ActivityContext.lookupContext(getContext()));
+ return true;
+ case DragEvent.ACTION_DRAG_ENDED:
+ mIsDraggingItem = false;
+ break;
+ }
+ return super.onDragEvent(event);
+ }
+
+ public boolean isDraggingItem() {
+ return mIsDraggingItem;
+ }
+
+ /**
+ * @return The bounding box of where the hotseat elements will be when we reach the given scale.
+ */
+ protected RectF getHotseatBoundsAtScale(float taskbarViewScale) {
+ View firstHotseatView = null, lastHotseatView = null;
+ for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
+ View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ if (firstHotseatView == null) {
+ firstHotseatView = child;
+ }
+ lastHotseatView = child;
+ }
+ }
+ if (firstHotseatView == null || lastHotseatView == null) {
+ return new RectF();
+ }
+ View leftmostHotseatView = !mIsRtl ? firstHotseatView : lastHotseatView;
+ View rightmostHotseatView = !mIsRtl ? lastHotseatView : firstHotseatView;
+ RectF hotseatBounds = new RectF(
+ leftmostHotseatView.getLeft() - mItemMarginLeftRight,
+ leftmostHotseatView.getTop(),
+ rightmostHotseatView.getRight() + mItemMarginLeftRight,
+ rightmostHotseatView.getBottom());
+ mTempMatrix.setScale(taskbarViewScale, taskbarViewScale, getPivotX(), getPivotY());
+ mTempMatrix.mapRect(hotseatBounds);
+ return hotseatBounds;
+ }
+
+ // FolderIconParent implemented methods.
+
+ @Override
+ public void drawFolderLeaveBehindForIcon(FolderIcon child) {
+ mLeaveBehindFolderIcon = child;
+ invalidate();
+ }
+
+ @Override
+ public void clearFolderLeaveBehind(FolderIcon child) {
+ mLeaveBehindFolderIcon = null;
+ invalidate();
+ }
+
+ // End FolderIconParent implemented methods.
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mLeaveBehindFolderIcon != null) {
+ canvas.save();
+ canvas.translate(mLeaveBehindFolderIcon.getLeft(), mLeaveBehindFolderIcon.getTop());
+ mLeaveBehindFolderIcon.getFolderBackground().drawLeaveBehind(canvas);
+ canvas.restore();
+ }
+ }
+
+ private View inflate(@LayoutRes int layoutResId) {
+ TaskbarActivityContext taskbarActivityContext = ActivityContext.lookupContext(getContext());
+ return taskbarActivityContext.getLayoutInflater().inflate(layoutResId, this, false);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
new file mode 100644
index 0000000..6d20d97
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
@@ -0,0 +1,98 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherState.TASKBAR;
+
+import android.animation.Animator;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's alpha based on LauncherState, whether
+ * Launcher is in the foreground, etc.
+ */
+public class TaskbarVisibilityController {
+
+ private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
+
+ private final BaseQuickstepLauncher mLauncher;
+ private final TaskbarController.TaskbarVisibilityControllerCallbacks mTaskbarCallbacks;
+
+ // Background alpha.
+ private AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
+ this::onTaskbarBackgroundAlphaChanged);
+
+ // Overall visibility.
+ private AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
+ this::updateVisibilityAlpha);
+ private AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
+ this::updateVisibilityAlpha);
+
+ public TaskbarVisibilityController(BaseQuickstepLauncher launcher,
+ TaskbarController.TaskbarVisibilityControllerCallbacks taskbarCallbacks) {
+ mLauncher = launcher;
+ mTaskbarCallbacks = taskbarCallbacks;
+ }
+
+ protected void init() {
+ mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f);
+ boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState()
+ .getVisibleElements(mLauncher) & TASKBAR) != 0;
+ mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f);
+ boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags()
+ & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0;
+ mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f);
+
+ onTaskbarBackgroundAlphaChanged();
+ updateVisibilityAlpha();
+ }
+
+ protected void cleanup() {
+ }
+
+ protected AnimatedFloat getTaskbarVisibilityForLauncherState() {
+ return mTaskbarVisibilityAlphaForLauncherState;
+ }
+
+ protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
+ return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
+ .setDuration(duration);
+ }
+
+ protected void animateToVisibilityForIme(float toAlpha) {
+ mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha)
+ .setDuration(IME_VISIBILITY_ALPHA_DURATION).start();
+ }
+
+ private void onTaskbarBackgroundAlphaChanged() {
+ mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
+ updateVisibilityAlpha();
+ }
+
+ private void updateVisibilityAlpha() {
+ // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
+ // assumption being that Taskbar should always be visible regardless of the current
+ // LauncherState if Launcher is paused.
+ float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
+ mTaskbarVisibilityAlphaForLauncherState.value);
+ float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
+ mTaskbarCallbacks.updateTaskbarVisibilityAlpha(alphaDueToLauncher * alphaDueToOther);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index aad7e17..0f13ef9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -30,6 +30,7 @@
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_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;
@@ -73,6 +74,8 @@
SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
SCRIM_MULTIPLIER.set(scrim, 1f);
getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
+ RECENTS_GRID_PROGRESS.set(mRecentsView, state.displayOverviewTasksAsGrid(mLauncher)
+ ? 1f : 0f);
}
@Override
@@ -117,6 +120,8 @@
mRecentsView, getTaskModalnessProperty(),
toState.getOverviewModalness(),
config.getInterpolator(ANIM_OVERVIEW_MODAL, LINEAR));
+ setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS,
+ toState.displayOverviewTasksAsGrid(mLauncher) ? 1f : 0f, LINEAR);
}
abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
index 010694b..bb1f6fc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
@@ -60,6 +60,14 @@
mListeners.add(r);
}
+ @Override
+ public void removeChangeListener(Runnable r) {
+ if (mListeners == null) {
+ return;
+ }
+ mListeners.remove(r);
+ }
+
private void registerDeviceConfigChangedListener(Context context) {
DeviceConfig.addOnPropertiesChangedListener(
NAMESPACE_LAUNCHER,
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
similarity index 83%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
rename to quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 597c17b..d330a68 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.PIN_PREDICTION;
import static com.android.launcher3.graphics.IconShape.getShape;
import android.content.Context;
@@ -29,7 +28,6 @@
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityNodeInfo;
import androidx.core.graphics.ColorUtils;
@@ -37,9 +35,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.graphics.IconPalette;
-import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
@@ -53,8 +49,7 @@
/**
* A BubbleTextView with a ring around it's drawable
*/
-public class PredictedAppIcon extends DoubleShadowBubbleTextView implements
- LauncherAccessibilityDelegate.AccessibilityActionHandler {
+public class PredictedAppIcon extends DoubleShadowBubbleTextView {
private static final int RING_SHADOW_COLOR = 0x99000000;
private static final float RING_EFFECT_RATIO = 0.095f;
@@ -148,29 +143,6 @@
}
@Override
- public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) {
- if (!mIsPinned) {
- accessibilityNodeInfo.addAction(
- new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
- getContext().getText(R.string.pin_prediction)));
- }
- }
-
- @Override
- public boolean performAccessibilityAction(int action, ItemInfo info) {
- QuickstepLauncher launcher = Launcher.cast(Launcher.getLauncher(getContext()));
- if (action == PIN_PREDICTION) {
- if (launcher == null || launcher.getHotseatPredictionController() == null) {
- return false;
- }
- HotseatPredictionController controller = launcher.getHotseatPredictionController();
- controller.pinPrediction(info);
- return true;
- }
- return false;
- }
-
- @Override
public void getIconBounds(Rect outBounds) {
super.getIconBounds(outBounds);
if (!mIsPinned && !mIsDrawingDot) {
@@ -179,12 +151,19 @@
}
}
+ public boolean isPinned() {
+ return mIsPinned;
+ }
+
private int getOutlineOffsetX() {
return (getMeasuredWidth() / 2) - mNormalizedIconRadius;
}
private int getOutlineOffsetY() {
- return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+ if (mDisplay != DISPLAY_TASKBAR) {
+ return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+ }
+ return (getMeasuredHeight() / 2) - mNormalizedIconRadius;
}
private void drawEffect(Canvas canvas, boolean isBadged) {
@@ -249,17 +228,13 @@
*/
public static class PredictedIconOutlineDrawing extends CellLayout.DelegatedCellDrawing {
- private int mOffsetX;
- private int mOffsetY;
- private int mIconRadius;
- private Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final PredictedAppIcon mIcon;
+ private final Paint mOutlinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
public PredictedIconOutlineDrawing(int cellX, int cellY, PredictedAppIcon icon) {
mDelegateCellX = cellX;
mDelegateCellY = cellY;
- mOffsetX = icon.getOutlineOffsetX();
- mOffsetY = icon.getOutlineOffsetY();
- mIconRadius = icon.mNormalizedIconRadius;
+ mIcon = icon;
mOutlinePaint.setStyle(Paint.Style.FILL);
mOutlinePaint.setColor(Color.argb(24, 245, 245, 245));
}
@@ -269,7 +244,8 @@
*/
@Override
public void drawUnderItem(Canvas canvas) {
- getShape().drawShape(canvas, mOffsetX, mOffsetY, mIconRadius, mOutlinePaint);
+ getShape().drawShape(canvas, mIcon.getOutlineOffsetX(), mIcon.getOutlineOffsetY(),
+ mIcon.mNormalizedIconRadius, mOutlinePaint);
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
rename to quickstep/src/com/android/launcher3/uioverrides/PredictedAppIconInflater.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
similarity index 64%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
rename to quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 9a03d92..b009629 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,11 @@
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
@@ -26,54 +31,50 @@
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.util.Executors.MAIN_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
-import android.os.Bundle;
-import android.util.Log;
import android.view.View;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.BaseQuickstepLauncher;
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.Workspace;
-import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.Folder;
+import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
-import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory;
-import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
import com.android.launcher3.uioverrides.touchcontrollers.NavBarToHomeTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonNavbarToOverviewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.NoButtonQuickSwitchTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
-import com.android.launcher3.util.IntArray;
+import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
+import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.util.QuickstepOnboardingPrefs;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -81,7 +82,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.OptionalInt;
+import java.util.Objects;
import java.util.stream.Stream;
public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -93,33 +94,63 @@
public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.createPredictor();
- }
- }
+ private FixedContainerItems mAllAppsPredictions;
+ private HotseatPredictionController mHotseatPredictionController;
@Override
protected void setupViews() {
super.setupViews();
- if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
- mHotseatPredictionController = new HotseatPredictionController(this);
- }
+ mHotseatPredictionController = new HotseatPredictionController(this);
}
@Override
protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+ // If the app launch is from any of the surfaces in AllApps then add the InstanceId from
+ // LiveSearchManager to recreate the AllApps session on the server side.
+ if (mAllAppsSessionLogId != null && ALL_APPS.equals(
+ getStateManager().getCurrentStableState())) {
+ instanceId = mAllAppsSessionLogId;
+ }
+
StatsLogger logger = getStatsLogManager()
.logger().withItemInfo(info).withInstanceId(instanceId);
- OptionalInt allAppsRank = PredictionUiStateManager.INSTANCE.get(this).getAllAppsRank(info);
- allAppsRank.ifPresent(logger::withRank);
+
+ if (mAllAppsPredictions != null
+ && (info.itemType == ITEM_TYPE_APPLICATION
+ || info.itemType == ITEM_TYPE_SHORTCUT
+ || info.itemType == ITEM_TYPE_DEEP_SHORTCUT)) {
+ int count = mAllAppsPredictions.items.size();
+ for (int i = 0; i < count; i++) {
+ ItemInfo targetInfo = mAllAppsPredictions.items.get(i);
+ if (targetInfo.itemType == info.itemType
+ && targetInfo.user.equals(info.user)
+ && Objects.equals(targetInfo.getIntent(), info.getIntent())) {
+ logger.withRank(i);
+ break;
+ }
+
+ }
+ }
logger.log(LAUNCHER_APP_LAUNCH_TAP);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
- }
+ mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
+ }
+
+ @Override
+ protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
+ return new QuickstepAccessibilityDelegate(this);
+ }
+
+ /**
+ * Returns Prediction controller for hybrid hotseat
+ */
+ public HotseatPredictionController getHotseatPredictionController() {
+ return mHotseatPredictionController;
+ }
+
+ @Override
+ protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs) {
+ return new QuickstepOnboardingPrefs(this, sharedPrefs);
}
@Override
@@ -129,12 +160,10 @@
}
@Override
- public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
- @Nullable String sourceContainer) {
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.setPauseUIUpdate(true);
- }
- return super.startActivitySafely(v, intent, item, sourceContainer);
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ // Only pause is taskbar controller is not present
+ mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
+ return super.startActivitySafely(v, intent, item);
}
@Override
@@ -145,36 +174,22 @@
onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
}
- if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
+ if (((changeBits & ACTIVITY_STATE_STARTED) != 0
|| (changeBits & getActivityFlags() & ACTIVITY_STATE_DEFERRED_RESUMED) != 0)) {
mHotseatPredictionController.setPauseUIUpdate(false);
}
}
@Override
- public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo) {
- super.folderCreatedFromItem(folder, itemInfo);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.folderCreatedFromWorkspaceItem(itemInfo, folder.getInfo());
- }
- }
-
- @Override
- public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {
- super.folderConvertedToItem(folder, itemInfo);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.folderConvertedToWorkspaceItem(itemInfo, folder.getInfo());
- }
+ protected void showAllAppsFromIntent(boolean alreadyOnHome) {
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY);
+ super.showAllAppsFromIntent(alreadyOnHome);
}
@Override
public Stream<SystemShortcut.Factory> getSupportedShortcuts() {
- if (mHotseatPredictionController != null) {
- return Stream.concat(super.getSupportedShortcuts(),
- Stream.of(mHotseatPredictionController));
- } else {
- return super.getSupportedShortcuts();
- }
+ return Stream.concat(
+ super.getSupportedShortcuts(), Stream.of(mHotseatPredictionController));
}
/**
@@ -196,20 +211,32 @@
}
@Override
- public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
- super.bindPredictedItems(appInfos, ranks);
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.showCachedItems(appInfos, ranks);
+ public void bindExtraContainerItems(FixedContainerItems item) {
+ if (item.containerId == Favorites.CONTAINER_PREDICTION) {
+ mAllAppsPredictions = item;
+ getAppsView().getFloatingHeaderView().findFixedRowByType(PredictionRowView.class)
+ .setPredictedApps(item.items);
+ } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
+ mHotseatPredictionController.setPredictedItems(item);
+ } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
+ getPopupDataProvider().setRecommendedWidgets(item.items);
+ }
+ }
+
+ @Override
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+ super.bindWorkspaceItemsChanged(updated);
+ if (getTaskbarController() != null && updated.stream()
+ .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
+ getTaskbarController().onHotseatUpdated();
}
}
@Override
public void onDestroy() {
super.onDestroy();
- if (mHotseatPredictionController != null) {
- mHotseatPredictionController.destroy();
- mHotseatPredictionController = null;
- }
+ getAppsView().getSearchUiManager().destroy();
+ mHotseatPredictionController.destroy();
}
@Override
@@ -219,18 +246,13 @@
switch (state.ordinal) {
case HINT_STATE_ORDINAL: {
Workspace workspace = getWorkspace();
- boolean willMoveScreens = workspace.getNextPage() != Workspace.DEFAULT_PAGE;
- getStateManager().goToState(NORMAL, true,
- willMoveScreens ? null : getScrimView()::startDragHandleEducationAnim);
- if (willMoveScreens) {
+ getStateManager().goToState(NORMAL);
+ if (workspace.getNextPage() != Workspace.DEFAULT_PAGE) {
workspace.post(workspace::moveToDefaultScreen);
}
break;
}
case OVERVIEW_STATE_ORDINAL: {
- RecentsView recentsView = getOverviewPanel();
- DiscoveryBounce.showForOverviewIfNeeded(this,
- recentsView.getPagedOrientationHandler());
RecentsView rv = getOverviewPanel();
sendCustomAccessibilityEvent(
rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
@@ -259,41 +281,26 @@
@Override
public TouchController[] createTouchControllers() {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.1");
- }
Mode mode = SysUINavigationMode.getMode(this);
ArrayList<TouchController> list = new ArrayList<>();
list.add(getDragController());
- if (mode == NO_BUTTON) {
- list.add(new NoButtonQuickSwitchTouchController(this));
- list.add(new NavBarToHomeTouchController(this));
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.2");
- }
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "createTouchControllers.3");
- }
+ switch (mode) {
+ case NO_BUTTON:
+ list.add(new NoButtonQuickSwitchTouchController(this));
+ list.add(new NavBarToHomeTouchController(this));
list.add(new NoButtonNavbarToOverviewTouchController(this));
- } else {
- list.add(new FlingAndHoldTouchController(this));
- }
- } else {
- if (getDeviceProfile().isVerticalBarLayout()) {
- list.add(new OverviewToAllAppsTouchController(this));
- list.add(new LandscapeEdgeSwipeController(this));
- if (mode.hasGestures) {
- list.add(new TransposedQuickSwitchTouchController(this));
- }
- } else {
- list.add(new PortraitStatesTouchController(this,
- mode.hasGestures /* allowDragToOverview */));
- if (mode.hasGestures) {
- list.add(new QuickSwitchTouchController(this));
- }
- }
+ break;
+ case TWO_BUTTONS:
+ list.add(new TwoButtonNavbarTouchController(this));
+ list.add(getDeviceProfile().isVerticalBarLayout()
+ ? new TransposedQuickSwitchTouchController(this)
+ : new QuickSwitchTouchController(this));
+ list.add(new PortraitStatesTouchController(this));
+ break;
+ case THREE_BUTTONS:
+ default:
+ list.add(new PortraitStatesTouchController(this));
}
if (!getDeviceProfile().isMultiWindowMode) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
similarity index 88%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
rename to quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 5ccc1e8..c9de662 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,7 +15,8 @@
*/
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
+import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
+import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
@@ -83,11 +84,14 @@
private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
LauncherState state) {
- float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0;
+ float clearAllButtonAlpha = (state.getVisibleElements(mLauncher) & CLEAR_ALL_BUTTON) != 0
+ ? 1 : 0;
propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
- buttonAlpha, LINEAR);
+ clearAllButtonAlpha, LINEAR);
+ float overviewButtonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0
+ ? 1 : 0;
propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
- MultiValueAlpha.VALUE, buttonAlpha, config.getInterpolator(
+ MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
index 7beb9db..d14e8ef 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginInitializerImpl.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.os.Looper;
+import com.android.launcher3.Utilities;
import com.android.systemui.shared.plugins.PluginInitializer;
public class PluginInitializerImpl implements PluginInitializer {
@@ -44,4 +45,8 @@
@Override
public void handleWtfs() {
}
+
+ public boolean isDebuggable() {
+ return Utilities.IS_DEBUG_DEVICE;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 29a6be0..37c774b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -16,17 +16,13 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.SysUINavigationMode;
/**
* Definition for AllApps state
@@ -43,7 +39,7 @@
};
public AllAppsState(int id) {
- super(id, ContainerType.ALLAPPS, STATE_FLAGS);
+ super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
}
@Override
@@ -66,13 +62,7 @@
public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
.getWorkspaceScaleAndTranslation(launcher);
- if (SysUINavigationMode.getMode(launcher) == NO_BUTTON && !ENABLE_OVERVIEW_ACTIONS.get()) {
- float normalScale = 1;
- // Scale down halfway to where we'd be in overview, to prepare for a potential pause.
- scaleAndTranslation.scale = (scaleAndTranslation.scale + normalScale) / 2;
- } else {
- scaleAndTranslation.scale = 1;
- }
+ scaleAndTranslation.scale = 1;
return scaleAndTranslation;
}
@@ -93,8 +83,7 @@
@Override
public float[] getOverviewScaleAndOffset(Launcher launcher) {
- float offset = ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ? 1 : 0;
- return new float[] {0.9f, offset};
+ return new float[] {0.9f, 1};
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
similarity index 90%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 8ff05f2..2ad718b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -15,12 +15,13 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+
import android.content.Context;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -33,7 +34,7 @@
| FLAG_WORKSPACE_INACCESSIBLE | FLAG_NON_INTERACTIVE | FLAG_CLOSE_POPUPS;
public BackgroundAppState(int id) {
- this(id, LauncherLogProto.ContainerType.TASKSWITCHER);
+ this(id, LAUNCHER_STATE_BACKGROUND);
}
protected BackgroundAppState(int id, int logContainer) {
@@ -68,7 +69,15 @@
@Override
public int getVisibleElements(Launcher launcher) {
return super.getVisibleElements(launcher)
- & ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
+ & ~OVERVIEW_ACTIONS
+ & ~CLEAR_ALL_BUTTON
+ & ~VERTICAL_SWIPE_INDICATOR
+ | TASKBAR;
+ }
+
+ @Override
+ public boolean displayOverviewTasksAsGrid(Launcher launcher) {
+ return false;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
similarity index 92%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index fc0dcd5..bdba482 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -15,13 +15,14 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
+
import android.content.Context;
import android.graphics.Rect;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.views.RecentsView;
/**
@@ -34,7 +35,7 @@
FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_WORKSPACE_INACCESSIBLE;
public OverviewModalTaskState(int id) {
- super(id, ContainerType.OVERVIEW, STATE_FLAGS);
+ super(id, LAUNCHER_STATE_OVERVIEW, STATE_FLAGS);
}
@Override
@@ -44,7 +45,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return OVERVIEW_BUTTONS;
+ return OVERVIEW_ACTIONS | CLEAR_ALL_BUTTON;
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
similarity index 72%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index d174bfd..d480b6d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -16,11 +16,8 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.content.Context;
import android.graphics.Rect;
@@ -31,8 +28,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
@@ -54,7 +50,7 @@
}
protected OverviewState(int id, int stateFlags) {
- this(id, ContainerType.TASKSWITCHER, stateFlags);
+ this(id, LAUNCHER_STATE_OVERVIEW, stateFlags);
}
protected OverviewState(int id, int logContainer, int stateFlags) {
@@ -65,7 +61,7 @@
public int getTransitionDuration(Context context) {
// In no-button mode, overview comes in all the way from the left, so give it more time.
boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(context).getMode() == NO_BUTTON;
- return isNoButtonMode && ENABLE_OVERVIEW_ACTIONS.get() ? 380 : 250;
+ return isNoButtonMode ? 380 : 250;
}
@Override
@@ -108,8 +104,7 @@
@Override
public ScaleAndTranslation getQsbScaleAndTranslation(Launcher launcher) {
- if (this == OVERVIEW && ENABLE_OVERVIEW_ACTIONS.get()
- && removeShelfFromOverview(launcher)) {
+ if (this == OVERVIEW) {
// Treat the QSB as part of the hotseat so they move together.
return getHotseatScaleAndTranslation(launcher);
}
@@ -128,18 +123,8 @@
@Override
public int getVisibleElements(Launcher launcher) {
- RecentsView recentsView = launcher.getOverviewPanel();
- if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ||
- hideShelfInTwoButtonLandscape(launcher, recentsView.getPagedOrientationHandler())) {
- return OVERVIEW_BUTTONS;
- } else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
- } else {
- boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
- && launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
- return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS
- | (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
- }
+ return displayOverviewTasksAsGrid(launcher) ? CLEAR_ALL_BUTTON
+ : CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
}
@Override
@@ -148,17 +133,8 @@
}
@Override
- public float getVerticalProgress(Launcher launcher) {
- if ((getVisibleElements(launcher) & ALL_APPS_HEADER_EXTRA) == 0) {
- // We have no all apps content, so we're still at the fully down progress.
- return super.getVerticalProgress(launcher);
- }
- return getDefaultVerticalProgress(launcher);
- }
-
- public static float getDefaultVerticalProgress(Launcher launcher) {
- return 1 - (getDefaultSwipeHeight(launcher)
- / launcher.getAllAppsController().getShiftRange());
+ public boolean displayOverviewTasksAsGrid(Launcher launcher) {
+ return launcher.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
}
@Override
@@ -179,8 +155,6 @@
public void onBackPressed(Launcher launcher) {
TaskView taskView = launcher.<RecentsView>getOverviewPanel().getRunningTaskView();
if (taskView != null) {
- launcher.getUserEventDispatcher().logActionCommand(Action.Command.BACK,
- newContainerTarget(ContainerType.OVERVIEW));
taskView.launchTask(true);
} else {
super.onBackPressed(launcher);
@@ -191,10 +165,6 @@
return new BackgroundAppState(id);
}
- public static OverviewState newPeekState(int id) {
- return new OverviewPeekState(id);
- }
-
public static OverviewState newSwitchState(int id) {
return new QuickSwitchState(id);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
similarity index 90%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 2c7373e..965f474 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -15,8 +15,9 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
+
import com.android.launcher3.Launcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
@@ -26,7 +27,7 @@
public class QuickSwitchState extends BackgroundAppState {
public QuickSwitchState(int id) {
- super(id, LauncherLogProto.ContainerType.APP);
+ super(id, LAUNCHER_STATE_BACKGROUND);
}
@Override
@@ -39,6 +40,6 @@
@Override
public int getVisibleElements(Launcher launcher) {
- return NONE;
+ return TASKBAR;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
new file mode 100644
index 0000000..473fe2d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -0,0 +1,166 @@
+/*
+ * 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.launcher3.uioverrides.states;
+
+import static android.view.View.VISIBLE;
+
+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.OVERVIEW;
+import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
+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.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_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_TRANSLATE_X;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
+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.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
+import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
+
+import android.animation.ValueAnimator;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.util.RecentsAtomicAnimationFactory;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Animation factory for quickstep specific transitions
+ */
+public class QuickstepAtomicAnimationFactory extends
+ RecentsAtomicAnimationFactory<QuickstepLauncher, LauncherState> {
+
+ // Scale recents takes before animating in
+ private static final float RECENTS_PREPARE_SCALE = 1.33f;
+
+ // Due to use of physics, duration may differ between devices so we need to calculate and
+ // cache the value.
+ private int mHintToNormalDuration = -1;
+
+ public QuickstepAtomicAnimationFactory(QuickstepLauncher activity) {
+ super(activity);
+ }
+
+ @Override
+ public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
+ StateAnimationConfig config) {
+ if (toState == NORMAL && fromState == OVERVIEW) {
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+ config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+ config.setInterpolator(ANIM_TASKBAR_FADE, ACCEL);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
+
+ if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
+ config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
+ } else {
+ config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+ }
+
+ 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;
+ if (isWorkspaceVisible) {
+ CellLayout currentChild = (CellLayout) workspace.getChildAt(
+ workspace.getCurrentPage());
+ isWorkspaceVisible = currentChild.getVisibility() == VISIBLE
+ && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
+ }
+ if (!isWorkspaceVisible) {
+ workspace.setScaleX(0.92f);
+ workspace.setScaleY(0.92f);
+ }
+ Hotseat hotseat = mActivity.getHotseat();
+ boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
+ if (!isHotseatVisible) {
+ hotseat.setScaleX(0.92f);
+ hotseat.setScaleY(0.92f);
+ AllAppsContainerView qsbContainer = mActivity.getAppsView();
+ View qsb = qsbContainer.getSearchView();
+ boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
+ if (!qsbVisible) {
+ qsbContainer.setScaleX(0.92f);
+ qsbContainer.setScaleY(0.92f);
+ }
+ }
+ } else if ((fromState == NORMAL || fromState == HINT_STATE) && toState == OVERVIEW) {
+ if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
+ config.setInterpolator(ANIM_WORKSPACE_SCALE,
+ fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+ } else {
+ config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+
+ // Scale up the recents, if it is not coming from the side
+ RecentsView overview = mActivity.getOverviewPanel();
+ if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
+ RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
+ }
+ }
+ config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
+ config.setInterpolator(ANIM_TASKBAR_FADE, OVERSHOOT_1_2);
+ } else if (fromState == HINT_STATE && toState == NORMAL) {
+ config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
+ if (mHintToNormalDuration == -1) {
+ ValueAnimator va = getSpringScaleAnimator(mActivity, mActivity.getWorkspace(),
+ toState.getWorkspaceScaleAndTranslation(mActivity).scale);
+ mHintToNormalDuration = (int) va.getDuration();
+ }
+ config.duration = Math.max(config.duration, mHintToNormalDuration);
+ } else if (mActivity.getTaskbarController() != null) {
+ boolean wasHotseatVisible = fromState.areElementsVisible(mActivity, HOTSEAT_ICONS);
+ boolean isHotseatVisible = toState.areElementsVisible(mActivity, HOTSEAT_ICONS);
+ if (wasHotseatVisible || isHotseatVisible) {
+ config.setInterpolator(ANIM_TASKBAR_FADE, INSTANT);
+ config.setInterpolator(ANIM_HOTSEAT_FADE, INSTANT);
+
+ if (isHotseatVisible) {
+ mActivity.getTaskbarController().alignRealHotseatWithTaskbar();
+ }
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
deleted file mode 100644
index bef191e..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.android.launcher3.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * Touch controller for handling edge swipes in landscape/seascape UI
- */
-public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
-
- private static final String TAG = "LandscapeEdgeSwipeCtrl";
-
- public LandscapeEdgeSwipeController(Launcher l) {
- super(l, SingleAxisSwipeDetector.HORIZONTAL);
- }
-
- @Override
- protected boolean canInterceptTouch(MotionEvent ev) {
- if (mCurrentAnimation != null) {
- // If we are already animating from a previous state, we can intercept.
- return true;
- }
- if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
- return false;
- }
- return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
- }
-
- @Override
- protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
- return draggingFromNav ? OVERVIEW : NORMAL;
- }
-
- @Override
- protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return LauncherLogProto.ContainerType.NAVBAR;
- }
-
- @Override
- protected float getShiftRange() {
- return mLauncher.getDragLayer().getWidth();
- }
-
- @Override
- protected float initCurrentAnimation(@AnimationFlags int animComponent) {
- float range = getShiftRange();
- long maxAccuracy = (long) (2 * range);
- mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
- maxAccuracy, animComponent);
- return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
- }
-
- @Override
- protected int getDirectionForLog() {
- return mLauncher.getDeviceProfile().isSeascape() ? Direction.RIGHT : Direction.LEFT;
- }
-
- @Override
- protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- super.onSwipeInteractionCompleted(targetState, logAction);
- if (mStartState == NORMAL && targetState == OVERVIEW) {
- SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
- }
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
similarity index 76%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index c6aaa43..a990f3e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -17,18 +17,18 @@
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
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.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.ValueAnimator;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.Interpolator;
@@ -45,18 +45,14 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.OverviewToHomeAnim;
import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
/**
* Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
@@ -105,37 +101,19 @@
}
private boolean canInterceptTouch(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NavBarToHomeTouchController.canInterceptTouch "
- + ev);
- }
boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
if (!cameFromNavBar) {
return false;
}
if (mStartState.overviewUi || mStartState == ALL_APPS) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED,
- "NavBarToHomeTouchController.canInterceptTouch true 1 "
- + mStartState.overviewUi + " " + (mStartState == ALL_APPS));
- }
return true;
}
int typeToClose = ENABLE_ALL_APPS_EDU.get() ? TYPE_ALL & ~TYPE_ALL_APPS_EDU : TYPE_ALL;
if (AbstractFloatingView.getTopOpenViewWithType(mLauncher, typeToClose) != null) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED,
- "NavBarToHomeTouchController.canInterceptTouch true 2 "
- + AbstractFloatingView.getTopOpenView(mLauncher), new Exception());
- }
return true;
}
if (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
&& AssistantUtilities.isExcludedAssistantRunning()) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED,
- "NavBarToHomeTouchController.canInterceptTouch true 3");
- }
return true;
}
return false;
@@ -167,10 +145,11 @@
OverviewScrim.SCRIM_MULTIPLIER, OVERVIEW_TO_HOME_SCRIM_MULTIPLIER,
PULLBACK_INTERPOLATOR);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- builder.addOnFrameCallback(
- () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
+ if (LIVE_TILE.get()) {
+ builder.addOnFrameCallback(recentsView::redrawLiveTile);
}
+
+ AbstractFloatingView.closeOpenContainer(mLauncher, AbstractFloatingView.TYPE_TASK_MENU);
} else if (mStartState == ALL_APPS) {
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
builder.setFloat(allAppsController, ALL_APPS_PROGRESS,
@@ -188,8 +167,8 @@
if (topView != null) {
topView.addHintCloseAnim(mPullbackDistance, PULLBACK_INTERPOLATOR, builder);
}
- mCurrentAnimation = builder.createPlaybackController()
- .setOnCancelRunnable(this::clearState);
+ mCurrentAnimation = builder.createPlaybackController();
+ mCurrentAnimation.getTarget().addListener(newCancelListener(this::clearState));
}
private void clearState() {
@@ -210,13 +189,12 @@
@Override
public void onDragEnd(float velocity) {
boolean fling = mSwipeDetector.isFling(velocity);
- final int logAction = fling ? Touch.FLING : Touch.SWIPE;
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
|| (velocity < 0 && fling);
if (success) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
RecentsView recentsView = mLauncher.getOverviewPanel();
recentsView.switchToScreenshot(null,
() -> recentsView.finishRecentsAnimation(true /* toRecents */, null));
@@ -229,15 +207,14 @@
() -> onSwipeInteractionCompleted(mEndState));
}
if (mStartState != mEndState) {
- logStateChange(mStartState.containerType, logAction);
+ logHomeGesture();
}
AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topOpenView != null) {
AbstractFloatingView.closeAllOpenViews(mLauncher);
- logStateChange(topOpenView.getLogContainerType(), logAction);
+ // TODO: add to WW log
}
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
} else {
// Quickly return to the state we came from (we didn't move far).
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
@@ -254,17 +231,10 @@
AccessibilityManagerCompat.sendStateEventToTest(mLauncher, targetState.ordinal);
}
- private void logStateChange(int startContainerType, int logAction) {
- mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
- LauncherLogProto.Action.Direction.UP,
- mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
- LauncherLogProto.ContainerType.NAVBAR,
- startContainerType,
- mEndState.containerType,
- mLauncher.getWorkspace().getCurrentPage());
+ private void logHomeGesture() {
mLauncher.getStatsLogManager().logger()
- .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
- .withDstState(StatsLogManager.containerTypeToAtomState(mEndState.containerType))
+ .withSrcState(mStartState.statsLogOrdinal)
+ .withDstState(mEndState.statsLogOrdinal)
.log(LAUNCHER_HOME_GESTURE);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
similarity index 62%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 8f0f683..45cb46f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -16,30 +16,42 @@
package com.android.launcher3.uioverrides.touchcontrollers;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
+import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+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_3;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
+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.util.VibratorWrapper.OVERVIEW_HAPTIC;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.PointF;
-import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
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.anim.Interpolators;
import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
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.views.RecentsView;
@@ -48,7 +60,7 @@
* the nav bar falls back to go to All Apps. Swiping from the nav bar without holding goes to the
* first home screen instead of to Overview.
*/
-public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchController {
+public class NoButtonNavbarToOverviewTouchController extends PortraitStatesTouchController {
// How much of the movement to use for translating overview after swipe and hold.
@@ -57,8 +69,11 @@
private static final float TRANSLATION_ANIM_VELOCITY_DP_PER_MS = 0.8f;
private final RecentsView mRecentsView;
+ private final MotionPauseDetector mMotionPauseDetector;
+ private final float mMotionPauseMinDisplacement;
private boolean mDidTouchStartInNavBar;
+ private boolean mStartedOverview;
private boolean mReachedOverview;
// The last recorded displacement before we reached overview.
private PointF mStartDisplacement = new PointF();
@@ -71,16 +86,8 @@
public NoButtonNavbarToOverviewTouchController(Launcher l) {
super(l);
mRecentsView = l.getOverviewPanel();
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController.ctor");
- }
- }
-
- @Override
- protected float getMotionPauseMaxDisplacement() {
- // No need to disallow pause when swiping up all the way up the screen (unlike
- // FlingAndHoldTouchController where user is probably intending to go to all apps).
- return Float.MAX_VALUE;
+ mMotionPauseDetector = new MotionPauseDetector(l);
+ mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
}
@Override
@@ -113,6 +120,13 @@
@Override
public void onDragStart(boolean start, float startDisplacement) {
super.onDragStart(start, startDisplacement);
+
+ mMotionPauseDetector.clear();
+
+ if (handlingOverviewAnim()) {
+ mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
+ }
+
if (mFromState == NORMAL && mToState == HINT_STATE) {
mNormalToHintOverviewScrimAnimator = ObjectAnimator.ofFloat(
mLauncher.getDragLayer().getOverviewScrim(),
@@ -120,6 +134,7 @@
mFromState.getOverviewScrimAlpha(mLauncher),
mToState.getOverviewScrimAlpha(mLauncher));
}
+ mStartedOverview = false;
mReachedOverview = false;
mOverviewResistYAnim = null;
}
@@ -134,7 +149,18 @@
@Override
public void onDragEnd(float velocity) {
- super.onDragEnd(velocity);
+ if (mStartedOverview) {
+ goToOverviewOrHomeOnDragEnd(velocity);
+ } else {
+ super.onDragEnd(velocity);
+ }
+
+ View searchView = mLauncher.getAppsView().getSearchView();
+ if (searchView instanceof FeedbackHandler) {
+ ((FeedbackHandler) searchView).resetFeedback();
+ }
+
+ mMotionPauseDetector.clear();
mNormalToHintOverviewScrimAnimator = null;
if (mLauncher.isInState(OVERVIEW)) {
// Normally we would cleanup the state based on mCurrentAnimation, but since we stop
@@ -156,41 +182,41 @@
}
}
- @Override
- protected void onMotionPauseChanged(boolean isPaused) {
+ private void onMotionPauseDetected() {
if (mCurrentAnimation == null) {
return;
}
mNormalToHintOverviewScrimAnimator = null;
- mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable(() -> {
+ mCurrentAnimation.getTarget().addListener(newCancelListener(() ->
mLauncher.getStateManager().goToState(OVERVIEW, true, () -> {
mOverviewResistYAnim = AnimatorControllerWithResistance
.createRecentsResistanceFromOverviewAnim(mLauncher, null)
.createPlaybackController();
mReachedOverview = true;
maybeSwipeInteractionToOverviewComplete();
- });
- });
+ })));
+
+ mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
+ mCurrentAnimation.dispatchOnCancel();
+ mStartedOverview = true;
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
}
private void maybeSwipeInteractionToOverviewComplete() {
if (mReachedOverview && mDetector.isSettlingState()) {
- onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
+ onSwipeInteractionCompleted(OVERVIEW);
}
}
- @Override
- protected boolean handlingOverviewAnim() {
- return mDidTouchStartInNavBar && super.handlingOverviewAnim();
+ private boolean handlingOverviewAnim() {
+ int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
+ return mDidTouchStartInNavBar && mStartState == NORMAL
+ && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0;
}
@Override
public boolean onDrag(float yDisplacement, float xDisplacement, MotionEvent event) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "NoButtonNavbarToOverviewTouchController");
- }
- if (mMotionPauseDetector.isPaused()) {
+ if (mStartedOverview) {
if (!mReachedOverview) {
mStartDisplacement.set(xDisplacement, yDisplacement);
mStartY = event.getY();
@@ -205,20 +231,21 @@
* OVERVIEW_MOVEMENT_FACTOR);
}
}
- // Stay in Overview.
- return true;
}
- return super.onDrag(yDisplacement, xDisplacement, event);
+
+ float upDisplacement = -yDisplacement;
+ mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
+ || upDisplacement < mMotionPauseMinDisplacement);
+ mMotionPauseDetector.addPosition(event);
+
+ // Stay in Overview.
+ return mStartedOverview || super.onDrag(yDisplacement, xDisplacement, event);
}
- @Override
- protected void goToOverviewOnDragEnd(float velocity) {
- float velocityDp = dpiFromPx(velocity);
- boolean isFling = Math.abs(velocityDp) > 1;
- StateManager<LauncherState> stateManager = mLauncher.getStateManager();
- boolean goToHomeInsteadOfOverview = isFling;
+ private void goToOverviewOrHomeOnDragEnd(float velocity) {
+ boolean goToHomeInsteadOfOverview = !mMotionPauseDetector.isPaused();
if (goToHomeInsteadOfOverview) {
- new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL, Touch.FLING))
+ new OverviewToHomeAnim(mLauncher, ()-> onSwipeInteractionCompleted(NORMAL))
.animateWithVelocity(velocity);
}
if (mReachedOverview) {
@@ -248,4 +275,54 @@
private float dpiFromPx(float pixels) {
return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics());
}
+
+ @Override
+ protected StateAnimationConfig getConfigForStates(
+ LauncherState fromState, LauncherState toState) {
+ if (fromState == NORMAL && toState == ALL_APPS) {
+ StateAnimationConfig builder = new StateAnimationConfig();
+ // Fade in prediction icons quickly, then rest of all apps after reaching overview.
+ float progressToReachOverview = NORMAL.getVerticalProgress(mLauncher)
+ - OVERVIEW.getVerticalProgress(mLauncher);
+ builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
+ ACCEL,
+ 0,
+ ALL_APPS_CONTENT_FADE_THRESHOLD));
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
+ ACCEL,
+ progressToReachOverview,
+ progressToReachOverview + ALL_APPS_CONTENT_FADE_THRESHOLD));
+
+ // Get workspace out of the way quickly, to prepare for potential pause.
+ builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
+ builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3);
+ builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
+ return builder;
+ } else if (fromState == ALL_APPS && toState == NORMAL) {
+ StateAnimationConfig builder = new StateAnimationConfig();
+ // Keep all apps/predictions opaque until the very end of the transition.
+ float progressToReachOverview = OVERVIEW.getVerticalProgress(mLauncher);
+ builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
+ DEACCEL,
+ progressToReachOverview - ALL_APPS_CONTENT_FADE_THRESHOLD,
+ progressToReachOverview));
+ builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
+ DEACCEL,
+ 1 - ALL_APPS_CONTENT_FADE_THRESHOLD,
+ 1));
+ return builder;
+ }
+ return super.getConfigForStates(fromState, toState);
+ }
+
+ /**
+ * Interface for views with feedback animation requiring reset
+ */
+ public interface FeedbackHandler {
+
+ /**
+ * reset searchWidget feedback
+ */
+ void resetFeedback();
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
similarity index 81%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 5c9fd47..4766870 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -15,17 +15,16 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
-import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
+import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.DEACCEL_5;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
@@ -37,12 +36,8 @@
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_PAUSE_TO_OVERVIEW_ANIM;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.CANCEL;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -50,6 +45,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.graphics.PointF;
@@ -60,18 +56,12 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.BothAxesSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.AnimatedFloat;
@@ -79,8 +69,6 @@
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.util.ShelfPeekAnim;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
import com.android.quickstep.views.LauncherRecentsView;
@@ -89,23 +77,25 @@
* the user as possible, also handles swipe up and hold to go to overview and swiping back home.
*/
public class NoButtonQuickSwitchTouchController implements TouchController,
- BothAxesSwipeDetector.Listener, MotionPauseDetector.OnMotionPauseListener {
+ BothAxesSwipeDetector.Listener {
/** The minimum progress of the scale/translationY animation until drag end. */
private static final float Y_ANIM_MIN_PROGRESS = 0.25f;
private static final Interpolator FADE_OUT_INTERPOLATOR = DEACCEL_5;
private static final Interpolator TRANSLATE_OUT_INTERPOLATOR = ACCEL_0_75;
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 BothAxesSwipeDetector mSwipeDetector;
- private final ShelfPeekAnim mShelfPeekAnim;
private final float mXRange;
private final float mYRange;
private final float mMaxYProgress;
private final MotionPauseDetector mMotionPauseDetector;
private final float mMotionPauseMinDisplacement;
private final LauncherRecentsView mRecentsView;
+ protected final AnimatorListener mClearStateOnCancelListener =
+ newCancelListener(this::clearState);
private boolean mNoIntercept;
private LauncherState mStartState;
@@ -121,7 +111,6 @@
public NoButtonQuickSwitchTouchController(BaseQuickstepLauncher launcher) {
mLauncher = launcher;
mSwipeDetector = new BothAxesSwipeDetector(mLauncher, this);
- mShelfPeekAnim = mLauncher.getShelfPeekAnim();
mRecentsView = mLauncher.getOverviewPanel();
mXRange = mLauncher.getDeviceProfile().widthPx / 2f;
mYRange = LayoutUtils.getShelfTrackingDistance(
@@ -178,7 +167,7 @@
if (start) {
mStartState = mLauncher.getStateManager().getState();
- mMotionPauseDetector.setOnMotionPauseListener(this);
+ mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseDetected);
// We have detected horizontal drag start, now allow swipe up as well.
mSwipeDetector.setDetectableScrollConditions(DIRECTION_RIGHT | DIRECTION_UP,
@@ -188,28 +177,8 @@
}
}
- @Override
- public void onMotionPauseChanged(boolean isPaused) {
+ private void onMotionPauseDetected() {
VibratorWrapper.INSTANCE.get(mLauncher).vibrate(OVERVIEW_HAPTIC);
-
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()) {
- return;
- }
-
- ShelfAnimState shelfState = isPaused ? PEEK : HIDE;
- if (shelfState == PEEK) {
- // Some shelf elements (e.g. qsb) were hidden, but we need them visible when peeking.
- AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
- allAppsController.setAlphas(
- NORMAL, new StateAnimationConfig(), NO_ANIM_PROPERTY_SETTER);
-
- if ((OVERVIEW.getVisibleElements(mLauncher) & HOTSEAT_ICONS) != 0) {
- // Hotseat was hidden, but we need it visible when peeking.
- mLauncher.getHotseat().setAlpha(1);
- }
- }
- mShelfPeekAnim.setShelfState(shelfState, ShelfPeekAnim.INTERPOLATOR,
- ShelfPeekAnim.DURATION);
}
private void setupAnimators() {
@@ -239,8 +208,8 @@
config.duration = (long) (Math.max(mXRange, mYRange) * 2);
config.animFlags = config.animFlags | SKIP_OVERVIEW;
mNonOverviewAnim = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(toState, config)
- .setOnCancelRunnable(this::clearState);
+ .createAnimationToNewWorkspace(toState, config);
+ mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener);
}
private void setupOverviewAnimators() {
@@ -253,7 +222,7 @@
mRecentsView.setContentAlpha(1);
mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
mLauncher.getActionsView().getVisibilityAlpha().setValue(
- (fromState.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1f : 0f);
+ (fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f);
float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
// As we drag right, animate the following properties:
@@ -301,22 +270,9 @@
mIsHomeScreenVisible = FADE_OUT_INTERPOLATOR.getInterpolation(xProgress)
<= 1 - ALPHA_CUTOFF_THRESHOLD;
- if (wasHomeScreenVisible && !mIsHomeScreenVisible) {
- // Get the shelf all the way offscreen so it pops up when we decide to peek it.
- mShelfPeekAnim.setShelfState(HIDE, LINEAR, 0);
- }
-
- // Only allow motion pause if the home screen is invisible, since some
- // home screen elements will appear in the shelf on motion pause.
- mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
- || -displacement.y < mMotionPauseMinDisplacement);
+ mMotionPauseDetector.setDisallowPause(-displacement.y < mMotionPauseMinDisplacement);
mMotionPauseDetector.addPosition(ev);
- if (mIsHomeScreenVisible) {
- // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
- mShelfPeekAnim.setShelfState(CANCEL, LINEAR, 0);
- }
-
if (mXOverviewAnim != null) {
mXOverviewAnim.setPlayFraction(xProgress);
}
@@ -331,16 +287,17 @@
boolean horizontalFling = mSwipeDetector.isFling(velocity.x);
boolean verticalFling = mSwipeDetector.isFling(velocity.y);
boolean noFling = !horizontalFling && !verticalFling;
- int logAction = noFling ? Touch.SWIPE : Touch.FLING;
if (mMotionPauseDetector.isPaused() && noFling) {
cancelAnimations();
- Animator overviewAnim = mLauncher.createAtomicAnimationFactory()
- .createStateElementAnimation(INDEX_PAUSE_TO_OVERVIEW_ANIM);
+ StateAnimationConfig config = new StateAnimationConfig();
+ config.duration = ATOMIC_DURATION_FROM_PAUSED_TO_OVERVIEW;
+ Animator overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
+ mStartState, OVERVIEW, config);
overviewAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- onAnimationToStateCompleted(OVERVIEW, logAction);
+ onAnimationToStateCompleted(OVERVIEW);
}
});
overviewAnim.start();
@@ -426,7 +383,8 @@
if (canceled) {
// Let the state manager know that the animation didn't go to the target state,
// but don't clean up yet (we already clean up when the animation completes).
- mNonOverviewAnim.dispatchOnCancelWithoutCancelRunnable();
+ mNonOverviewAnim.getTarget().removeListener(mClearStateOnCancelListener);
+ mNonOverviewAnim.dispatchOnCancel();
}
float startProgress = mNonOverviewAnim.getProgressFraction();
float endProgress = canceled ? 0 : 1;
@@ -435,7 +393,7 @@
}
nonOverviewAnim.setDuration(Math.max(xDuration, yDuration));
- mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState, logAction));
+ mNonOverviewAnim.setEndAction(() -> onAnimationToStateCompleted(targetState));
cancelAnimations();
xOverviewAnim.start();
@@ -443,27 +401,17 @@
nonOverviewAnim.start();
}
- private void onAnimationToStateCompleted(LauncherState targetState, int logAction) {
- mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
- getDirectionForLog(), mSwipeDetector.getDownX(), mSwipeDetector.getDownY(),
- LauncherLogProto.ContainerType.NAVBAR,
- mStartState.containerType,
- targetState.containerType,
- mLauncher.getWorkspace().getCurrentPage());
+ private void onAnimationToStateCompleted(LauncherState targetState) {
mLauncher.getStatsLogManager().logger()
.withSrcState(LAUNCHER_STATE_HOME)
- .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
- .log(getLauncherAtomEvent(mStartState.containerType, targetState.containerType,
+ .withDstState(targetState.statsLogOrdinal)
+ .log(getLauncherAtomEvent(mStartState.statsLogOrdinal, targetState.statsLogOrdinal,
targetState.ordinal > mStartState.ordinal
? LAUNCHER_UNKNOWN_SWIPEUP
: LAUNCHER_UNKNOWN_SWIPEDOWN));
mLauncher.getStateManager().goToState(targetState, false, this::clearState);
}
- private int getDirectionForLog() {
- return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
- }
-
private void cancelAnimations() {
if (mNonOverviewAnim != null) {
mNonOverviewAnim.getAnimationPlayer().cancel();
@@ -474,7 +422,6 @@
if (mYOverviewAnim != null) {
mYOverviewAnim.cancelAnimation();
}
- mShelfPeekAnim.setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
mMotionPauseDetector.clear();
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
similarity index 97%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index 845699a..4df0f63 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -76,7 +76,7 @@
* @return the animation
*/
PendingAnimation createSwipeDownToTaskAppAnimation(long duration, Interpolator interpolator) {
- mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
+ mRecentsView.setCurrentPage(mRecentsView.getDestinationPage());
TaskView taskView = mRecentsView.getCurrentPageTaskView();
if (taskView == null) {
throw new IllegalStateException("There is no task view to animate to.");
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index a684b9d..facfb9d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -24,13 +24,10 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
@@ -49,12 +46,10 @@
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.SystemUiProxy;
-import com.android.quickstep.TouchInteractionService;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
/**
* Touch controller for handling various state transitions in portrait UI.
@@ -77,26 +72,26 @@
private final InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
- private final boolean mAllowDragToOverview;
-
// If true, we will finish the current animation instantly on second touch.
private boolean mFinishFastOnSecondTouch;
- public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
+ public PortraitStatesTouchController(Launcher l) {
super(l, SingleAxisSwipeDetector.VERTICAL);
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
- mAllowDragToOverview = allowDragToOverview;
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
+ // If we are swiping to all apps instead of overview, allow it from anywhere.
+ boolean interceptAnywhere = mLauncher.isInState(NORMAL);
if (mCurrentAnimation != null) {
if (mFinishFastOnSecondTouch) {
mCurrentAnimation.getAnimationPlayer().end();
}
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
- if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()) {
+ if (ev.getY() >= allAppsController.getShiftRange() * allAppsController.getProgress()
+ || interceptAnywhere) {
// If we are already animating from a previous state, we can intercept as long as
// the touch is below the current all apps progress (to allow for double swipe).
return true;
@@ -118,9 +113,7 @@
return false;
}
} else {
- // If we are swiping to all apps instead of overview, allow it from anywhere.
- boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
- // For all other states, only listen if the event originated below the hotseat height
+ // For non-normal states, only listen if the event originated below the hotseat height
if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
return false;
}
@@ -133,50 +126,16 @@
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "PortraitStatesTouchController.getTargetState");
- }
if (fromState == ALL_APPS && !isDragTowardPositive) {
- // Should swipe down go to OVERVIEW instead?
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
- "PortraitStatesTouchController.getTargetState 1");
- }
- if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mLauncher)) {
- // Don't allow swiping down to overview.
- return NORMAL;
- }
- return TouchInteractionService.isConnected() ?
- mLauncher.getStateManager().getLastState() : NORMAL;
+ return NORMAL;
} else if (fromState == OVERVIEW) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
- "PortraitStatesTouchController.getTargetState 2");
- }
- LauncherState positiveDragTarget = ALL_APPS;
- if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(mLauncher)) {
- // Don't allow swiping up to all apps.
- positiveDragTarget = OVERVIEW;
- }
- return isDragTowardPositive ? positiveDragTarget : NORMAL;
+ return isDragTowardPositive ? OVERVIEW : NORMAL;
} else if (fromState == NORMAL && isDragTowardPositive) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS,
- "PortraitStatesTouchController.getTargetState 3");
- }
- int stateFlags = SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags();
- return mAllowDragToOverview && TouchInteractionService.isConnected()
- && (stateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0
- ? OVERVIEW : ALL_APPS;
+ return ALL_APPS;
}
return fromState;
}
- @Override
- protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return isTouchOverHotseat(mLauncher, ev) ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
- }
-
private StateAnimationConfig getNormalToOverviewAnimation() {
mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
@@ -249,32 +208,32 @@
final StateAnimationConfig config = totalShift == 0 ? new StateAnimationConfig()
: getConfigForStates(mFromState, mToState);
- config.animFlags = updateAnimComponentsOnReinit(animFlags);
+ config.animFlags = animFlags;
config.duration = maxAccuracy;
- cancelPendingAnim();
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
+ mCurrentAnimation.dispatchOnCancel();
+ }
+ mGoingBetweenStates = true;
if (mFromState == OVERVIEW && mToState == NORMAL
&& mOverviewPortraitStateTouchHelper.shouldSwipeDownReturnToApp()) {
// Reset the state manager, when changing the interaction mode
mLauncher.getStateManager().goToState(OVERVIEW, false /* animate */);
- mPendingAnimation = mOverviewPortraitStateTouchHelper
- .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR);
- Runnable onCancelRunnable = () -> {
- cancelPendingAnim();
- clearState();
- };
- mCurrentAnimation = mPendingAnimation.createPlaybackController()
- .setOnCancelRunnable(onCancelRunnable);
+ mGoingBetweenStates = false;
+ mCurrentAnimation = mOverviewPortraitStateTouchHelper
+ .createSwipeDownToTaskAppAnimation(maxAccuracy, Interpolators.LINEAR)
+ .createPlaybackController();
mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
RecentsView recentsView = mLauncher.getOverviewPanel();
totalShift = LayoutUtils.getShelfTrackingDistance(mLauncher,
mLauncher.getDeviceProfile(), recentsView.getPagedOrientationHandler());
} else {
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, config)
- .setOnCancelRunnable(this::clearState);
+ .createAnimationToNewWorkspace(mToState, config);
}
+ mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener);
if (totalShift == 0) {
totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
@@ -283,21 +242,6 @@
return 1 / totalShift;
}
- /**
- * Give subclasses the chance to update the animation when we re-initialize towards a new state.
- */
- @AnimationFlags
- protected int updateAnimComponentsOnReinit(@AnimationFlags int animComponents) {
- return animComponents;
- }
-
- private void cancelPendingAnim() {
- if (mPendingAnimation != null) {
- mPendingAnimation.finish(false, Touch.SWIPE);
- mPendingAnimation = null;
- }
- }
-
@Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
@@ -328,8 +272,8 @@
}
@Override
- protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- super.onSwipeInteractionCompleted(targetState, logAction);
+ protected void onSwipeInteractionCompleted(LauncherState targetState) {
+ super.onSwipeInteractionCompleted(targetState);
if (mStartState == NORMAL && targetState == OVERVIEW) {
SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
}
@@ -361,4 +305,44 @@
return baseInterpolator.getInterpolation(v);
}
}
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ InteractionJankMonitorWrapper.begin(
+ mLauncher.getRootView(), InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_UP:
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ break;
+ }
+ return super.onControllerInterceptTouchEvent(ev);
+
+ }
+
+ @Override
+ protected void onReinitToState(LauncherState newToState) {
+ super.onReinitToState(newToState);
+ if (newToState != ALL_APPS) {
+ InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ }
+ }
+
+ @Override
+ protected void onReachedFinalState(LauncherState toState) {
+ super.onReinitToState(toState);
+ if (toState == ALL_APPS) {
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ }
+ }
+
+ @Override
+ protected void clearState() {
+ super.clearState();
+ InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_OPEN_ALL_APPS);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
similarity index 88%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index c643858..fc9e1bb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
@@ -41,14 +42,12 @@
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TaskUtils;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
/**
* Handles quick switching to a recent task from the home screen.
@@ -92,14 +91,13 @@
@Override
public void onDragStart(boolean start, float startDisplacement) {
super.onDragStart(start, startDisplacement);
- mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ mStartContainerType = LAUNCHER_STATE_BACKGROUND;
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
}
@Override
- protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- super.onSwipeInteractionCompleted(targetState, logAction);
+ protected void onSwipeInteractionCompleted(LauncherState targetState) {
+ super.onSwipeInteractionCompleted(targetState);
}
@Override
@@ -108,8 +106,8 @@
setupInterpolators(config);
config.duration = (long) (getShiftRange() * 2);
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, config)
- .setOnCancelRunnable(this::clearState);
+ .createAnimationToNewWorkspace(mToState, config);
+ mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener);
mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator ->
updateFullscreenProgress((Float) valueAnimator.getAnimatedValue()));
return 1 / getShiftRange();
@@ -153,14 +151,4 @@
protected float getShiftRange() {
return mLauncher.getDeviceProfile().widthPx / 2f;
}
-
- @Override
- protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return LauncherLogProto.ContainerType.NAVBAR;
- }
-
- @Override
- protected int getDirectionForLog() {
- return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
- }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 7a7cbb4..fe69c9b 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -15,10 +15,11 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
+import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-import static android.view.MotionEvent.ACTION_CANCEL;
+
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN;
import android.graphics.PointF;
@@ -32,12 +33,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
-
import com.android.quickstep.SystemUiProxy;
+
import java.io.PrintWriter;
/**
@@ -134,9 +132,6 @@
int action = ev.getAction();
if (action == ACTION_UP || action == ACTION_CANCEL) {
dispatchTouchEvent(ev);
- mLauncher.getUserEventDispatcher().logActionOnContainer(action == ACTION_UP ?
- Touch.FLING : Touch.SWIPE, Direction.DOWN, ContainerType.WORKSPACE,
- mLauncher.getWorkspace().getCurrentPage());
mLauncher.getStatsLogManager().logger()
.log(LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN);
setWindowSlippery(false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
similarity index 80%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 2cfbc2b..c2e5cda 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -16,10 +16,8 @@
package com.android.launcher3.uioverrides.touchcontrollers;
import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
-import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -34,10 +32,10 @@
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.touch.BaseSwipeDetector;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -52,18 +50,16 @@
extends AnimatorListenerAdapter implements TouchController,
SingleAxisSwipeDetector.Listener {
- // Progress after which the transition is assumed to be a success in case user does not fling
- public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
protected final T mActivity;
private final SingleAxisSwipeDetector mDetector;
private final RecentsView mRecentsView;
private final int[] mTempCords = new int[2];
private final boolean mIsRtl;
- private PendingAnimation mPendingAnimation;
private AnimatorPlaybackController mCurrentAnimation;
private boolean mCurrentAnimationIsGoingUp;
+ private boolean mAllowGoingUp;
+ private boolean mAllowGoingDown;
private boolean mNoIntercept;
@@ -79,7 +75,7 @@
mRecentsView = activity.getOverviewPanel();
mIsRtl = Utilities.isRtl(activity.getResources());
SingleAxisSwipeDetector.Direction dir =
- mRecentsView.getPagedOrientationHandler().getOppositeSwipeDirection();
+ mRecentsView.getPagedOrientationHandler().getUpDownSwipeDirection();
mDetector = new SingleAxisSwipeDetector(activity, this, dir);
}
@@ -153,15 +149,26 @@
break;
}
mTaskBeingDragged = view;
- if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
+ int upDirection = mRecentsView.getPagedOrientationHandler()
+ .getUpDirection(mIsRtl);
+ if (!SysUINavigationMode.getMode(mActivity).hasGestures || (
+ mActivity.getDeviceProfile().isTablet
+ && FeatureFlags.ENABLE_OVERVIEW_GRID.get())) {
// Don't allow swipe down to open if we don't support swipe up
- // to enter overview.
- directionsToDetectScroll = DIRECTION_POSITIVE;
+ // to enter overview, or when grid layout is enabled.
+ directionsToDetectScroll = upDirection;
+ mAllowGoingUp = true;
+ mAllowGoingDown = false;
} else {
// The task can be dragged up to dismiss it,
// and down to open if it's the current page.
- directionsToDetectScroll = i == mRecentsView.getCurrentPage()
- ? DIRECTION_BOTH : DIRECTION_POSITIVE;
+ mAllowGoingUp = true;
+ if (i == mRecentsView.getCurrentPage()) {
+ mAllowGoingDown = true;
+ directionsToDetectScroll = DIRECTION_BOTH;
+ } else {
+ directionsToDetectScroll = upDirection;
+ }
}
break;
}
@@ -194,18 +201,14 @@
// No need to init
return;
}
- int scrollDirections = mDetector.getScrollDirections();
- if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0)
- || !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) {
+ if ((goingUp && !mAllowGoingUp) || (!goingUp && !mAllowGoingDown)) {
// Trying to re-init in an unsupported direction.
return;
}
if (mCurrentAnimation != null) {
mCurrentAnimation.setPlayFraction(0);
- }
- if (mPendingAnimation != null) {
- mPendingAnimation.finish(false, Touch.SWIPE);
- mPendingAnimation = null;
+ mCurrentAnimation.getTarget().removeListener(this);
+ mCurrentAnimation.dispatchOnCancel();
}
PagedOrientationHandler orientationHandler = mRecentsView.getPagedOrientationHandler();
@@ -218,15 +221,16 @@
// The interpolator controlling the most prominent visual movement. We use this to determine
// whether we passed SUCCESS_TRANSITION_PROGRESS.
final Interpolator currentInterpolator;
+ PendingAnimation pa;
if (goingUp) {
currentInterpolator = Interpolators.LINEAR;
- mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+ pa = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
true /* animateTaskView */, true /* removeTask */, maxDuration);
mEndDisplacement = -secondaryTaskDimension;
} else {
currentInterpolator = Interpolators.ZOOM_IN;
- mPendingAnimation = mRecentsView.createTaskLaunchAnimation(
+ pa = mRecentsView.createTaskLaunchAnimation(
mTaskBeingDragged, maxDuration, currentInterpolator);
// Since the thumbnail is what is filling the screen, based the end displacement on it.
@@ -236,12 +240,8 @@
mEndDisplacement = secondaryLayerDimension - mTempCords[1];
}
mEndDisplacement *= verticalFactor;
+ mCurrentAnimation = pa.createPlaybackController();
- if (mCurrentAnimation != null) {
- mCurrentAnimation.setOnCancelRunnable(null);
- }
- mCurrentAnimation = mPendingAnimation.createPlaybackController()
- .setOnCancelRunnable(this::clearState);
// Setting this interpolator doesn't affect the visual motion, but is used to determine
// whether we successfully reached the target state in onDragEnd().
mCurrentAnimation.getTarget().setInterpolator(currentInterpolator);
@@ -279,11 +279,6 @@
mCurrentAnimation.setPlayFraction(Utilities.boundToRange(
totalDisplacement * mProgressMultiplier, 0, 1));
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsView.getCurrentPage() != 0 || isGoingUp) {
- mRecentsView.redrawLiveTile(true);
- }
- }
return true;
}
@@ -291,7 +286,6 @@
public void onDragEnd(float velocity) {
boolean fling = mDetector.isFling(velocity);
final boolean goingToEnd;
- final int logAction;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
if (blockedFling) {
fling = false;
@@ -300,11 +294,9 @@
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = mCurrentAnimation.getInterpolatedProgress();
if (fling) {
- logAction = Touch.FLING;
boolean goingUp = orientationHandler.isGoingUp(velocity, mIsRtl);
goingToEnd = goingUp == mCurrentAnimationIsGoingUp;
} else {
- logAction = Touch.SWIPE;
goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
}
long animationDuration = BaseSwipeDetector.calculateDuration(
@@ -313,24 +305,10 @@
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
}
- mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
- if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
- mRecentsView.redrawLiveTile(true);
- }
- });
- }
+ mCurrentAnimation.setEndAction(this::clearState);
mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,
- velocity, mEndDisplacement, animationDuration);
- }
-
- private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
- if (mPendingAnimation != null) {
- mPendingAnimation.finish(wasSuccess, logAction);
- mPendingAnimation = null;
- }
- clearState();
+ velocity * orientationHandler.getSecondaryTranslationDirectionFactor(),
+ mEndDisplacement, animationDuration);
}
private void clearState() {
@@ -338,9 +316,5 @@
mDetector.setDetectableScrollConditions(0, false);
mTaskBeingDragged = null;
mCurrentAnimation = null;
- if (mPendingAnimation != null) {
- mPendingAnimation.finish(false, Touch.SWIPE);
- mPendingAnimation = null;
- }
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TransposedQuickSwitchTouchController.java
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
new file mode 100644
index 0000000..faf5054
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
@@ -0,0 +1,141 @@
+/*
+ * 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.launcher3.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.AbstractFloatingView.getOpenView;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+
+import android.animation.ValueAnimator;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.LayoutUtils;
+import com.android.quickstep.views.AllAppsEduView;
+
+/**
+ * Touch controller for handling edge swipes in 2-button mode
+ */
+public class TwoButtonNavbarTouchController extends AbstractStateChangeTouchController {
+
+ private static final int MAX_NUM_SWIPES_TO_TRIGGER_EDU = 3;
+
+ private static final String TAG = "2BtnNavbarTouchCtrl";
+
+ private final boolean mIsTransposed;
+
+ // If true, we will finish the current animation instantly on second touch.
+ private boolean mFinishFastOnSecondTouch;
+
+ private int mContinuousTouchCount = 0;
+
+ public TwoButtonNavbarTouchController(Launcher l) {
+ super(l, l.getDeviceProfile().isVerticalBarLayout()
+ ? SingleAxisSwipeDetector.HORIZONTAL : SingleAxisSwipeDetector.VERTICAL);
+ mIsTransposed = l.getDeviceProfile().isVerticalBarLayout();
+ }
+
+ @Override
+ protected boolean canInterceptTouch(MotionEvent ev) {
+ boolean canIntercept = canInterceptTouchInternal(ev);
+ if (!canIntercept) {
+ mContinuousTouchCount = 0;
+ }
+ return canIntercept;
+ }
+
+ private boolean canInterceptTouchInternal(MotionEvent ev) {
+ if (mCurrentAnimation != null) {
+ if (mFinishFastOnSecondTouch) {
+ mCurrentAnimation.getAnimationPlayer().end();
+ }
+
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+ if ((ev.getEdgeFlags() & EDGE_NAV_BAR) == 0) {
+ return false;
+ }
+ if (!mIsTransposed && mLauncher.isInState(OVERVIEW)) {
+ return true;
+ }
+ return mLauncher.isInState(NORMAL);
+ }
+
+ @Override
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ if (mIsTransposed) {
+ boolean draggingFromNav =
+ mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
+ return draggingFromNav ? OVERVIEW : NORMAL;
+ } else {
+ LauncherState startState = mStartState != null ? mStartState : fromState;
+ return isDragTowardPositive ^ (startState == OVERVIEW) ? OVERVIEW : NORMAL;
+ }
+ }
+
+ @Override
+ protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
+ LauncherState targetState, float velocity, boolean isFling) {
+ super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
+ velocity, isFling);
+ mFinishFastOnSecondTouch = !mIsTransposed && mFromState == NORMAL;
+ }
+
+ @Override
+ protected float getShiftRange() {
+ // Should be in sync with TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT
+ return LayoutUtils.getDefaultSwipeHeight(mLauncher, mLauncher.getDeviceProfile());
+ }
+
+ @Override
+ protected float initCurrentAnimation(@AnimationFlags int animComponent) {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+ mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
+ maxAccuracy, animComponent);
+ return (mLauncher.getDeviceProfile().isSeascape() ? 1 : -1) / range;
+ }
+
+ @Override
+ protected void onSwipeInteractionCompleted(LauncherState targetState) {
+ super.onSwipeInteractionCompleted(targetState);
+ if (!mIsTransposed) {
+ mContinuousTouchCount++;
+ }
+ if (mStartState == NORMAL && targetState == OVERVIEW) {
+ SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ } else if (targetState == NORMAL
+ && mContinuousTouchCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
+ mContinuousTouchCount = 0;
+ if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
+ AllAppsEduView.show(mLauncher);
+ }
+ }
+ mStartState = null;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
similarity index 60%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
rename to quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index f468788..e0c041e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,20 +15,28 @@
*/
package com.android.quickstep;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static android.widget.Toast.LENGTH_SHORT;
+
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.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.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
+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_OVERVIEW;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -37,74 +45,105 @@
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
-import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
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.ActivityManager;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Matrix;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.os.Build;
import android.os.SystemClock;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets;
import android.view.animation.Interpolator;
+import android.widget.Toast;
+import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.tracing.SwipeHandlerProto;
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.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
+import com.android.quickstep.util.InputConsumerProxy;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.ProtoTracer;
+import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.ShelfPeekAnim;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
-import com.android.quickstep.views.LiveTileOverlay;
+import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.SwipePipToHomeAnimator;
+import com.android.quickstep.util.TransformParams;
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.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.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
/**
* Handles the navigation gestures when Launcher is the default home activity.
- * TODO: Merge this with BaseSwipeUpHandler
*/
-@TargetApi(Build.VERSION_CODES.O)
-public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q extends RecentsView>
- extends BaseSwipeUpHandler<T, Q> implements OnApplyWindowInsetsListener {
- private static final String TAG = BaseSwipeUpHandlerV2.class.getSimpleName();
+@TargetApi(Build.VERSION_CODES.R)
+public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
+ extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
+ RecentsAnimationCallbacks.RecentsAnimationListener {
+ private static final String TAG = "AbsSwipeUpHandler";
- private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
+ private static final String[] STATE_NAMES = DEBUG_STATES ? new String[17] : null;
+
+ protected final BaseActivityInterface<?, T> mActivityInterface;
+ protected final InputConsumerProxy mInputConsumerProxy;
+ protected final ActivityInitListener mActivityInitListener;
+ // Callbacks to be made once the recents animation starts
+ private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
+ protected RecentsAnimationController mRecentsAnimationController;
+ protected RecentsAnimationTargets mRecentsAnimationTargets;
+ protected T mActivity;
+ protected Q mRecentsView;
+ protected Runnable mGestureEndCallback;
+ protected MultiStateCallback mStateCallback;
+ protected boolean mCanceled;
+ private boolean mRecentsViewScrollLinked = false;
private static int getFlagForIndex(int index, String name) {
if (DEBUG_STATES) {
@@ -152,6 +191,8 @@
getFlagForIndex(14, "STATE_START_NEW_TASK");
private static final int STATE_CURRENT_TASK_FINISHED =
getFlagForIndex(15, "STATE_CURRENT_TASK_FINISHED");
+ private static final int STATE_FINISH_WITH_NO_END =
+ getFlagForIndex(16, "STATE_FINISH_WITH_NO_END");
private static final int LAUNCHER_UI_STATES =
STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_LAUNCHER_STARTED;
@@ -175,7 +216,8 @@
// Either RectFSpringAnim (if animating home) or ObjectAnimator (from mCurrentShift) otherwise
private RunningWindowAnim mRunningWindowAnim;
- private boolean mIsShelfPeeking;
+ private boolean mIsMotionPaused;
+ private boolean mHasMotionEverBeenPaused;
private boolean mContinuingLastGesture;
@@ -190,8 +232,7 @@
private boolean mPassedOverviewThreshold;
private boolean mGestureStarted;
- private int mLogAction = Touch.SWIPE;
- private int mLogDirection = Direction.UP;
+ private boolean mLogDirectionUpOrLeft = true;
private PointF mDownPos;
private boolean mIsLikelyToStartNewTask;
@@ -200,11 +241,19 @@
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
- public BaseSwipeUpHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
+ private static final long SWIPE_PIP_TO_HOME_DURATION = 425;
+ private SwipePipToHomeAnimator mSwipePipToHomeAnimator;
+ protected boolean mIsSwipingPipToHome;
+
+ public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, inputConsumer);
+ super(context, deviceState, gestureState, new TransformParams());
+ mActivityInterface = gestureState.getActivityInterface();
+ mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
+ mInputConsumerProxy =
+ new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -265,17 +314,21 @@
this::invalidateHandlerWithLauncher);
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_RESUME_LAST_TASK,
this::notifyTransitionCancelled);
+ mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED | STATE_FINISH_WITH_NO_END,
+ this::notifyTransitionCancelled);
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (!LIVE_TILE.get()) {
mStateCallback.addChangeListener(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
| STATE_SCREENSHOT_VIEW_SHOWN | STATE_CAPTURE_SCREENSHOT,
(b) -> mRecentsView.setRunningTaskHidden(!b));
}
}
- @Override
protected boolean onActivityInit(Boolean alreadyOnHome) {
- super.onActivityInit(alreadyOnHome);
+ T createdActivity = mActivityInterface.getCreatedActivity();
+ if (createdActivity != null) {
+ initTransitionEndpoints(createdActivity.getDeviceProfile());
+ }
final T activity = mActivityInterface.getCreatedActivity();
if (mActivity == activity) {
return true;
@@ -299,7 +352,6 @@
mRecentsView = activity.getOverviewPanel();
mRecentsView.setOnPageTransitionEndCallback(null);
- addLiveTileOverlay();
mStateCallback.setState(STATE_LAUNCHER_PRESENT);
if (alreadyOnHome) {
@@ -309,18 +361,14 @@
}
setupRecentsViewUi();
-
- if (mDeviceState.getNavMode() == TWO_BUTTONS) {
- // If the device is in two button mode, swiping up will show overview with predictions
- // so we need to kick off switching to the overview predictions as soon as possible
- mActivityInterface.updateOverviewPredictionState();
- }
linkRecentsViewScroll();
return true;
}
- @Override
+ /**
+ * Return true if the window should be translated horizontally if the recents view scrolls
+ */
protected boolean moveWindowWithRecentsScroll() {
return mGestureState.getEndTarget() != HOME;
}
@@ -333,7 +381,7 @@
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return;
}
- mTaskViewSimulator.setRecentsRotation(mActivity.getDisplay().getRotation());
+ mTaskViewSimulator.setOrientationState(mRecentsView.getPagedViewOrientedState());
// If we've already ended the gesture and are going home, don't prepare recents UI,
// as that will set the state as BACKGROUND_APP, overriding the animation to NORMAL.
@@ -397,15 +445,19 @@
mOnDeferredActivityLaunch);
mGestureState.runOnceAtState(STATE_END_TARGET_SET,
- () -> mDeviceState.getRotationTouchHelper().
- onEndTargetCalculated(mGestureState.getEndTarget(),
- mActivityInterface));
+ () -> {
+ mDeviceState.getRotationTouchHelper()
+ .onEndTargetCalculated(mGestureState.getEndTarget(),
+ mActivityInterface);
+
+ mRecentsView.onGestureEndTargetCalculated(mGestureState.getEndTarget());
+ });
notifyGestureStartedAsync();
}
private void onDeferredActivityLaunch() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
mActivityInterface.switchRunningTaskViewToScreenshot(
null, () -> {
mTaskAnimationManager.finishRunningRecentsAnimation(true /* toHome */);
@@ -436,11 +488,8 @@
Object traceToken = TraceHelper.INSTANCE.beginSection("logToggleRecents",
TraceHelper.FLAG_IGNORE_BINDERS);
- // Only used in debug builds
- if (LatencyTrackerCompat.isEnabled(mContext)) {
- LatencyTrackerCompat.logToggleRecents(
- (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
- }
+ LatencyTrackerCompat.logToggleRecents(
+ mContext, (int) (mLauncherFrameDrawnTime - mTouchTimeMs));
TraceHelper.INSTANCE.endSection(traceToken);
// This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
@@ -449,15 +498,20 @@
.getHighResLoadingState().setVisible(true);
}
- @Override
- public void onMotionPauseChanged(boolean isPaused) {
- setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
+ public MotionPauseDetector.OnMotionPauseListener getMotionPauseListener() {
+ return new MotionPauseDetector.OnMotionPauseListener() {
+ @Override
+ public void onMotionPauseDetected() {
+ mHasMotionEverBeenPaused = true;
+ maybeUpdateRecentsAttachedState();
+ performHapticFeedback();
+ }
- if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
- // In fully gestural nav mode, switch to overview predictions once the user has paused
- // (this is a no-op if the predictions are already in that state)
- mActivityInterface.updateOverviewPredictionState();
- }
+ @Override
+ public void onMotionPauseChanged(boolean isPaused) {
+ mIsMotionPaused = isPaused;
+ }
+ };
}
public void maybeUpdateRecentsAttachedState() {
@@ -488,7 +542,7 @@
// The window is going away so make sure recents is always visible in this case.
recentsAttachedToAppWindow = true;
} else {
- recentsAttachedToAppWindow = mIsShelfPeeking || mIsLikelyToStartNewTask;
+ recentsAttachedToAppWindow = mHasMotionEverBeenPaused || mIsLikelyToStartNewTask;
}
mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
@@ -507,7 +561,6 @@
}
}
- @Override
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
}
@@ -519,19 +572,6 @@
}
}
- @UiThread
- public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
- mAnimationFactory.setShelfState(shelfState, interpolator, duration);
- boolean wasShelfPeeking = mIsShelfPeeking;
- mIsShelfPeeking = shelfState == PEEK;
- if (mIsShelfPeeking != wasShelfPeeking) {
- maybeUpdateRecentsAttachedState();
- }
- if (shelfState.shouldPreformHaptic) {
- performHapticFeedback();
- }
- }
-
private void buildAnimationController() {
if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
@@ -562,21 +602,16 @@
updateLauncherTransitionProgress();
}
- @Override
public Intent getLaunchIntent() {
return mGestureState.getOverviewIntent();
}
+ /**
+ * Called when the value of {@link #mCurrentShift} changes
+ */
+ @UiThread
@Override
public void updateFinalShift() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsAnimationTargets != null) {
- LiveTileOverlay.INSTANCE.update(
- mTaskViewSimulator.getCurrentCropRect(),
- mTaskViewSimulator.getCurrentCornerRadius());
- }
- }
-
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
if (passed != mPassedOverviewThreshold) {
mPassedOverviewThreshold = passed;
@@ -587,6 +622,7 @@
updateSysUiFlags(mCurrentShift.value);
applyWindowTransform();
+
updateLauncherTransitionProgress();
}
@@ -624,7 +660,42 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
- super.onRecentsAnimationStart(controller, targets);
+ mRecentsAnimationController = controller;
+ mRecentsAnimationTargets = targets;
+ mTransformParams.setTargetSet(mRecentsAnimationTargets);
+ RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
+ mGestureState.getRunningTaskId());
+
+ if (runningTaskTarget != null) {
+ mTaskViewSimulator.setPreview(runningTaskTarget);
+ }
+
+ // 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) {
+ DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
+ if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+ Rect overviewStackBounds = mActivityInterface
+ .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+ dp = dp.getMultiWindowProfile(mContext,
+ new WindowBounds(overviewStackBounds, targets.homeContentInsets));
+ } else {
+ // If we are not in multi-window mode, home insets should be same as system insets.
+ dp = dp.copy(mContext);
+ }
+ dp.updateInsets(targets.homeContentInsets);
+ dp.updateIsSeascape(mContext);
+ initTransitionEndpoints(dp);
+ mTaskViewSimulator.getOrientationState().setMultiWindowMode(dp.isMultiWindowMode);
+ }
+
+ // Notify when the animation starts
+ if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+ for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+ action.run();
+ }
+ mRecentsAnimationStartCallbacks.clear();
+ }
// Only add the callback to enable the input consumer after we actually have the controller
mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
@@ -641,11 +712,21 @@
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
// Defer clearing the controller and the targets until after we've updated the state
- super.onRecentsAnimationCanceled(thumbnailData);
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
}
- @Override
+ @UiThread
public void onGestureStarted(boolean isLikelyToStartNewTask) {
+ if (mRecentsView != null) {
+ InteractionJankMonitorWrapper.begin(mRecentsView,
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
+ InteractionJankMonitorWrapper.begin(mRecentsView,
+ InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ }
notifyGestureStartedAsync();
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
@@ -668,11 +749,10 @@
/**
* Called as a result on ACTION_CANCEL to return the UI to the start state.
*/
- @Override
+ @UiThread
public void onGestureCancelled() {
updateDisplacement(0);
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
- mLogAction = Touch.SWIPE_NOOP;
handleNormalGestureEnd(0, false, new PointF(), true /* isCancel */);
}
@@ -681,25 +761,27 @@
* @param velocity The x and y components of the velocity when the gesture ends.
* @param downPos The x and y value of where the gesture started.
*/
- @Override
+ @UiThread
public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
float flingThreshold = mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_threshold_velocity);
- boolean isFling = mGestureStarted && Math.abs(endVelocity) > flingThreshold;
+ .getDimension(R.dimen.quickstep_fling_threshold_speed);
+ boolean isFling = mGestureStarted && !mIsMotionPaused
+ && Math.abs(endVelocity) > flingThreshold;
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
-
- mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
boolean isVelocityVertical = Math.abs(velocity.y) > Math.abs(velocity.x);
if (isVelocityVertical) {
- mLogDirection = velocity.y < 0 ? Direction.UP : Direction.DOWN;
+ mLogDirectionUpOrLeft = velocity.y < 0;
} else {
- mLogDirection = velocity.x < 0 ? Direction.LEFT : Direction.RIGHT;
+ mLogDirectionUpOrLeft = velocity.x < 0;
}
mDownPos = downPos;
handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
- @Override
+ /**
+ * Called to create a input proxy for the running task
+ */
+ @UiThread
protected InputConsumer createNewInputProxyHandler() {
endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
endLauncherTransitionController();
@@ -722,7 +804,16 @@
private void onSettledOnEndTarget() {
// Fast-finish the attaching animation if it's still running.
maybeUpdateRecentsAttachedState(false);
- switch (mGestureState.getEndTarget()) {
+ final GestureEndTarget endTarget = mGestureState.getEndTarget();
+ if (endTarget != NEW_TASK) {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ }
+ if (endTarget != HOME) {
+ InteractionJankMonitorWrapper.cancel(
+ InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
+ }
+ switch (endTarget) {
case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify swipe-to-home (recents animation) is finished
@@ -739,10 +830,10 @@
mStateCallback.setState(STATE_RESUME_LAST_TASK);
break;
}
- ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget());
+ ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
}
- @Override
+ /** @return Whether this was the task we were waiting to appear, and thus handled it. */
protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
@@ -777,7 +868,7 @@
if (isCancel) {
endTarget = LAST_TASK;
} else if (mDeviceState.isFullyGesturalNavMode()) {
- if (mIsShelfPeeking) {
+ if (mIsMotionPaused) {
endTarget = RECENTS;
} else if (goingToNewTask) {
endTarget = NEW_TASK;
@@ -799,7 +890,7 @@
if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !willGoToNewTaskOnSwipeUp) {
endTarget = HOME;
- } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp && !mIsShelfPeeking) {
+ } else if (mDeviceState.isFullyGesturalNavMode() && isSwipeUp) {
// If swiping at a diagonal, base end target on the faster velocity.
endTarget = NEW_TASK;
} else if (isSwipeUp) {
@@ -819,60 +910,42 @@
@UiThread
private void handleNormalGestureEnd(float endVelocity, boolean isFling, PointF velocity,
boolean isCancel) {
- PointF velocityPxPerMs = new PointF(velocity.x / 1000, velocity.y / 1000);
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
isFling, isCancel);
float endShift = endTarget.isLauncher ? 1 : 0;
final float startShift;
- Interpolator interpolator = DEACCEL;
if (!isFling) {
long expectedDuration = Math.abs(Math.round((endShift - currentShift)
* MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
startShift = currentShift;
- interpolator = endTarget == RECENTS ? OVERSHOOT_1_2 : DEACCEL;
} else {
- startShift = Utilities.boundToRange(currentShift - velocityPxPerMs.y
+ startShift = Utilities.boundToRange(currentShift - velocity.y
* getSingleFrameMs(mContext) / mTransitionDragLength, 0, mDragLengthFactor);
- float minFlingVelocity = mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_min_velocity);
- if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
- if (endTarget == RECENTS && !mDeviceState.isFullyGesturalNavMode()) {
- Interpolators.OvershootParams overshoot = new Interpolators.OvershootParams(
- startShift, endShift, endShift, endVelocity / 1000,
- mTransitionDragLength, mContext);
- endShift = overshoot.end;
- interpolator = overshoot.interpolator;
- duration = Utilities.boundToRange(overshoot.duration, MIN_OVERSHOOT_DURATION,
- MAX_SWIPE_DURATION);
- } else {
+ if (mTransitionDragLength > 0) {
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 / velocityPxPerMs.y));
+ long baseDuration = Math.round(Math.abs(distanceToTravel / velocity.y));
duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
-
- if (endTarget == RECENTS) {
- interpolator = OVERSHOOT_1_2;
- }
- }
}
}
+ Interpolator interpolator =
+ endTarget == RECENTS ? (mDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
+ ? ACCEL_DEACCEL : OVERSHOOT_1_2) : DEACCEL;
if (endTarget.isLauncher) {
mInputConsumerProxy.enable();
}
if (endTarget == HOME) {
- setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
} else if (endTarget == RECENTS) {
- LiveTileOverlay.INSTANCE.startIconAnimation();
if (mRecentsView != null) {
- int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
+ int nearestPage = mRecentsView.getDestinationPage();
if (mRecentsView.getNextPage() != nearestPage) {
// We shouldn't really scroll to the next page when swiping up to recents.
// Only allow settling on the next page if it's nearest to the center.
@@ -883,9 +956,6 @@
}
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
- if (mDeviceState.isFullyGesturalNavMode()) {
- setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
- }
}
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
@@ -897,25 +967,10 @@
mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
}
- animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
+ animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocity);
}
- private void doLogGesture(GestureEndTarget endTarget) {
- DeviceProfile dp = mDp;
- if (dp == null || mDownPos == null) {
- // We probably never received an animation controller, skip logging.
- return;
- }
-
- int pageIndex = endTarget == LAST_TASK
- ? LOG_NO_OP_PAGE_INDEX
- : mRecentsView.getNextPage();
- UserEventDispatcher.newInstance(mContext).logStateChangeAction(
- mLogAction, mLogDirection,
- (int) mDownPos.x, (int) mDownPos.y,
- ContainerType.NAVBAR, ContainerType.APP,
- endTarget.containerType,
- pageIndex);
+ private void doLogGesture(GestureEndTarget endTarget, @Nullable TaskView targetTask) {
StatsLogManager.EventEnum event;
switch (endTarget) {
case HOME:
@@ -926,30 +981,29 @@
break;
case LAST_TASK:
case NEW_TASK:
- event = (mLogDirection == Direction.LEFT)
- ? LAUNCHER_QUICKSWITCH_LEFT
+ event = mLogDirectionUpOrLeft ? LAUNCHER_QUICKSWITCH_LEFT
: LAUNCHER_QUICKSWITCH_RIGHT;
break;
default:
event = IGNORE;
}
- ComponentName componentName = mGestureState.getRunningTask().baseActivity;
- StatsLogManager.newInstance(mContext).logger()
+ StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
.withSrcState(LAUNCHER_STATE_BACKGROUND)
- .withDstState(StatsLogManager.containerTypeToAtomState(endTarget.containerType))
- .withItemInfo(getItemInfo(componentName))
- .log(event);
- }
+ .withDstState(endTarget.containerType);
+ if (targetTask != null) {
+ logger.withItemInfo(targetTask.getItemInfo());
+ }
- /**
- * Builds proto for logging
- */
- public WorkspaceItemInfo getItemInfo(ComponentName componentName) {
- WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
- placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
- placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_TASKFOREGROUND;
- placeholderInfo.intent = new Intent().setComponent(componentName);
- return placeholderInfo;
+ DeviceProfile dp = mDp;
+ if (dp == null || mDownPos == null) {
+ // We probably never received an animation controller, skip logging.
+ return;
+ }
+ int pageIndex = endTarget == LAST_TASK
+ ? LOG_NO_OP_PAGE_INDEX
+ : mRecentsView.getNextPage();
+ // TODO: set correct container using the pageIndex
+ logger.log(event);
}
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
@@ -962,7 +1016,7 @@
protected abstract HomeAnimationFactory createHomeAnimationFactory(long duration);
- private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
+ private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
@Override
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
@@ -990,32 +1044,53 @@
if (mGestureState.getEndTarget().isLauncher) {
ActivityManagerWrapper.getInstance().registerTaskStackListener(
mActivityRestartListener);
+
+ mActivityInterface.onAnimateToLauncher(mGestureState.getEndTarget(), duration);
}
if (mGestureState.getEndTarget() == HOME) {
- HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
- RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
- windowAnim.addAnimatorListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- if (mRecentsAnimationController == null) {
- // If the recents animation is interrupted, we still end the running
- // animation (not canceled) so this is still called. In that case, we can
- // skip doing any future work here for the current gesture.
- return;
- }
- // Finalize the state and notify of the change
- mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
- }
- });
getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
- windowAnim.start(mContext, velocityPxPerMs);
+ final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
+ ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
+ : null;
+ HomeAnimationFactory homeAnimFactory = createHomeAnimationFactory(duration);
+ mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome()
+ && runningTaskTarget != null
+ && runningTaskTarget.pictureInPictureParams != null
+ && TaskInfoCompat.isAutoEnterPipEnabled(
+ runningTaskTarget.pictureInPictureParams);
+ if (mIsSwipingPipToHome) {
+ mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
+ homeAnimFactory, runningTaskTarget, start);
+ mSwipePipToHomeAnimator.setDuration(SWIPE_PIP_TO_HOME_DURATION);
+ mSwipePipToHomeAnimator.setInterpolator(interpolator);
+ mSwipePipToHomeAnimator.setFloatValues(0f, 1f);
+ mSwipePipToHomeAnimator.start();
+ mRunningWindowAnim = RunningWindowAnim.wrap(mSwipePipToHomeAnimator);
+ } else {
+ mSwipePipToHomeAnimator = null;
+ RectFSpringAnim windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
+ windowAnim.addAnimatorListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case,
+ // we can skip doing any future work here for the current gesture.
+ return;
+ }
+ // Finalize the state and notify of the change
+ mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+ }
+ });
+ windowAnim.start(mContext, velocityPxPerMs);
+ mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+ }
homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
- mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
mLauncherTransitionController = null;
} else {
+ AnimatorSet animatorSet = new AnimatorSet();
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
- windowAnim.setDuration(duration).setInterpolator(interpolator);
windowAnim.addUpdateListener(valueAnimator -> {
computeRecentsScrollIfInvisible();
});
@@ -1050,11 +1125,75 @@
mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
}
});
- windowAnim.start();
- mRunningWindowAnim = RunningWindowAnim.wrap(windowAnim);
+ animatorSet.play(windowAnim);
+ if (mRecentsView != null && mDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
+ && mGestureState.getEndTarget() == RECENTS) {
+ animatorSet.play(ObjectAnimator.ofFloat(mRecentsView, RECENTS_GRID_PROGRESS, 1));
+ animatorSet.play(mTaskViewSimulator.gridProgress.animateToValue(0, 1));
+ }
+ animatorSet.setDuration(duration).setInterpolator(interpolator);
+ animatorSet.start();
+ mRunningWindowAnim = RunningWindowAnim.wrap(animatorSet);
}
}
+ private SwipePipToHomeAnimator getSwipePipToHomeAnimator(HomeAnimationFactory homeAnimFactory,
+ RemoteAnimationTargetCompat runningTaskTarget, float startProgress) {
+ // Directly animate the app to PiP (picture-in-picture) mode
+ final ActivityManager.RunningTaskInfo taskInfo = mGestureState.getRunningTask();
+ final RecentsOrientedState orientationState = mTaskViewSimulator.getOrientationState();
+ final int windowRotation = orientationState.getDisplayRotation();
+ final int homeRotation = orientationState.getRecentsActivityRotation();
+ final Rect destinationBounds = SystemUiProxy.INSTANCE.get(mContext)
+ .startSwipePipToHome(taskInfo.topActivity,
+ TaskInfoCompat.getTopActivityInfo(taskInfo),
+ runningTaskTarget.pictureInPictureParams,
+ homeRotation,
+ mDp.hotseatBarSizePx);
+ final Rect startBounds = new Rect();
+ updateProgressForStartRect(new Matrix(), startProgress).round(startBounds);
+ final SwipePipToHomeAnimator swipePipToHomeAnimator = new SwipePipToHomeAnimator(
+ runningTaskTarget.taskId,
+ taskInfo.topActivity,
+ runningTaskTarget.leash.getSurfaceControl(),
+ TaskInfoCompat.getPipSourceRectHint(runningTaskTarget.pictureInPictureParams),
+ TaskInfoCompat.getWindowConfigurationBounds(taskInfo),
+ startBounds,
+ destinationBounds,
+ mRecentsView);
+ // We would assume home and app window always in the same rotation While homeRotation
+ // is not ROTATION_0 (which implies the rotation is turned on in launcher settings).
+ if (homeRotation == ROTATION_0
+ && (windowRotation == ROTATION_90 || windowRotation == ROTATION_270)) {
+ swipePipToHomeAnimator.setFromRotation(mTaskViewSimulator, windowRotation);
+ }
+ swipePipToHomeAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean mHasAnimationEnded;
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (mHasAnimationEnded) return;
+ // Ensure Launcher ends in NORMAL state, we intentionally skip other callbacks
+ // since they are not relevant in this swipe-pip-to-home case.
+ homeAnimFactory.createActivityAnimationToHome().dispatchOnStart();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mHasAnimationEnded) return;
+ mHasAnimationEnded = true;
+ if (mRecentsAnimationController == null) {
+ // If the recents animation is interrupted, we still end the running
+ // animation (not canceled) so this is still called. In that case, we can
+ // skip doing any future work here for the current gesture.
+ return;
+ }
+ // Finalize the state and notify of the change
+ mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
+ }
+ });
+ return swipePipToHomeAnimator;
+ }
+
private void computeRecentsScrollIfInvisible() {
if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
// Views typically don't compute scroll when invisible as an optimization,
@@ -1087,13 +1226,6 @@
});
anim.addAnimatorListener(new AnimationSuccessListener() {
@Override
- public void onAnimationStart(Animator animation) {
- if (mActivity != null) {
- removeLiveTileOverlay();
- }
- }
-
- @Override
public void onAnimationSuccess(Animator animator) {
if (mRecentsView != null) {
mRecentsView.post(mRecentsView::resetTaskVisuals);
@@ -1103,20 +1235,23 @@
mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
}
});
+ if (mRecentsAnimationTargets != null) {
+ mRecentsAnimationTargets.addReleaseCheck(anim);
+ }
return anim;
}
- @Override
public void onConsumerAboutToBeSwitched() {
if (mActivity != null) {
// In the off chance that the gesture ends before Launcher is started, we should clear
// the callback here so that it doesn't update with the wrong state
mActivity.clearRunOnceOnStartCallback();
- resetLauncherListenersAndOverlays();
+ resetLauncherListeners();
}
if (mGestureState.getEndTarget() != null && !mGestureState.isRunningAnimationToLauncher()) {
cancelCurrentAnimation();
} else {
+ mStateCallback.setStateOnUiThread(STATE_FINISH_WITH_NO_END);
reset();
}
}
@@ -1129,21 +1264,13 @@
private void resumeLastTask() {
mRecentsAnimationController.finish(false /* toRecents */, null);
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
- doLogGesture(LAST_TASK);
+ doLogGesture(LAST_TASK, null);
reset();
}
@UiThread
private void startNewTask() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal);
- } else {
- startNewTaskInternal();
- }
- }
-
- @UiThread
- private void startNewTaskInternal() {
+ TaskView taskToLaunch = mRecentsView == null ? null : mRecentsView.getNextPageTaskView();
startNewTask(success -> {
if (!success) {
reset();
@@ -1152,13 +1279,23 @@
endLauncherTransitionController();
updateSysUiFlags(1 /* windowProgress == overview */);
}
- doLogGesture(NEW_TASK);
+ doLogGesture(NEW_TASK, taskToLaunch);
});
}
- @Override
+ /**
+ * Called when we successfully startNewTask() on the task that was previously running. Normally
+ * we call resumeLastTask() when returning to the previously running task, but this handles a
+ * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+ * start A again to ensure it stays on top.
+ */
+ @androidx.annotation.CallSuper
protected void onRestartPreviouslyAppearedTask() {
- super.onRestartPreviouslyAppearedTask();
+ // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+ // appeared.
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false, null);
+ }
reset();
}
@@ -1192,11 +1329,10 @@
endLauncherTransitionController();
mRecentsView.onGestureAnimationEnd();
- resetLauncherListenersAndOverlays();
+ resetLauncherListeners();
}
private void endLauncherTransitionController() {
- setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
if (mLauncherTransitionController != null) {
// End the animation, but stay at the same visual progress.
mLauncherTransitionController.getNormalController().dispatchSetInterpolator(
@@ -1206,13 +1342,12 @@
}
}
- private void resetLauncherListenersAndOverlays() {
+ private void resetLauncherListeners() {
// Reset the callback for deferred activity launches
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (!LIVE_TILE.get()) {
mActivityInterface.setOnDeferredActivityLaunchCallback(null);
}
mActivity.getRootView().setOnApplyWindowInsetsListener(null);
- removeLiveTileOverlay();
}
private void notifyTransitionCancelled() {
@@ -1229,7 +1364,7 @@
protected void switchToScreenshot() {
final int runningTaskId = mGestureState.getRunningTaskId();
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.getController().setWillFinishToHome(true);
// Update the screenshot of the task
@@ -1247,36 +1382,58 @@
if (mRecentsAnimationController != null) {
// Update the screenshot of the task
if (mTaskSnapshot == null) {
- mTaskSnapshot = mRecentsAnimationController.screenshotTask(runningTaskId);
+ UI_HELPER_EXECUTOR.execute(() -> {
+ if (mRecentsAnimationController == null) return;
+ final ThumbnailData taskSnapshot =
+ mRecentsAnimationController.screenshotTask(runningTaskId);
+ MAIN_EXECUTOR.execute(() -> {
+ mTaskSnapshot = taskSnapshot;
+ if (!updateThumbnail(runningTaskId)) {
+ setScreenshotCapturedState();
+ }
+ });
+ });
+ return;
}
- final TaskView taskView;
- if (mGestureState.getEndTarget() == HOME) {
- // Capture the screenshot before finishing the transition to home to ensure it's
- // taken in the correct orientation, but no need to update the thumbnail.
- taskView = null;
- } else {
- taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
- }
- if (taskView != null && !mCanceled) {
- // Defer finishing the animation until the next launcher frame with the
- // new thumbnail
- finishTransitionPosted = ViewUtils.postDraw(taskView,
- () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
- this::isCanceled);
- }
+ finishTransitionPosted = updateThumbnail(runningTaskId);
}
if (!finishTransitionPosted) {
- // If we haven't posted a draw callback, set the state immediately.
- Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
- TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
- mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
- TraceHelper.INSTANCE.endSection(traceToken);
+ setScreenshotCapturedState();
}
}
}
+ // Returns whether finish transition was posted.
+ private boolean updateThumbnail(int runningTaskId) {
+ boolean finishTransitionPosted = false;
+ final TaskView taskView;
+ if (mGestureState.getEndTarget() == HOME) {
+ // Capture the screenshot before finishing the transition to home to ensure it's
+ // taken in the correct orientation, but no need to update the thumbnail.
+ taskView = null;
+ } else {
+ taskView = mRecentsView.updateThumbnail(runningTaskId, mTaskSnapshot);
+ }
+ if (taskView != null && !mCanceled) {
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
+ () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
+ this::isCanceled);
+ }
+ return finishTransitionPosted;
+ }
+
+ private void setScreenshotCapturedState() {
+ // If we haven't posted a draw callback, set the state immediately.
+ Object traceToken = TraceHelper.INSTANCE.beginSection(SCREENSHOT_CAPTURED_EVT,
+ TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS);
+ mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
+ TraceHelper.INSTANCE.endSection(traceToken);
+ }
+
private void finishCurrentTransitionToRecents() {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else if (!hasTargets() || mRecentsAnimationController == null) {
// If there are no targets or the animation not started, then there is nothing to finish
@@ -1293,42 +1450,313 @@
// If there are no targets or the animation not started, then there is nothing to finish
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else {
+ maybeFinishSwipePipToHome();
finishRecentsControllerToHome(
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
- doLogGesture(HOME);
+ doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView());
+ }
+
+ /**
+ * Resets the {@link #mIsSwipingPipToHome} and notifies SysUI that transition is finished
+ * if applicable. This should happen before {@link #finishRecentsControllerToHome(Runnable)}.
+ */
+ private void maybeFinishSwipePipToHome() {
+ if (mIsSwipingPipToHome && mSwipePipToHomeAnimator != null) {
+ SystemUiProxy.INSTANCE.get(mContext).stopSwipePipToHome(
+ mSwipePipToHomeAnimator.getComponentName(),
+ mSwipePipToHomeAnimator.getDestinationBounds());
+ mRecentsAnimationController.setFinishTaskBounds(
+ mSwipePipToHomeAnimator.getTaskId(),
+ mSwipePipToHomeAnimator.getDestinationBounds());
+ mIsSwipingPipToHome = false;
+ }
}
protected abstract void finishRecentsControllerToHome(Runnable callback);
+ private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ if (mRecentsView.getRunningTaskIndex() != -1
+ && mRecentsView.getRunningTaskId() == task.taskId
+ && mRecentsAnimationTargets.hasTask(task.taskId)) {
+ launchOtherTaskInLiveTileMode(task.taskId, mRecentsAnimationTargets.apps);
+ }
+ ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
+ mLiveTileRestartListener);
+ }
+ };
+
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
endLauncherTransitionController();
mActivityInterface.onSwipeUpToRecentsComplete();
- if (mRecentsAnimationController != null) {
- mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
- true /* screenshot */);
- }
mRecentsView.onSwipeUpAnimationSuccess();
+ if (LIVE_TILE.get()) {
+ mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
+ appearedTaskTarget -> {
+ RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
+ mRecentsAnimationTargets.apps,
+ mRecentsAnimationTargets.apps.length + 1);
+ apps[apps.length - 1] = appearedTaskTarget;
+ launchOtherTaskInLiveTileMode(appearedTaskTarget.taskId, apps);
+ });
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(
+ mLiveTileRestartListener);
+ }
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
- doLogGesture(RECENTS);
+ doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
reset();
}
- private void addLiveTileOverlay() {
- if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
- mRecentsView.setLiveTileOverlayAttached(true);
+ private void launchOtherTaskInLiveTileMode(int taskId, RemoteAnimationTargetCompat[] apps) {
+ AnimatorSet anim = new AnimatorSet();
+ TaskView taskView = mRecentsView.getTaskView(taskId);
+ if (taskView == null || !mRecentsView.isTaskViewVisible(taskView)) {
+ // TODO: Refine this animation.
+ SurfaceTransactionApplier surfaceApplier =
+ new SurfaceTransactionApplier(mActivity.getDragLayer());
+ ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+ appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+ appAnimator.setInterpolator(ACCEL_DEACCEL);
+ appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+ @Override
+ public void onUpdate(float percent) {
+ SurfaceParams.Builder builder = new SurfaceParams.Builder(
+ apps[apps.length - 1].leash);
+ Matrix matrix = new Matrix();
+ matrix.postScale(percent, percent);
+ matrix.postTranslate(mDp.widthPx * (1 - percent) / 2,
+ mDp.heightPx * (1 - percent) / 2);
+ builder.withAlpha(percent).withMatrix(matrix);
+ surfaceApplier.scheduleApply(builder.build());
+ }
+ });
+ anim.play(appAnimator);
+ } else {
+ TaskViewUtils.composeRecentsLaunchAnimator(
+ anim, taskView, apps,
+ mRecentsAnimationTargets.wallpapers, true /* launcherClosing */,
+ mActivity.getStateManager(), mRecentsView,
+ mActivityInterface.getDepthController());
}
- }
+ anim.addListener(new AnimatorListenerAdapter(){
- private void removeLiveTileOverlay() {
- LiveTileOverlay.INSTANCE.detach(mActivity.getRootView().getOverlay());
- mRecentsView.setLiveTileOverlayAttached(false);
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ cleanUp(false);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ cleanUp(true);
+ }
+
+ private void cleanUp(boolean canceled) {
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false /* toRecents */,
+ null /* onFinishComplete */);
+ if (canceled) {
+ mRecentsAnimationController = null;
+ } else {
+ mActivityInterface.onLaunchTaskSuccess();
+ }
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+ }
+ });
+ anim.start();
}
private static boolean isNotInRecents(RemoteAnimationTargetCompat 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(
+ mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+ }
+
+ protected void performHapticFeedback() {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+ }
+
+ public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
+ return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
+ }
+
+ public void setGestureEndCallback(Runnable gestureEndCallback) {
+ mGestureEndCallback = gestureEndCallback;
+ }
+
+ protected void linkRecentsViewScroll() {
+ SurfaceTransactionApplier.create(mRecentsView, applier -> {
+ mTransformParams.setSyncTransactionApplier(applier);
+ runOnRecentsAnimationStart(() ->
+ mRecentsAnimationTargets.addReleaseCheck(applier));
+ });
+
+ mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ if (moveWindowWithRecentsScroll()) {
+ updateFinalShift();
+ }
+ });
+ runOnRecentsAnimationStart(() ->
+ mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
+ mRecentsAnimationTargets));
+ mRecentsViewScrollLinked = true;
+ }
+
+ protected void startNewTask(Consumer<Boolean> resultCallback) {
+ // Launch the task user scrolled to (mRecentsView.getNextPage()).
+ if (!mCanceled) {
+ TaskView nextTask = mRecentsView.getNextPageTaskView();
+ if (nextTask != null) {
+ int taskId = nextTask.getTask().key.id;
+ mGestureState.updateLastStartedTaskId(taskId);
+ boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+ .contains(taskId);
+ nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
+ success -> {
+ resultCallback.accept(success);
+ if (success) {
+ if (hasTaskPreviouslyAppeared) {
+ onRestartPreviouslyAppearedTask();
+ }
+ } else {
+ mActivityInterface.onLaunchTaskFailed();
+ nextTask.notifyTaskLaunchFailed(TAG);
+ mRecentsAnimationController.finish(true /* toRecents */, null);
+ }
+ }, MAIN_EXECUTOR.getHandler());
+ } else {
+ mActivityInterface.onLaunchTaskFailed();
+ Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
+ mRecentsAnimationController.finish(true /* toRecents */, null);
+ }
+ }
+ mCanceled = false;
+ }
+
+ /**
+ * Runs the given {@param action} if the recents animation has already started, or queues it to
+ * be run when it is next started.
+ */
+ protected void runOnRecentsAnimationStart(Runnable action) {
+ if (mRecentsAnimationTargets == null) {
+ mRecentsAnimationStartCallbacks.add(action);
+ } else {
+ action.run();
+ }
+ }
+
+ /**
+ * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
+ * @return whether the recents animation has started and there are valid app targets.
+ */
+ protected boolean hasTargets() {
+ return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
+ }
+
+ @Override
+ public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ if (mRecentsAnimationController != null) {
+ if (handleTaskAppeared(appearedTaskTarget)) {
+ mRecentsAnimationController.finish(false /* toRecents */,
+ null /* onFinishComplete */);
+ mActivityInterface.onLaunchTaskSuccess();
+ 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.
+ */
+ protected int getLastAppearedTaskIndex() {
+ return mGestureState.getLastAppearedTaskId() != -1
+ ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
+ : mRecentsView.getRunningTaskIndex();
+ }
+
+ /**
+ * @return Whether we are continuing a gesture that already landed on a new task,
+ * but before that task appeared.
+ */
+ protected boolean hasStartedNewTask() {
+ return mGestureState.getLastStartedTaskId() != -1;
+ }
+
+ /**
+ * Registers a callback to run when the activity is ready.
+ * @param intent The intent that will be used to start the activity if it doesn't exist already.
+ */
+ public void initWhenReady(Intent intent) {
+ // Preload the plan
+ RecentsModel.INSTANCE.get(mContext).getTasks(null);
+
+ mActivityInitListener.register(intent);
+ }
+
+ /**
+ * Applies the transform on the recents animation
+ */
+ protected void applyWindowTransform() {
+ if (mWindowTransitionController != null) {
+ mWindowTransitionController.setProgress(mCurrentShift.value, mDragLengthFactor);
+ }
+ // No need to apply any transform if there is ongoing swipe-pip-to-home animator since
+ // that animator handles the leash solely.
+ if (mRecentsAnimationTargets != null && !mIsSwipingPipToHome) {
+ if (mRecentsViewScrollLinked) {
+ mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+ }
+ mTaskViewSimulator.apply(mTransformParams);
+ }
+ ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
+ }
+
+ /**
+ * Used for winscope tracing, see launcher_trace.proto
+ * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+ * @param inputConsumerProto The parent of this proto message.
+ */
+ public void writeToProto(InputConsumerProto.Builder inputConsumerProto) {
+ SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder();
+
+ mGestureState.writeToProto(swipeHandlerProto);
+
+ swipeHandlerProto.setIsRecentsAttachedToAppWindow(
+ mAnimationFactory.isRecentsAttachedToAppWindow());
+ swipeHandlerProto.setScrollOffset(mRecentsView == null
+ ? 0
+ : mRecentsView.getScrollOffset());
+ swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value);
+
+ inputConsumerProto.setSwipeHandler(swipeHandlerProto);
+ }
+
+ public interface Factory {
+
+ AbsSwipeUpHandler<StatefulActivity<?>, RecentsView> newHandler(
+ GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
similarity index 94%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
rename to quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index 55f5424..d159fa0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -33,6 +33,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator;
@@ -119,6 +120,11 @@
OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
}
+ TaskbarController taskbarController = mActivityInterface.getTaskbarController();
+ if (taskbarController != null) {
+ pa.add(taskbarController.createAnimToLauncher(OVERVIEW, getRecentsLaunchDuration()));
+ }
+
RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
wallpaperTargets, MODE_CLOSING);
@@ -132,9 +138,8 @@
TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
tsv.setDp(mActivity.getDeviceProfile());
+ tsv.setOrientationState(mRecentsView.getPagedViewOrientedState());
tsv.setPreview(runningTaskTarget);
- tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
- mRecentsView.getPagedViewOrientedState().getDisplayRotation());
TransformParams params = new TransformParams()
.setTargetSet(targets)
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index bdf525d..ce14197 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -18,11 +18,8 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
@@ -37,7 +34,6 @@
import android.graphics.Rect;
import android.os.Build;
import android.view.MotionEvent;
-import android.view.animation.Interpolator;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -49,12 +45,12 @@
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
-import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -126,6 +122,11 @@
return null;
}
+ @Nullable
+ public TaskbarController getTaskbarController() {
+ return null;
+ }
+
public final boolean isResumed() {
ACTIVITY_TYPE activity = getCreatedActivity();
return activity != null && activity.hasBeenResumed();
@@ -152,20 +153,15 @@
return deviceState.isInDeferredGestureRegion(ev);
}
- public abstract void onExitOverview(RotationTouchHelper deviceState,
- Runnable exitRunnable);
-
/**
- * Updates the prediction state to the overview state.
+ * @return Whether the gesture in progress should be cancelled.
*/
- public void updateOverviewPredictionState() {
- // By public overview predictions are not supported
+ public boolean shouldCancelCurrentGesture() {
+ return false;
}
- /**
- * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
- */
- public abstract int getContainerType();
+ public abstract void onExitOverview(RotationTouchHelper deviceState,
+ Runnable exitRunnable);
public abstract boolean isInLiveTileMode();
@@ -201,33 +197,26 @@
*/
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect,
PagedOrientationHandler orientedState) {
- calculateTaskSize(context, dp, getExtraSpace(context, dp, orientedState),
- outRect, orientedState);
+ calculateTaskSize(context, dp, getExtraSpace(context, dp, orientedState), outRect);
}
protected abstract float getExtraSpace(Context context, DeviceProfile dp,
PagedOrientationHandler orientedState);
- private void calculateTaskSize(
- Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect,
- PagedOrientationHandler orientationHandler) {
+ private void calculateTaskSize(Context context, DeviceProfile dp, float extraVerticalSpace,
+ Rect outRect) {
Resources res = context.getResources();
- final boolean showLargeTaskSize = showOverviewActions(context) ||
- hideShelfInTwoButtonLandscape(context, orientationHandler);
final int paddingResId;
if (dp.isMultiWindowMode) {
paddingResId = R.dimen.multi_window_task_card_horz_space;
} else if (dp.isVerticalBarLayout()) {
paddingResId = R.dimen.landscape_task_card_horz_space;
- } else if (showLargeTaskSize) {
- paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
} else {
- paddingResId = R.dimen.portrait_task_card_horz_space;
+ paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
}
float paddingHorz = res.getDimension(paddingResId);
- float paddingVert = showLargeTaskSize
- ? 0 : res.getDimension(R.dimen.task_card_vert_space);
+ float paddingVert = 0;
calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
@@ -300,21 +289,36 @@
return overviewActionsHeight;
}
+ /**
+ * Called when the gesture ends and the animation starts towards the given target. No-op by
+ * default, but subclasses can override to add an additional animation with the same duration.
+ */
+ public void onAnimateToLauncher(GestureState.GestureEndTarget endTarget, long duration) {
+ }
+
+ /**
+ * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
+ * @param systemUiStateFlags The latest SystemUiStateFlags
+ */
+ public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+ }
+
public interface AnimationFactory {
void createActivityInterface(long transitionLength);
default void onTransitionCancelled() { }
- default void setShelfState(ShelfPeekAnim.ShelfAnimState animState,
- Interpolator interpolator, long duration) { }
-
/**
* @param attached Whether to show RecentsView alongside the app window. If false, recents
* will be hidden by some property we can animate, e.g. alpha.
* @param animate Whether to animate recents to/from its new attached state.
*/
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
+
+ default boolean isRecentsAttachedToAppWindow() {
+ return false;
+ }
}
class DefaultAnimationFactory implements AnimationFactory {
@@ -406,6 +410,11 @@
fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
}
+ @Override
+ public boolean isRecentsAttachedToAppWindow() {
+ return mIsAttachedToWindow;
+ }
+
protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
// Scale down recents from being full screen to being in overview.
RecentsView recentsView = activity.getOverviewPanel();
@@ -415,7 +424,8 @@
}
}
- protected static boolean showOverviewActions(Context context) {
- return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
+ /** Called when OverviewService is bound to this process */
+ void onOverviewServiceBound() {
+ // Do nothing
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
similarity index 87%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
rename to quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 3898f0b..96e4f38 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -28,7 +28,6 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.RecentsState;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -59,9 +58,7 @@
calculateTaskSize(context, dp, outRect, orientationHandler);
if (dp.isVerticalBarLayout()
&& SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
- Rect targetInsets = dp.getInsets();
- int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
- return dp.hotseatBarSizePx + hotseatInset;
+ return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
} else {
return dp.heightPx - outRect.bottom;
}
@@ -145,15 +142,6 @@
}
@Override
- public int getContainerType() {
- RecentsActivity activity = getCreatedActivity();
- boolean visible = activity != null && activity.isStarted() && activity.hasWindowFocus();
- return visible
- ? LauncherLogProto.ContainerType.OTHER_LAUNCHER_APP
- : LauncherLogProto.ContainerType.APP;
- }
-
- @Override
public boolean isInLiveTileMode() {
return false;
}
@@ -171,8 +159,6 @@
@Override
protected float getExtraSpace(Context context, DeviceProfile dp,
PagedOrientationHandler orientationHandler) {
- return showOverviewActions(context)
- ? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
- : 0;
+ return context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
rename to quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index f60a50b..a80c111 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -22,12 +22,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_REMOTE_CALLBACK;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
@@ -71,7 +73,7 @@
*/
@TargetApi(Build.VERSION_CODES.R)
public class FallbackSwipeHandler extends
- BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
+ AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
/**
* Message used for receiving gesture nav contract information. We use a static messenger to
@@ -126,7 +128,11 @@
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
mActiveAnimationFactory.addGestureContract(intent);
- mContext.startActivity(intent, options.toBundle());
+ try {
+ mContext.startActivity(intent, options.toBundle());
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ mContext.startActivity(createHomeIntent());
+ }
return mActiveAnimationFactory;
}
@@ -294,8 +300,12 @@
if (mSurfaceControl != null) {
currentRect.roundOut(mTempRect);
Transaction t = new Transaction();
- t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
- t.apply();
+ try {
+ t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
+ t.apply();
+ } catch (RuntimeException e) {
+ // Ignore
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 00b5eb9..8d67ee6 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -15,6 +15,9 @@
*/
package com.android.quickstep;
+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.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import android.annotation.TargetApi;
@@ -23,7 +26,8 @@
import android.os.Build;
import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.tracing.GestureStateProto;
+import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -44,19 +48,22 @@
* Defines the end targets of a gesture and the associated state.
*/
public enum GestureEndTarget {
- HOME(true, ContainerType.WORKSPACE, false),
+ HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME),
- RECENTS(true, ContainerType.TASKSWITCHER, true),
+ RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS),
- NEW_TASK(false, ContainerType.APP, true),
+ NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
+ GestureStateProto.GestureEndTarget.NEW_TASK),
- LAST_TASK(false, ContainerType.APP, true);
+ LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
+ GestureStateProto.GestureEndTarget.LAST_TASK);
- GestureEndTarget(boolean isLauncher, int containerType,
- boolean recentsAttachedToAppWindow) {
+ GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
+ GestureStateProto.GestureEndTarget protoEndTarget) {
this.isLauncher = isLauncher;
this.containerType = containerType;
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
+ this.protoEndTarget = protoEndTarget;
}
/** Whether the target is in the launcher activity. Implicitly, if the end target is going
@@ -66,6 +73,8 @@
public final int containerType;
/** Whether RecentsView should be attached to the window as we animate to this target */
public final boolean recentsAttachedToAppWindow;
+ /** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */
+ public final GestureStateProto.GestureEndTarget protoEndTarget;
}
private static final String TAG = "GestureState";
@@ -133,6 +142,9 @@
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
+ /** The time when the swipe up gesture is triggered. */
+ private long mSwipeUpStartTimeMs;
+
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
mHomeIntent = componentObserver.getHomeIntent();
mOverviewIntent = componentObserver.getOverviewIntent();
@@ -334,6 +346,14 @@
mStateCallback.setState(STATE_RECENTS_ANIMATION_ENDED);
}
+ void setSwipeUpStartTimeMs(long uptimeMs) {
+ mSwipeUpStartTimeMs = uptimeMs;
+ }
+
+ long getSwipeUpStartTimeMs() {
+ return mSwipeUpStartTimeMs;
+ }
+
public void dump(PrintWriter pw) {
pw.println("GestureState:");
pw.println(" gestureID=" + mGestureId);
@@ -343,4 +363,17 @@
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
}
+
+ /**
+ * Used for winscope tracing, see launcher_trace.proto
+ * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+ * @param swipeHandlerProto The parent of this proto message.
+ */
+ public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) {
+ GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder();
+ gestureStateProto.setEndTarget(mEndTarget == null
+ ? GestureStateProto.GestureEndTarget.UNSET
+ : mEndTarget.protoEndTarget);
+ swipeHandlerProto.setGestureState(gestureStateProto);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
similarity index 73%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java
rename to quickstep/src/com/android/quickstep/ImageActionsApi.java
index ba8ba33..8cb64c2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -22,11 +22,14 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.ImageActionUtils.persistBitmapAndStartActivity;
+import android.app.prediction.AppTarget;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -61,6 +64,20 @@
*/
@UiThread
public void shareWithExplicitIntent(@Nullable Rect crop, Intent intent) {
+ addImageAndSendIntent(crop, intent, false);
+ }
+
+ /**
+ * Share the image this api was constructed with using the provided intent. The implementation
+ * should set the intent's data field to the URI pointing to the image.
+ */
+ @UiThread
+ public void shareAsDataWithExplicitIntent(@Nullable Rect crop, Intent intent) {
+ addImageAndSendIntent(crop, intent, true);
+ }
+
+ @UiThread
+ private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData) {
if (mBitmapSupplier.get() == null) {
Log.e(TAG, "No snapshot available, not starting share.");
return;
@@ -68,20 +85,22 @@
UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
- intentForUri
- .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
- .putExtra(EXTRA_STREAM, uri);
+ intentForUri.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
+ if (setData) {
+ intentForUri.setData(uri);
+ } else {
+ intentForUri.putExtra(EXTRA_STREAM, uri);
+ }
return new Intent[]{intentForUri};
}, TAG));
-
}
/**
* Share the image this api was constructed with.
*/
@UiThread
- public void startShareActivity() {
- ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, null, null, TAG);
+ public void startShareActivity(Rect crop) {
+ ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, crop, null, TAG);
}
/**
@@ -96,4 +115,12 @@
ImageActionUtils.saveScreenshot(mSystemUiProxy, screenshot, screenshotBounds, visibleInsets,
task);
}
+
+ /**
+ * Share the image when user taps on overview share targets.
+ */
+ @UiThread
+ public void shareImage(RectF rectF, ShortcutInfo shortcutInfo, AppTarget appTarget) {
+ ImageActionUtils.shareImage(mContext, mBitmapSupplier, rectF, shortcutInfo, appTarget, TAG);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/InputConsumer.java b/quickstep/src/com/android/quickstep/InputConsumer.java
index ec720d5..0b2a057 100644
--- a/quickstep/src/com/android/quickstep/InputConsumer.java
+++ b/quickstep/src/com/android/quickstep/InputConsumer.java
@@ -21,6 +21,9 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
+import com.android.launcher3.tracing.InputConsumerProto;
+import com.android.launcher3.tracing.TouchInteractionServiceProto;
+
@TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer {
@@ -35,6 +38,7 @@
int TYPE_RESET_GESTURE = 1 << 8;
int TYPE_OVERSCROLL = 1 << 9;
int TYPE_SYSUI_OVERLAY = 1 << 10;
+ int TYPE_ONE_HANDED = 1 << 11;
String[] NAMES = new String[] {
"TYPE_NO_OP", // 0
@@ -47,7 +51,8 @@
"TYPE_OVERVIEW_WITHOUT_FOCUS", // 7
"TYPE_RESET_GESTURE", // 8
"TYPE_OVERSCROLL", // 9
- "TYPE_SYSUI_OVERLAY" // 10
+ "TYPE_SYSUI_OVERLAY", // 10
+ "TYPE_ONE_HANDED", // 11
};
InputConsumer NO_OP = () -> TYPE_NO_OP;
@@ -114,4 +119,21 @@
}
return name.toString();
}
+
+ /**
+ * Used for winscope tracing, see launcher_trace.proto
+ * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+ * @param serviceProto The parent of this proto message.
+ */
+ default void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
+ InputConsumerProto.Builder inputConsumerProto = InputConsumerProto.newBuilder();
+ inputConsumerProto.setName(getName());
+ writeToProtoInternal(inputConsumerProto);
+ serviceProto.setInputConsumer(inputConsumerProto);
+ }
+
+ /**
+ * @see #writeToProto - allows subclasses to write additional info to the proto.
+ */
+ default void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
similarity index 69%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
rename to quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index cd4be18..3f3e5ad 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -16,18 +16,17 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
import static com.android.quickstep.SysUINavigationMode.getMode;
-import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
-import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.util.Log;
-import android.view.animation.Interpolator;
+import android.view.MotionEvent;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -38,20 +37,18 @@
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.TaskbarController;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
import com.android.quickstep.views.RecentsView;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -76,26 +73,13 @@
PagedOrientationHandler orientationHandler) {
calculateTaskSize(context, dp, outRect, orientationHandler);
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
- Rect targetInsets = dp.getInsets();
- int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
- return dp.hotseatBarSizePx + hotseatInset;
+ return dp.isSeascape() ? outRect.left : (dp.widthPx - outRect.right);
} else {
return LayoutUtils.getShelfTrackingDistance(context, dp, orientationHandler);
}
}
@Override
- public void onSwipeUpToRecentsComplete() {
- super.onSwipeUpToRecentsComplete();
- Launcher launcher = getCreatedActivity();
- if (launcher != null) {
- RecentsView recentsView = launcher.getOverviewPanel();
- DiscoveryBounce.showForOverviewIfNeeded(launcher,
- recentsView.getPagedOrientationHandler());
- }
- }
-
- @Override
public void onSwipeUpToHomeComplete(RecentsAnimationDeviceState deviceState) {
Launcher launcher = getCreatedActivity();
if (launcher == null) {
@@ -123,26 +107,10 @@
notifyRecentsOfOrientation(deviceState.getRotationTouchHelper());
DefaultAnimationFactory factory = new DefaultAnimationFactory(callback) {
@Override
- public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator,
- long duration) {
- mActivity.getShelfPeekAnim().setShelfState(shelfState, interpolator, duration);
- }
-
- @Override
protected void createBackgroundToOverviewAnim(BaseQuickstepLauncher activity,
PendingAnimation pa) {
super.createBackgroundToOverviewAnim(activity, pa);
- if (!activity.getDeviceProfile().isVerticalBarLayout()
- && SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
- // Don't animate the shelf when the mode is NO_BUTTON, because we
- // update it atomically.
- pa.add(activity.getStateManager().createStateElementAnimation(
- INDEX_SHELF_ANIM,
- BACKGROUND_APP.getVerticalProgress(activity),
- OVERVIEW.getVerticalProgress(activity)));
- }
-
// Animate the blur and wallpaper zoom
float fromDepthRatio = BACKGROUND_APP.getDepth(activity);
float toDepthRatio = OVERVIEW.getDepth(activity);
@@ -193,6 +161,16 @@
@Nullable
@Override
+ public TaskbarController getTaskbarController() {
+ BaseQuickstepLauncher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return null;
+ }
+ return launcher.getTaskbarController();
+ }
+
+ @Nullable
+ @Override
public RecentsView getVisibleRecentsView() {
Launcher launcher = getVisibleLauncher();
return launcher != null && launcher.getStateManager().getState().overviewUi
@@ -209,18 +187,11 @@
@Override
public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "switchToRecentsIfVisible");
- }
Launcher launcher = getVisibleLauncher();
if (launcher == null) {
return false;
}
- launcher.getUserEventDispatcher().logActionCommand(
- LauncherLogProto.Action.Command.RECENTS_BUTTON,
- getContainerType(),
- LauncherLogProto.ContainerType.TASKSWITCHER);
launcher.getStateManager().goToState(OVERVIEW,
launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
return true;
@@ -262,23 +233,6 @@
}
@Override
- public void updateOverviewPredictionState() {
- Launcher launcher = getCreatedActivity();
- if (launcher == null) {
- return;
- }
- PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
- PredictionUiStateManager.Client.OVERVIEW);
- }
-
- @Override
- public int getContainerType() {
- final Launcher launcher = getVisibleLauncher();
- return launcher != null ? launcher.getStateManager().getState().containerType
- : LauncherLogProto.ContainerType.APP;
- }
-
- @Override
public boolean isInLiveTileMode() {
Launcher launcher = getCreatedActivity();
return launcher != null && launcher.getStateManager().getState() == OVERVIEW &&
@@ -311,35 +265,66 @@
@Override
protected float getExtraSpace(Context context, DeviceProfile dp,
PagedOrientationHandler orientationHandler) {
- if ((dp.isVerticalBarLayout() && !showOverviewActions(context))
- || hideShelfInTwoButtonLandscape(context, orientationHandler)) {
- return 0;
- } else {
- Resources res = context.getResources();
- if (showOverviewActions(context)) {
- //TODO: this needs to account for the swipe gesture height and accessibility
- // UI when shown.
- float actionsBottomMargin = 0;
- if (!dp.isVerticalBarLayout()) {
- if (getMode(context) == Mode.THREE_BUTTONS) {
- actionsBottomMargin = res.getDimensionPixelSize(
- R.dimen.overview_actions_bottom_margin_three_button);
- } else {
- actionsBottomMargin = res.getDimensionPixelSize(
- R.dimen.overview_actions_bottom_margin_gesture);
- }
- }
- float actionsHeight = actionsBottomMargin
- + res.getDimensionPixelSize(R.dimen.overview_actions_height);
- return actionsHeight;
+ Resources res = context.getResources();
+ //TODO: this needs to account for the swipe gesture height and accessibility
+ // UI when shown.
+ float actionsBottomMargin = 0;
+ if (!dp.isVerticalBarLayout()) {
+ if (getMode(context) == Mode.THREE_BUTTONS) {
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_three_button);
} else {
- return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_extra_vertical_size)
- + res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_bottom_padding);
+ actionsBottomMargin = res.getDimensionPixelSize(
+ R.dimen.overview_actions_bottom_margin_gesture);
}
}
+ float actionsHeight = actionsBottomMargin
+ + res.getDimensionPixelSize(R.dimen.overview_actions_height);
+ return actionsHeight;
}
-}
\ No newline at end of file
+ @Override
+ void onOverviewServiceBound() {
+ final BaseQuickstepLauncher activity = getCreatedActivity();
+ if (activity == null) return;
+ activity.getAppTransitionManager().registerRemoteTransitions();
+ }
+
+ @Override
+ public void onAnimateToLauncher(GestureEndTarget endTarget, long duration) {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return;
+ }
+ LauncherState toState = endTarget == GestureEndTarget.RECENTS ? OVERVIEW : NORMAL;
+ taskbarController.createAnimToLauncher(toState, duration).start();
+ }
+
+ @Override
+ public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return;
+ }
+ boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+ taskbarController.setIsImeVisible(isImeVisible);
+ }
+
+ @Override
+ public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return super.deferStartingActivity(deviceState, ev);
+ }
+ return taskbarController.isEventOverAnyTaskbarItem(ev);
+ }
+
+ @Override
+ public boolean shouldCancelCurrentGesture() {
+ TaskbarController taskbarController = getTaskbarController();
+ if (taskbarController == null) {
+ return super.shouldCancelCurrentGesture();
+ }
+ return taskbarController.isDraggingItem();
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
similarity index 96%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
rename to quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 052d0a6..842fb84 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -39,7 +39,7 @@
* Temporary class to allow easier refactoring
*/
public class LauncherSwipeHandlerV2 extends
- BaseSwipeUpHandlerV2<BaseQuickstepLauncher, RecentsView> {
+ AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView> {
public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
@@ -56,6 +56,7 @@
final TaskView runningTaskView = mRecentsView.getRunningTaskView();
final View workspaceView;
if (runningTaskView != null
+ && !mIsSwipingPipToHome
&& runningTaskView.getTask().key.getComponent() != null) {
workspaceView = mActivity.getWorkspace().getFirstMatchForAppClose(
runningTaskView.getTask().key.getComponent().getPackageName(),
@@ -140,5 +141,10 @@
new StaggeredWorkspaceAnim(mActivity, velocity,
true /* animateOverviewScrim */).start();
}
+
+ @Override
+ public boolean supportSwipePipToHome() {
+ return true;
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 1081548..a749f9a 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -30,16 +30,18 @@
import android.graphics.Point;
import android.graphics.RectF;
import android.util.Log;
-import android.util.SparseArray;
import android.view.MotionEvent;
import android.view.Surface;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController.Info;
import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
/**
* Maintains state for supporting nav bars and tracking their gestures in multiple orientations.
@@ -51,6 +53,37 @@
*/
class OrientationTouchTransformer {
+ class CurrentDisplay {
+ public Point size;
+ public int rotation;
+
+ CurrentDisplay() {
+ this.size = new Point(0, 0);
+ this.rotation = 0;
+ }
+
+ CurrentDisplay(Point size, int rotation) {
+ this.size = size;
+ this.rotation = rotation;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CurrentDisplay display = (CurrentDisplay) o;
+ if (rotation != display.rotation) return false;
+
+ return Objects.equals(size, display.size);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(size, rotation);
+ }
+ };
+
private static final String TAG = "OrientationTouchTransformer";
private static final boolean DEBUG = false;
private static final int MAX_ORIENTATIONS = 4;
@@ -60,10 +93,14 @@
private final Matrix mTmpMatrix = new Matrix();
private final float[] mTmpPoint = new float[2];
- private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
+ private Map<CurrentDisplay, OrientationRectF> mSwipeTouchRegions =
+ new HashMap<CurrentDisplay, OrientationRectF>();
private final RectF mAssistantLeftRegion = new RectF();
private final RectF mAssistantRightRegion = new RectF();
- private int mCurrentDisplayRotation;
+ private final RectF mOneHandedModeRegion = new RectF();
+ private CurrentDisplay mCurrentDisplay = new CurrentDisplay();
+ private int mNavBarGesturalHeight;
+ private int mNavBarLargerGesturalHeight;
private boolean mEnableMultipleRegions;
private Resources mResources;
private OrientationRectF mLastRectTouched;
@@ -83,7 +120,7 @@
* QUICKSTEP_ROTATION_UNINITIALIZED, then user has not tapped on an active nav region.
* Otherwise it will be the rotation of the display when the user first interacted with the
* active nav bar region.
- * The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is
+ * The "session" ends when {@link #enableMultipleRegions(boolean, Info)} is
* called - usually from a timeout or if user starts interacting w/ the foreground app.
*
* This is different than {@link #mLastRectTouched} as it can get reset by the system whereas
@@ -103,18 +140,36 @@
mResources = resources;
mMode = mode;
mContractInfo = contractInfo;
+ mNavBarGesturalHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
+ mNavBarLargerGesturalHeight = ResourceUtils.getDimenByName(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, resources,
+ mNavBarGesturalHeight);
}
- void setNavigationMode(SysUINavigationMode.Mode newMode, DefaultDisplay.Info info) {
+ private void refreshTouchRegion(Info info, Resources newRes) {
+ // Swipe touch regions are independent of nav mode, so we have to clear them explicitly
+ // here to avoid, for ex, a nav region for 2-button rotation 0 being used for 3-button mode
+ // It tries to cache and reuse swipe regions whenever possible based only on rotation
+ mResources = newRes;
+ mSwipeTouchRegions.clear();
+ resetSwipeRegions(info);
+ }
+
+ void setNavigationMode(SysUINavigationMode.Mode newMode, Info info,
+ Resources newRes) {
if (mMode == newMode) {
return;
}
this.mMode = newMode;
- // Swipe touch regions are independent of nav mode, so we have to clear them explicitly
- // here to avoid, for ex, a nav region for 2-button rotation 0 being used for 3-button mode
- // It tries to cache and reuse swipe regions whenever possible based only on rotation
- mSwipeTouchRegions.clear();
- resetSwipeRegions(info);
+ refreshTouchRegion(info, newRes);
+ }
+
+ void setGesturalHeight(int newGesturalHeight, Info info, Resources newRes) {
+ if (mNavBarGesturalHeight == newGesturalHeight) {
+ return;
+ }
+ mNavBarGesturalHeight = newGesturalHeight;
+ refreshTouchRegion(info, newRes);
}
/**
@@ -123,24 +178,25 @@
* alongside other regions.
* Ok to call multiple times
*
- * @see #enableMultipleRegions(boolean, DefaultDisplay.Info)
+ * @see #enableMultipleRegions(boolean, Info)
*/
- void createOrAddTouchRegion(DefaultDisplay.Info info) {
- mCurrentDisplayRotation = info.rotation;
+ void createOrAddTouchRegion(Info info) {
+ mCurrentDisplay = new CurrentDisplay(info.realSize, info.rotation);
+
if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
- && mCurrentDisplayRotation == mQuickStepStartingRotation) {
+ && mCurrentDisplay.rotation == mQuickStepStartingRotation) {
// User already was swiping and the current screen is same rotation as the starting one
// Remove active nav bars in other rotations except for the one we started out in
resetSwipeRegions(info);
return;
}
- OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+ OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplay);
if (region != null) {
return;
}
if (mEnableMultipleRegions) {
- mSwipeTouchRegions.put(mCurrentDisplayRotation, createRegionForDisplay(info));
+ mSwipeTouchRegions.put(mCurrentDisplay, createRegionForDisplay(info));
} else {
resetSwipeRegions(info);
}
@@ -153,7 +209,7 @@
* @param enableMultipleRegions Set to true to start tracking multiple nav bar regions
* @param info The current displayInfo which will be the start of the quickswitch gesture
*/
- void enableMultipleRegions(boolean enableMultipleRegions, DefaultDisplay.Info info) {
+ void enableMultipleRegions(boolean enableMultipleRegions, Info info) {
mEnableMultipleRegions = enableMultipleRegions &&
mMode != SysUINavigationMode.Mode.TWO_BUTTONS;
if (mEnableMultipleRegions) {
@@ -174,7 +230,7 @@
*
* @param displayInfo The display whos rotation will be used as the current active rotation
*/
- void setSingleActiveRegion(DefaultDisplay.Info displayInfo) {
+ void setSingleActiveRegion(Info displayInfo) {
mActiveTouchRotation = displayInfo.rotation;
resetSwipeRegions(displayInfo);
}
@@ -185,41 +241,41 @@
* To be called whenever we want to stop tracking more than one swipe region.
* Ok to call multiple times.
*/
- private void resetSwipeRegions(DefaultDisplay.Info region) {
+ private void resetSwipeRegions(Info region) {
if (DEBUG) {
- Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplayRotation);
+ Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation);
}
- mCurrentDisplayRotation = region.rotation;
- OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+ mCurrentDisplay = new CurrentDisplay(region.realSize, region.rotation);
+ OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
if (regionToKeep == null) {
regionToKeep = createRegionForDisplay(region);
}
mSwipeTouchRegions.clear();
- mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep);
+ mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
updateAssistantRegions(regionToKeep);
}
private void resetSwipeRegions() {
- OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
+ OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
mSwipeTouchRegions.clear();
if (regionToKeep != null) {
- mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep);
+ mSwipeTouchRegions.put(mCurrentDisplay, regionToKeep);
updateAssistantRegions(regionToKeep);
}
}
- private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) {
+ private OrientationRectF createRegionForDisplay(Info display) {
if (DEBUG) {
- Log.d(TAG, "creating rotation region for: " + mCurrentDisplayRotation);
+ Log.d(TAG, "creating rotation region for: " + mCurrentDisplay.rotation);
}
Point size = display.realSize;
int rotation = display.rotation;
+ int touchHeight = mNavBarGesturalHeight;
OrientationRectF orientationRectF =
new OrientationRectF(0, 0, size.x, size.y, rotation);
if (mMode == SysUINavigationMode.Mode.NO_BUTTON) {
- int touchHeight = getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
orientationRectF.top = orientationRectF.bottom - touchHeight;
updateAssistantRegions(orientationRectF);
} else {
@@ -235,10 +291,12 @@
+ getNavbarSize(ResourceUtils.NAVBAR_LANDSCAPE_LEFT_RIGHT_SIZE);
break;
default:
- orientationRectF.top = orientationRectF.bottom
- - getNavbarSize(ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE);
+ orientationRectF.top = orientationRectF.bottom - touchHeight;
}
}
+ // One handed gestural only active on portrait mode
+ mOneHandedModeRegion.set(0, orientationRectF.bottom - mNavBarLargerGesturalHeight,
+ size.x, size.y);
return orientationRectF;
}
@@ -264,6 +322,10 @@
}
+ boolean touchInOneHandedModeRegion(MotionEvent ev) {
+ return mOneHandedModeRegion.contains(ev.getX(), ev.getY());
+ }
+
private int getNavbarSize(String resName) {
return ResourceUtils.getNavbarSize(resName, mResources);
}
@@ -313,7 +375,9 @@
}
for (int i = 0; i < MAX_ORIENTATIONS; i++) {
- OrientationRectF rect = mSwipeTouchRegions.get(i);
+ CurrentDisplay display = new CurrentDisplay(mCurrentDisplay.size, i);
+ OrientationRectF rect = mSwipeTouchRegions.get(display);
+
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SWIPE_TO_HOME, "transform:DOWN, rect=" + rect);
}
@@ -327,7 +391,7 @@
mLastRectTouched = rect;
mActiveTouchRotation = rect.mRotation;
if (mEnableMultipleRegions
- && mCurrentDisplayRotation == mActiveTouchRotation) {
+ && mCurrentDisplay.rotation == mActiveTouchRotation) {
// TODO(b/154580671) might make this block unnecessary
// Start a touch session for the default nav region for the display
mQuickStepStartingRotation = mLastRectTouched.mRotation;
@@ -350,11 +414,14 @@
pw.println(" lastTouchedRegion=" + mLastRectTouched);
pw.println(" multipleRegionsEnabled=" + mEnableMultipleRegions);
StringBuilder regions = new StringBuilder(" currentTouchableRotations=");
- for(int i = 0; i < mSwipeTouchRegions.size(); i++) {
- OrientationRectF rectF = mSwipeTouchRegions.get(mSwipeTouchRegions.keyAt(i));
- regions.append(rectF.mRotation).append(" ");
+ for (CurrentDisplay key: mSwipeTouchRegions.keySet()) {
+ OrientationRectF rectF = mSwipeTouchRegions.get(key);
+ regions.append(rectF).append(" ");
}
pw.println(regions.toString());
+ pw.println(" mNavBarGesturalHeight=" + mNavBarGesturalHeight);
+ pw.println(" mNavBarLargerGesturalHeight=" + mNavBarLargerGesturalHeight);
+ pw.println(" mOneHandedModeRegion=" + mOneHandedModeRegion);
}
private class OrientationRectF extends RectF {
@@ -386,12 +453,12 @@
boolean applyTransform(MotionEvent event, boolean forceTransform) {
mTmpMatrix.reset();
- postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
+ postDisplayRotation(deltaRotation(mCurrentDisplay.rotation, mRotation),
mHeight, mWidth, mTmpMatrix);
if (forceTransform) {
if (DEBUG) {
Log.d(TAG, "Transforming rotation due to forceTransform, "
- + "mCurrentRotation: " + mCurrentDisplayRotation
+ + "mCurrentRotation: " + mCurrentDisplay.rotation
+ "mRotation: " + mRotation);
}
event.transform(mTmpMatrix);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java b/quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OverscrollPluginFactory.java
rename to quickstep/src/com/android/quickstep/OverscrollPluginFactory.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
similarity index 84%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
rename to quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 4879db7..985389e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -25,19 +25,18 @@
import android.content.Context;
import android.os.Build;
import android.os.SystemClock;
+import android.os.Trace;
import android.view.ViewConfiguration;
import androidx.annotation.BinderThread;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -49,7 +48,6 @@
private final Context mContext;
private final RecentsAnimationDeviceState mDeviceState;
- private final RecentsModel mRecentsModel;
private final OverviewComponentObserver mOverviewComponentObserver;
private long mLastToggleTime;
@@ -58,7 +56,6 @@
OverviewComponentObserver observer) {
mContext = context;
mDeviceState = deviceState;
- mRecentsModel = RecentsModel.INSTANCE.get(mContext);
mOverviewComponentObserver = observer;
}
@@ -69,16 +66,14 @@
return;
}
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
}
@BinderThread
public void onOverviewShown(boolean triggeredFromAltTab) {
if (triggeredFromAltTab) {
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
}
MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
}
@@ -88,12 +83,6 @@
MAIN_EXECUTOR.execute(new HideRecentsCommand());
}
- @BinderThread
- public void onTip(int actionType, int viewType) {
- MAIN_EXECUTOR.execute(() ->
- UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
- }
-
private class ShowRecentsCommand extends RecentsActivityCommand {
private final boolean mTriggeredFromAltTab;
@@ -153,12 +142,12 @@
private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
+ private static final String TRANSITION_NAME = "Transition:toOverview";
protected final BaseActivityInterface<?, T> mActivityInterface;
private final long mCreateTime;
private final AppToOverviewAnimationProvider<T> mAnimationProvider;
private final long mToggleClickedTime = SystemClock.uptimeMillis();
- private boolean mUserEventLogged;
private ActivityInitListener mListener;
public RecentsActivityCommand() {
@@ -168,7 +157,7 @@
ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
// Preload the plan
- mRecentsModel.getTasks(null);
+ RecentsModel.INSTANCE.get(mContext).getTasks(null);
}
@Override
@@ -186,6 +175,13 @@
return;
}
+ final T activity = mActivityInterface.getCreatedActivity();
+ if (activity != null) {
+ InteractionJankMonitorWrapper.begin(
+ activity.getRootView(),
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ }
+
// Otherwise, start overview.
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
@@ -220,26 +216,13 @@
private boolean onActivityReady(Boolean wasVisible) {
final T activity = mActivityInterface.getCreatedActivity();
- if (!mUserEventLogged) {
- activity.getUserEventDispatcher().logActionCommand(
- LauncherLogProto.Action.Command.RECENTS_BUTTON,
- mActivityInterface.getContainerType(),
- LauncherLogProto.ContainerType.TASKSWITCHER);
- mUserEventLogged = true;
- }
-
- // Switch prediction client to overview
- PredictionUiStateManager.INSTANCE.get(activity).switchClient(
- PredictionUiStateManager.Client.OVERVIEW);
return mAnimationProvider.onActivityReady(activity, wasVisible);
}
private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets) {
- if (LatencyTrackerCompat.isEnabled(mContext)) {
- LatencyTrackerCompat.logToggleRecents(
- (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
- }
+ LatencyTrackerCompat.logToggleRecents(
+ mContext, (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
mListener.unregister();
@@ -247,8 +230,15 @@
wallpaperTargets);
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ Trace.beginAsyncSection(TRANSITION_NAME, 0);
+ super.onAnimationStart(animation);
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
onTransitionComplete();
+ Trace.endAsyncSection(TRANSITION_NAME, 0);
}
});
return animatorSet;
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 07f838b..fb8f9fe 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,10 +20,10 @@
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 static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +35,8 @@
import android.content.pm.ResolveInfo;
import android.util.SparseIntArray;
+import com.android.launcher3.tracing.OverviewComponentObserverProto;
+import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -73,9 +75,7 @@
public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
mContext = context;
mDeviceState = deviceState;
- mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mCurrentHomeIntent = createHomeIntent();
mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
ComponentName myHomeComponent =
@@ -117,10 +117,6 @@
updateOverviewTargets();
}
- public boolean assistantGestureIsConstrained() {
- return (mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
- }
-
/**
* Update overview intent and {@link BaseActivityInterface} based off the current launcher home
* component.
@@ -262,4 +258,17 @@
pw.println(" overviewIntent=" + mOverviewIntent);
pw.println(" homeIntent=" + mCurrentHomeIntent);
}
+
+ /**
+ * Used for winscope tracing, see launcher_trace.proto
+ * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
+ * @param serviceProto The parent of this proto message.
+ */
+ public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
+ OverviewComponentObserverProto.Builder overviewComponentObserver =
+ OverviewComponentObserverProto.newBuilder();
+ overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted());
+ overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
+ serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
index 32268a4..192738f 100644
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -15,21 +15,34 @@
*/
package com.android.quickstep;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.UserManager;
import android.util.Log;
import com.android.launcher3.BuildConfig;
import com.android.launcher3.MainProcessInitializer;
+import com.android.launcher3.util.Executors;
+import com.android.quickstep.logging.SettingsChangeLogger;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.ThreadedRendererCompat;
@SuppressWarnings("unused")
+@TargetApi(Build.VERSION_CODES.R)
public class QuickstepProcessInitializer extends MainProcessInitializer {
private static final String TAG = "QuickstepProcessInitializer";
+ private static final int SETUP_DELAY_MILLIS = 5000;
- public QuickstepProcessInitializer(Context context) { }
+ public QuickstepProcessInitializer(Context context) {
+ // Fake call to create an instance of InteractionJankMonitor to avoid binder calls during
+ // its initialization during transitions.
+ InteractionJankMonitorWrapper.cancel(-1);
+ }
@Override
protected void init(Context context) {
@@ -48,8 +61,14 @@
super.init(context);
+ LIVE_TILE.initialize(context);
+
// Elevate GPU priority for Quickstep and Remote animations.
ThreadedRendererCompat.setContextPriority(
ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+
+ // Initialize settings logger after a default timeout
+ Executors.MAIN_EXECUTOR.getHandler()
+ .postDelayed(() -> new SettingsChangeLogger(context), SETUP_DELAY_MILLIS);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
rename to quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
index 5026f36..39af0db 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepTestInformationHandler.java
+++ b/quickstep/src/com/android/quickstep/QuickstepTestInformationHandler.java
@@ -53,17 +53,17 @@
Bundle::putInt, PortraitStatesTouchController::getHotseatTop);
}
- case TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED: {
- response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
- FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get());
- return response;
- }
-
case TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED: {
response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
FeatureFlags.ENABLE_OVERVIEW_SHARE.get());
return response;
}
+
+ case TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED: {
+ response.putBoolean(TestProtocol.TEST_INFO_RESPONSE_FIELD,
+ FeatureFlags.ENABLE_OVERVIEW_CONTENT_PUSH.get());
+ return response;
+ }
}
return super.call(method);
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index 70b4f20..2f1538b 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -22,10 +22,12 @@
import android.app.ActivityManager;
import android.os.Build;
import android.os.Process;
+import android.util.Log;
import android.util.SparseBooleanArray;
import androidx.annotation.VisibleForTesting;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.LooperExecutor;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -51,6 +53,9 @@
// The list change id, increments as the task list changes in the system
private int mChangeId;
+ // Whether we are currently updating the tasks in the background (up to when the result is
+ // posted back on the main thread)
+ private boolean mLoadingTasksInBackground;
private TaskLoadResult mResultsBg = INVALID_RESULT;
private TaskLoadResult mResultsUi = INVALID_RESULT;
@@ -64,6 +69,11 @@
mActivityManagerWrapper.registerTaskStackListener(this);
}
+ @VisibleForTesting
+ public boolean isLoadingTasksInBackground() {
+ return mLoadingTasksInBackground;
+ }
+
/**
* Fetches the task keys skipping any local cache.
*/
@@ -83,6 +93,10 @@
* @return The change id of the current task list
*/
public synchronized int getTasks(boolean loadKeysOnly, Consumer<ArrayList<Task>> callback) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getTasks: keysOnly=" + loadKeysOnly
+ + " callback=" + callback);
+ }
final int requestLoadId = mChangeId;
if (mResultsUi.isValidForRequest(requestLoadId, loadKeysOnly)) {
// The list is up to date, send the callback on the next frame,
@@ -90,22 +104,38 @@
if (callback != null) {
// Copy synchronously as the changeId might change by next frame
ArrayList<Task> result = copyOf(mResultsUi);
- mMainThreadExecutor.post(() -> callback.accept(result));
+ mMainThreadExecutor.post(() -> {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getTasks: no new tasks");
+ }
+ callback.accept(result);
+ });
}
return requestLoadId;
}
// Kick off task loading in the background
+ mLoadingTasksInBackground = true;
UI_HELPER_EXECUTOR.execute(() -> {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getTasks: loading in bg start");
+ }
if (!mResultsBg.isValidForRequest(requestLoadId, loadKeysOnly)) {
mResultsBg = loadTasksInBackground(Integer.MAX_VALUE, requestLoadId, loadKeysOnly);
}
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getTasks: loading in bg end");
+ }
TaskLoadResult loadResult = mResultsBg;
mMainThreadExecutor.execute(() -> {
+ mLoadingTasksInBackground = false;
mResultsUi = loadResult;
if (callback != null) {
ArrayList<Task> result = copyOf(mResultsUi);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getTasks: callback w/ bg results");
+ }
callback.accept(result);
}
});
@@ -191,6 +221,7 @@
} else {
task = new Task(taskKey);
}
+ task.setLastSnapshotData(rawTask);
allTasks.add(task);
}
@@ -200,9 +231,7 @@
private ArrayList<Task> copyOf(ArrayList<Task> tasks) {
ArrayList<Task> newTasks = new ArrayList<>();
for (int i = 0; i < tasks.size(); i++) {
- Task t = tasks.get(i);
- newTasks.add(new Task(t.key, t.colorPrimary, t.colorBackground, t.isDockable,
- t.isLocked, t.taskDescription, t.topActivity));
+ newTasks.add(new Task(tasks.get(i)));
}
return newTasks;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
similarity index 88%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
rename to quickstep/src/com/android/quickstep/RecentsActivity.java
index 6f2f9fb..1b2fd41 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -41,7 +42,11 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.LauncherAnimationRunner.AnimationResult;
import com.android.launcher3.R;
+import com.android.launcher3.WrappedAnimationRunnerImpl;
+import com.android.launcher3.WrappedLauncherAnimationRunner;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -62,11 +67,11 @@
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.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.List;
/**
* A recents activity that shows the recently launched tasks as swipable task cards.
@@ -87,6 +92,10 @@
private StateManager<RecentsState> mStateManager;
+ // Strong refs to runners which are cleared when the activity is destroyed
+ private WrappedAnimationRunnerImpl mActivityLaunchAnimationRunner;
+ private SearchAdapterProvider mSearchAdapterProvider;
+
/**
* Init drag layer and overview panel views.
*/
@@ -118,7 +127,6 @@
* etc.)
*/
protected void onHandleConfigChanged() {
- mUserEventDispatcher = null;
initDeviceProfile();
AbstractFloatingView.closeOpenViews(this, true,
@@ -170,20 +178,28 @@
}
final TaskView taskView = (TaskView) v;
- RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler,
- true /* startAtFrontOfQueue */) {
+ mActivityLaunchAnimationRunner = new WrappedAnimationRunnerImpl() {
+ @Override
+ public Handler getHandler() {
+ return mUiHandler;
+ }
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonAppTargets,
+ AnimationResult result) {
AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
wallpaperTargets);
anim.addListener(resetStateListener());
result.setAnimation(anim, RecentsActivity.this);
}
};
+ final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner<>(
+ mActivityLaunchAnimationRunner, true /* startAtFrontOfQueue */);
return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
- runner, RECENTS_LAUNCH_DURATION,
+ wrapper, RECENTS_LAUNCH_DURATION,
RECENTS_LAUNCH_DURATION - STATUS_BAR_TRANSITION_DURATION
- STATUS_BAR_TRANSITION_PRE_DELAY));
}
@@ -288,6 +304,7 @@
protected void onDestroy() {
super.onDestroy();
ACTIVITY_TRACKER.onActivityDestroyed(this);
+ mActivityLaunchAnimationRunner = null;
}
@Override
@@ -297,14 +314,12 @@
}
public void startHome() {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ startActivity(createHomeIntent());
}
@Override
- protected StateHandler<RecentsState>[] createStateHandlers() {
- return new StateHandler[] { new FallbackRecentsStateController(this) };
+ protected void collectStateHandlers(List<StateHandler> out) {
+ out.add(new FallbackRecentsStateController(this));
}
@Override
@@ -321,7 +336,7 @@
@Override
public AtomicAnimationFactory<RecentsState> createAtomicAnimationFactory() {
- return new RecentsAtomicAnimationFactory<>(this, 0);
+ return new RecentsAtomicAnimationFactory<>(this);
}
private AnimatorListenerAdapter resetStateListener() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 51f5e5d..646c5a0 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -16,13 +16,17 @@
package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.graphics.Rect;
+
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import com.android.launcher3.util.Preconditions;
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;
@@ -89,30 +93,12 @@
}
/**
- * Notifies the controller that we want to defer cancel until the next app transition starts.
- * If {@param screenshot} is set, then we will receive a screenshot on the next
- * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
- * {@link #cleanupScreenshot()} when that screenshot is no longer used.
- */
- public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
- mController.setDeferCancelUntilNextTransition(defer, screenshot);
- }
-
- /**
- * Cleans up the screenshot previously returned from
- * {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
- */
- public void cleanupScreenshot() {
- UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
- }
-
- /**
* Remove task remote animation target from
* {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
*/
@UiThread
- public boolean removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
- return mController.removeTask(target.taskId);
+ public void removeTaskTarget(@NonNull RemoteAnimationTargetCompat target) {
+ THREAD_POOL_EXECUTOR.execute(() -> mController.removeTask(target.taskId));
}
@UiThread
@@ -149,6 +135,8 @@
mOnFinishedListener.accept(this);
UI_HELPER_EXECUTOR.execute(() -> {
mController.finish(toRecents, sendUserLeaveHint);
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME);
if (callback != null) {
MAIN_EXECUTOR.execute(callback);
}
@@ -156,6 +144,18 @@
}
/**
+ * Sets the final bounds on a Task. This is used by Launcher to notify the system that
+ * animating Activity to PiP has completed and the associated task surface should be updated
+ * accordingly. This should be called before `finish`
+ * @param taskId for which the leash should be updated
+ * @param destinationBounds bounds of the final PiP window
+ */
+ public void setFinishTaskBounds(int taskId, Rect destinationBounds) {
+ UI_HELPER_EXECUTOR.execute(
+ () -> mController.setFinishTaskBounds(taskId, destinationBounds));
+ }
+
+ /**
* Enables the input consumer to start intercepting touches in the app window.
*/
public void enableInputConsumer() {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 7a6bbb4..f99b7e6 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,18 +17,22 @@
import static android.content.Intent.ACTION_USER_UNLOCKED;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
+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.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL;
+import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
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_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
+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 static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
@@ -42,19 +46,27 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Region;
+import android.net.Uri;
import android.os.Process;
+import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.DisplayMetrics;
import android.view.MotionEvent;
+import android.view.Surface;
import androidx.annotation.BinderThread;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.util.SettingsCache;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayHolder;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -71,11 +83,14 @@
*/
public class RecentsAnimationDeviceState implements
NavigationModeChangeListener,
- DefaultDisplay.DisplayInfoChangeListener {
+ DisplayInfoChangeListener,
+ OneHandedModeChangeListener {
+
+ static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
private final Context mContext;
private final SysUINavigationMode mSysUiNavMode;
- private final DefaultDisplay mDefaultDisplay;
+ private final DisplayHolder mDisplayHolder;
private final int mDisplayId;
private final RotationTouchHelper mRotationTouchHelper;
@@ -88,6 +103,9 @@
private final Region mDeferredGestureRegion = new Region();
private boolean mAssistantAvailable;
private float mAssistantVisibility;
+ private boolean mIsOneHandedModeEnabled;
+ private boolean mIsSwipeToNotificationEnabled;
+ private final boolean mIsOneHandedModeSupported;
private boolean mIsUserUnlocked;
private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
@@ -109,12 +127,17 @@
private boolean mIsUserSetupComplete;
public RecentsAnimationDeviceState(Context context) {
+ this(context, DisplayController.getDefaultDisplay(context));
+ }
+
+ public RecentsAnimationDeviceState(Context context, DisplayHolder displayHolder) {
mContext = context;
+ mDisplayHolder = displayHolder;
mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
- mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
- mDisplayId = mDefaultDisplay.getInfo().id;
- runOnDestroy(() -> mDefaultDisplay.removeChangeListener(this));
- mRotationTouchHelper = new RotationTouchHelper(context);
+ mDisplayId = mDisplayHolder.getInfo().id;
+ mIsOneHandedModeSupported = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
+ runOnDestroy(() -> mDisplayHolder.removeChangeListener(this));
+ mRotationTouchHelper = new RotationTouchHelper(context, mDisplayHolder);
runOnDestroy(mRotationTouchHelper::destroy);
// Register for user unlocked if necessary
@@ -157,15 +180,33 @@
}
}
- SecureSettingsObserver userSetupObserver = new SecureSettingsObserver(
- context.getContentResolver(),
- e -> mIsUserSetupComplete = e,
- Settings.Secure.USER_SETUP_COMPLETE,
- 0);
- mIsUserSetupComplete = userSetupObserver.getValue();
+ SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
+ if (mIsOneHandedModeSupported) {
+ Uri oneHandedUri = Settings.Secure.getUriFor(ONE_HANDED_ENABLED);
+ SettingsCache.OnChangeListener onChangeListener =
+ enabled -> mIsOneHandedModeEnabled = enabled;
+ settingsCache.register(oneHandedUri, onChangeListener);
+ settingsCache.dispatchOnChange(oneHandedUri);
+ runOnDestroy(() -> settingsCache.unregister(oneHandedUri, onChangeListener));
+ } else {
+ mIsOneHandedModeEnabled = false;
+ }
+
+
+ Uri swipeBottomNotificationUri =
+ Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
+ SettingsCache.OnChangeListener onChangeListener =
+ enabled -> mIsSwipeToNotificationEnabled = enabled;
+ settingsCache.register(swipeBottomNotificationUri, onChangeListener);
+ settingsCache.dispatchOnChange(swipeBottomNotificationUri);
+ runOnDestroy(() -> settingsCache.unregister(swipeBottomNotificationUri, onChangeListener));
+
+ Uri setupCompleteUri = Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE);
+ mIsUserSetupComplete = settingsCache.getValue(setupCompleteUri, 0);
if (!mIsUserSetupComplete) {
- userSetupObserver.register();
- runOnDestroy(userSetupObserver::unregister);
+ SettingsCache.OnChangeListener userSetupChangeListener = e -> mIsUserSetupComplete = e;
+ settingsCache.register(setupCompleteUri, userSetupChangeListener);
+ runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
}
}
@@ -191,11 +232,20 @@
runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(listener));
}
+ /**
+ * Adds a listener for the one handed mode change,
+ * guaranteed to be called after the device state's mode has changed.
+ */
+ public void addOneHandedModeChangedCallback(OneHandedModeChangeListener listener) {
+ listener.onOneHandedModeChanged(mSysUiNavMode.addOneHandedOverlayChangeListener(listener));
+ runOnDestroy(() -> mSysUiNavMode.removeOneHandedOverlayChangeListener(listener));
+ }
+
@Override
public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
- mDefaultDisplay.removeChangeListener(this);
- mDefaultDisplay.addChangeListener(this);
- onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
+ mDisplayHolder.removeChangeListener(this);
+ mDisplayHolder.addChangeListener(this);
+ onDisplayInfoChanged(mDisplayHolder.getInfo(), CHANGE_ALL);
if (newMode == NO_BUTTON) {
mExclusionListener.register();
@@ -203,13 +253,12 @@
mExclusionListener.unregister();
}
- mNavBarPosition = new NavBarPosition(newMode, mDefaultDisplay.getInfo());
-
+ mNavBarPosition = new NavBarPosition(newMode, mDisplayHolder.getInfo());
mMode = newMode;
}
@Override
- public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ public void onDisplayInfoChanged(Info info, int flags) {
if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) {
// ignore displays that aren't running launcher and frame refresh rate changes
return;
@@ -221,6 +270,11 @@
mNavBarPosition = new NavBarPosition(mMode, info);
}
+ @Override
+ public void onOneHandedModeChanged(int newGesturalHeight) {
+ mRotationTouchHelper.setGesturalHeight(newGesturalHeight);
+ }
+
/**
* @return the current navigation mode for the device.
*/
@@ -246,7 +300,7 @@
* @return whether the current nav mode has some gestures (either 2 or 0 button mode).
*/
public boolean isGesturalNavMode() {
- return mMode == TWO_BUTTONS || mMode == NO_BUTTON;
+ return mMode.hasGestures;
}
/**
@@ -324,7 +378,7 @@
* @return the system ui state flags.
*/
// TODO(141886704): See if we can remove this
- public @SystemUiStateFlags int getSystemUiStateFlags() {
+ public int getSystemUiStateFlags() {
return mSystemUiStateFlags;
}
@@ -333,6 +387,7 @@
*/
public boolean canStartSystemGesture() {
boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+ || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
|| mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
@@ -357,6 +412,13 @@
}
/**
+ * @return whether assistant gesture is constraint
+ */
+ public boolean isAssistantGestureIsConstrained() {
+ return (mSystemUiStateFlags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
+ }
+
+ /**
* @return whether the bubble stack is expanded
*/
public boolean isBubblesExpanded() {
@@ -406,6 +468,13 @@
}
/**
+ * @return whether one-handed mode is enabled and active
+ */
+ public boolean isOneHandedModeActive() {
+ return (mSystemUiStateFlags & SYSUI_STATE_ONE_HANDED_ACTIVE) != 0;
+ }
+
+ /**
* Sets the region in screen space where the gestures should be deferred (ie. due to specific
* nav bar ui).
*/
@@ -467,6 +536,35 @@
&& !isGestureBlockedActivity(task);
}
+ /**
+ * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
+ *
+ * @param ev The touch screen motion event.
+ * @return whether the given motion event can trigger the one handed mode.
+ */
+ public boolean canTriggerOneHandedAction(MotionEvent ev) {
+ if (!mIsOneHandedModeSupported) {
+ return false;
+ }
+
+ if (mIsOneHandedModeEnabled || mIsSwipeToNotificationEnabled) {
+ final Info displayInfo = mDisplayHolder.getInfo();
+ return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
+ && displayInfo.rotation != Surface.ROTATION_90
+ && displayInfo.rotation != Surface.ROTATION_270
+ && displayInfo.metrics.densityDpi < DisplayMetrics.DENSITY_600);
+ }
+ return false;
+ }
+
+ public boolean isOneHandedModeEnabled() {
+ return mIsOneHandedModeEnabled;
+ }
+
+ public boolean isSwipeToNotificationEnabled() {
+ return mIsSwipeToNotificationEnabled;
+ }
+
public RotationTouchHelper getRotationTouchHelper() {
return mRotationTouchHelper;
}
@@ -481,6 +579,9 @@
pw.println(" assistantDisabled="
+ QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
pw.println(" isUserUnlocked=" + mIsUserUnlocked);
+ pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
+ pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
+ pw.println(" deferredGestureRegion=" + mDeferredGestureRegion);
mRotationTouchHelper.dump(pw);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index 718c5ba..da0a664 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -41,4 +41,13 @@
public boolean hasTargets() {
return unfilteredApps.length != 0;
}
+
+ public boolean hasTask(int taskId) {
+ for (RemoteAnimationTargetCompat target : unfilteredApps) {
+ if (target.taskId == taskId) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 6f54ba2..ba24e6a 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -18,7 +18,6 @@
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.createAndStartNewLooper;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import android.annotation.TargetApi;
@@ -26,11 +25,13 @@
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.os.Build;
-import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
+import androidx.annotation.VisibleForTesting;
+
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -40,6 +41,8 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
import java.util.function.Consumer;
/**
@@ -52,6 +55,9 @@
public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
new MainThreadInitializedObject<>(RecentsModel::new);
+ private static final Executor RECENTS_MODEL_EXECUTOR = Executors.newSingleThreadExecutor(
+ new SimpleThreadFactory("TaskThumbnailIconCache-", THREAD_PRIORITY_BACKGROUND));
+
private final List<TaskVisualsChangeListener> mThumbnailChangeListeners = new ArrayList<>();
private final Context mContext;
@@ -61,12 +67,10 @@
private RecentsModel(Context context) {
mContext = context;
- Looper looper =
- createAndStartNewLooper("TaskThumbnailIconCache", THREAD_PRIORITY_BACKGROUND);
mTaskList = new RecentTasksList(MAIN_EXECUTOR,
new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
- mIconCache = new TaskIconCache(context, looper);
- mThumbnailCache = new TaskThumbnailCache(context, looper);
+ mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR);
+ mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
IconProvider.registerIconChangeListener(context,
@@ -100,6 +104,14 @@
}
/**
+ * @return Whether the task list is currently updating in the background
+ */
+ @VisibleForTesting
+ public boolean isLoadingTasksInBackground() {
+ return mTaskList.isLoadingTasksInBackground();
+ }
+
+ /**
* Finds and returns the task key associated with the given task id.
*
* @param callback The callback to receive the task key if it is found or null. This is always
@@ -160,9 +172,9 @@
@Override
public void onTaskRemoved(int taskId) {
- Task.TaskKey dummyKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
- mThumbnailCache.remove(dummyKey);
- mIconCache.onTaskRemoved(dummyKey);
+ Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
+ mThumbnailCache.remove(stubKey);
+ mIconCache.onTaskRemoved(stubKey);
}
public void onTrimMemory(int level) {
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 5f3c022..2cf3212 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -17,8 +17,8 @@
import static android.view.Surface.ROTATION_0;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_ALL;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL;
+import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
@@ -28,8 +28,9 @@
import android.view.OrientationEventListener;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.DisplayController.DisplayHolder;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
@@ -40,10 +41,10 @@
public class RotationTouchHelper implements
SysUINavigationMode.NavigationModeChangeListener,
- DefaultDisplay.DisplayInfoChangeListener {
+ DisplayInfoChangeListener {
private final OrientationTouchTransformer mOrientationTouchTransformer;
- private final DefaultDisplay mDefaultDisplay;
+ private final DisplayHolder mDisplayHolder;
private final SysUINavigationMode mSysUiNavMode;
private final int mDisplayId;
private int mDisplayRotation;
@@ -120,12 +121,12 @@
private final Context mContext;
- public RotationTouchHelper(Context context) {
+ public RotationTouchHelper(Context context, DisplayHolder displayHolder) {
mContext = context;
+ mDisplayHolder = displayHolder;
Resources resources = mContext.getResources();
mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
- mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
- mDisplayId = mDefaultDisplay.getInfo().id;
+ mDisplayId = mDisplayHolder.getInfo().id;
mOrientationTouchTransformer = new OrientationTouchTransformer(resources, mMode,
() -> QuickStepContract.getWindowCornerRadius(resources));
@@ -188,6 +189,10 @@
return mOrientationTouchTransformer.touchInAssistantRegion(ev);
}
+ public boolean touchInOneHandedModeRegion(MotionEvent ev) {
+ return mOrientationTouchTransformer.touchInOneHandedModeRegion(ev);
+ }
+
/**
* Updates the regions for detecting the swipe up/quickswitch and assistant gestures.
*/
@@ -196,7 +201,7 @@
return;
}
- mOrientationTouchTransformer.createOrAddTouchRegion(mDefaultDisplay.getInfo());
+ mOrientationTouchTransformer.createOrAddTouchRegion(mDisplayHolder.getInfo());
}
/**
@@ -218,11 +223,12 @@
@Override
public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
- mDefaultDisplay.removeChangeListener(this);
- mDefaultDisplay.addChangeListener(this);
- onDisplayInfoChanged(mDefaultDisplay.getInfo(), CHANGE_ALL);
+ mDisplayHolder.removeChangeListener(this);
+ mDisplayHolder.addChangeListener(this);
+ onDisplayInfoChanged(mDisplayHolder.getInfo(), CHANGE_ALL);
- mOrientationTouchTransformer.setNavigationMode(newMode, mDefaultDisplay.getInfo());
+ mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayHolder.getInfo(),
+ mContext.getResources());
if (!mMode.hasGestures && newMode.hasGestures) {
setupOrientationSwipeHandler();
} else if (mMode.hasGestures && !newMode.hasGestures){
@@ -237,7 +243,7 @@
}
@Override
- public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ public void onDisplayInfoChanged(Info info, int flags) {
if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) {
// ignore displays that aren't running launcher and frame refresh rate changes
return;
@@ -267,6 +273,14 @@
}
/**
+ * Sets the gestural height.
+ */
+ void setGesturalHeight(int newGesturalHeight) {
+ mOrientationTouchTransformer.setGesturalHeight(newGesturalHeight, mDisplayHolder.getInfo(),
+ mContext.getResources());
+ }
+
+ /**
* *May* apply a transform on the motion event if it lies in the nav bar region for another
* orientation that is currently being tracked as a part of quickstep
*/
@@ -279,7 +293,7 @@
}
private void enableMultipleRegions(boolean enable) {
- mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
+ mOrientationTouchTransformer.enableMultipleRegions(enable, mDisplayHolder.getInfo());
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getQuickStepStartingRotation());
if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
// Clear any previous state from sensor manager
@@ -342,7 +356,7 @@
* notifies system UI of the primary rotation the user is interacting with
*/
private void toggleSecondaryNavBarsForRotation() {
- mOrientationTouchTransformer.setSingleActiveRegion(mDefaultDisplay.getInfo());
+ mOrientationTouchTransformer.setSingleActiveRegion(mDisplayHolder.getInfo());
notifySysuiOfCurrentRotation(mOrientationTouchTransformer.getCurrentActiveRotation());
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
similarity index 87%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
rename to quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 2963b6c..f4b8b62 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -29,6 +29,7 @@
import androidx.annotation.UiThread;
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;
@@ -66,6 +67,8 @@
// How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1;
+ protected final float mMaxShadowRadius;
+
protected AnimatorControllerWithResistance mWindowTransitionController;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
@@ -76,9 +79,12 @@
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
mTransformParams = transformParams;
- mTaskViewSimulator.setLayoutRotation(
+ mTaskViewSimulator.getOrientationState().update(
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
mDeviceState.getRotationTouchHelper().getDisplayRotation());
+
+ mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
+ mTransformParams.setShadowRadius(mMaxShadowRadius);
}
protected void initTransitionEndpoints(DeviceProfile dp) {
@@ -88,13 +94,7 @@
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
dp, mContext, TEMP_RECT,
mTaskViewSimulator.getOrientationState().getOrientationHandler());
-
- if (mDeviceState.isFullyGesturalNavMode()) {
- // We can drag all the way to the top of the screen.
- mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
- } else {
- mDragLengthFactor = 1 + AnimatorControllerWithResistance.TWO_BUTTON_EXTRA_DRAG_FACTOR;
- }
+ mDragLengthFactor = (float) dp.heightPx / mTransitionDragLength;
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
mTaskViewSimulator.addAppToOverviewAnim(pa, LINEAR);
@@ -158,6 +158,32 @@
public void update(RectF currentRect, float progress, float radius) { }
public void onCancel() { }
+
+ /**
+ * @return {@code true} if this factory supports animating an Activity to PiP window on
+ * swiping up to home.
+ */
+ public boolean supportSwipePipToHome() {
+ return false;
+ }
+ }
+
+ /**
+ * Update with start progress for window animation to home.
+ * @param outMatrix {@link Matrix} to map a rect in Launcher space to window space.
+ * @param startProgress The progress of {@link #mCurrentShift} to start thw window from.
+ * @return {@link RectF} represents the bounds as starting point in window space.
+ */
+ protected RectF updateProgressForStartRect(Matrix outMatrix, float startProgress) {
+ mCurrentShift.updateValue(startProgress);
+ mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+ RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
+
+ mTaskViewSimulator.applyWindowToHomeRotation(outMatrix);
+
+ final RectF startRect = new RectF(cropRectF);
+ mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
+ return startRect;
}
/**
@@ -169,16 +195,11 @@
HomeAnimationFactory homeAnimationFactory) {
final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
- mCurrentShift.updateValue(startProgress);
- mTaskViewSimulator.apply(mTransformParams.setProgress(startProgress));
+ Matrix homeToWindowPositionMap = new Matrix();
+ final RectF startRect = updateProgressForStartRect(
+ homeToWindowPositionMap, startProgress);
RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
- // Matrix to map a rect in Launcher space to window space
- Matrix homeToWindowPositionMap = new Matrix();
- mTaskViewSimulator.applyWindowToHomeRotation(homeToWindowPositionMap);
-
- final RectF startRect = new RectF(cropRectF);
- mTaskViewSimulator.getCurrentMatrix().mapRect(startRect);
// Move the startRect to Launcher space as floatingIconView runs in Launcher
Matrix windowToHomePositionMap = new Matrix();
homeToWindowPositionMap.invert(windowToHomePositionMap);
@@ -250,9 +271,11 @@
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
+ float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0);
mTransformParams
.setTargetAlpha(getWindowAlpha(progress))
- .setCornerRadius(cornerRadius);
+ .setCornerRadius(cornerRadius)
+ .setShadowRadius(shadowRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
@@ -263,7 +286,8 @@
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
builder.withMatrix(mMatrix)
.withWindowCrop(mCropRect)
- .withCornerRadius(params.getCornerRadius());
+ .withCornerRadius(params.getCornerRadius())
+ .withShadowRadius(app.isTranslucent ? 0 : params.getShadowRadius());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index f2675f2..efec037 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
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;
@@ -24,11 +25,10 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.util.Log;
+import com.android.launcher3.ResourceUtils;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.MainThreadInitializedObject;
import java.io.PrintWriter;
@@ -64,15 +64,20 @@
new MainThreadInitializedObject<>(SysUINavigationMode::new);
private static final String TAG = "SysUINavigationMode";
-
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;
private Mode mMode;
+ private int mNavBarGesturalHeight;
+ private int mNavBarLargerGesturalHeight;
+
private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+ private final List<OneHandedModeChangeListener> mOneHandedOverlayChangeListeners =
+ new ArrayList<>();
public SysUINavigationMode(Context context) {
mContext = context;
@@ -82,8 +87,9 @@
@Override
public void onReceive(Context context, Intent intent) {
updateMode();
+ updateGesturalHeight();
}
- }, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
+ }, getPackageFilter(TARGET_OVERLAY_PACKAGE, ACTION_OVERLAY_CHANGED));
}
/** Updates navigation mode when needed. */
@@ -95,9 +101,49 @@
}
}
+ private void updateGesturalHeight() {
+ int newGesturalHeight = ResourceUtils.getDimenByName(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources(),
+ INVALID_RESOURCE_HANDLE);
+
+ if (newGesturalHeight == INVALID_RESOURCE_HANDLE) {
+ Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+ return;
+ }
+
+ if (mNavBarGesturalHeight != newGesturalHeight) {
+ mNavBarGesturalHeight = newGesturalHeight;
+ }
+
+ int newLargerGesturalHeight = ResourceUtils.getDimenByName(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, mContext.getResources(),
+ INVALID_RESOURCE_HANDLE);
+ if (newLargerGesturalHeight == INVALID_RESOURCE_HANDLE) {
+ Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+ return;
+ }
+ if (mNavBarLargerGesturalHeight != newLargerGesturalHeight) {
+ mNavBarLargerGesturalHeight = newLargerGesturalHeight;
+ dispatchOneHandedOverlayChange();
+ }
+ }
+
private void initializeMode() {
- int modeInt = getSystemIntegerRes(mContext, NAV_BAR_INTERACTION_MODE_RES_NAME);
- for(Mode m : Mode.values()) {
+ int modeInt = ResourceUtils.getIntegerByName(NAV_BAR_INTERACTION_MODE_RES_NAME,
+ mContext.getResources(), INVALID_RESOURCE_HANDLE);
+ mNavBarGesturalHeight = ResourceUtils.getDimenByName(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE, mContext.getResources(),
+ INVALID_RESOURCE_HANDLE);
+ mNavBarLargerGesturalHeight = ResourceUtils.getDimenByName(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_LARGER_SIZE, mContext.getResources(),
+ mNavBarGesturalHeight);
+
+ if (modeInt == INVALID_RESOURCE_HANDLE) {
+ Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+ return;
+ }
+
+ for (Mode m : Mode.values()) {
if (m.resValue == modeInt) {
mMode = m;
}
@@ -110,6 +156,12 @@
}
}
+ private void dispatchOneHandedOverlayChange() {
+ for (OneHandedModeChangeListener listener : mOneHandedOverlayChangeListeners) {
+ listener.onOneHandedModeChanged(mNavBarLargerGesturalHeight);
+ }
+ }
+
public Mode addModeChangeListener(NavigationModeChangeListener listener) {
mChangeListeners.add(listener);
return mMode;
@@ -119,40 +171,30 @@
mChangeListeners.remove(listener);
}
+ public int addOneHandedOverlayChangeListener(OneHandedModeChangeListener listener) {
+ mOneHandedOverlayChangeListeners.add(listener);
+ return mNavBarLargerGesturalHeight;
+ }
+
+ public void removeOneHandedOverlayChangeListener(OneHandedModeChangeListener listener) {
+ mOneHandedOverlayChangeListeners.remove(listener);
+ }
+
public Mode getMode() {
return mMode;
}
- private static int getSystemIntegerRes(Context context, String resName) {
- Resources res = context.getResources();
- int resId = res.getIdentifier(resName, "integer", "android");
-
- if (resId != 0) {
- return res.getInteger(resId);
- } else {
- Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
- return -1;
- }
- }
-
- /** @return Whether we can remove the shelf from overview. */
- public static boolean removeShelfFromOverview(Context context) {
- // The shelf is core to the two-button mode model, so we need to continue supporting it.
- return getMode(context) != Mode.TWO_BUTTONS;
- }
-
- public static boolean hideShelfInTwoButtonLandscape(Context context,
- PagedOrientationHandler pagedOrientationHandler) {
- return getMode(context) == Mode.TWO_BUTTONS &&
- !pagedOrientationHandler.isLayoutNaturalToLauncher();
- }
-
public void dump(PrintWriter pw) {
pw.println("SysUINavigationMode:");
pw.println(" mode=" + mMode.name());
+ pw.println(" mNavBarGesturalHeight=:" + mNavBarGesturalHeight);
}
public interface NavigationModeChangeListener {
void onNavigationModeChanged(Mode newMode);
}
-}
\ No newline at end of file
+
+ public interface OneHandedModeChangeListener {
+ void onOneHandedModeChanged(int newGesturalHeight);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 299e9e5..5668817 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,7 +17,12 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+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.graphics.Insets;
import android.graphics.Rect;
@@ -25,13 +30,17 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
+import com.android.systemui.shared.recents.IStartingWindowListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
/**
* Holds the reference to SystemUI.
@@ -114,17 +123,6 @@
}
@Override
- public void onSplitScreenInvoked() {
- if (mSystemUiProxy != null) {
- try {
- mSystemUiProxy.onSplitScreenInvoked();
- } catch (RemoteException e) {
- Log.w(TAG, "Failed call onSplitScreenInvoked", e);
- }
- }
- }
-
- @Override
public void onOverviewShown(boolean fromHome) {
onOverviewShown(fromHome, TAG);
}
@@ -352,10 +350,207 @@
if (mSystemUiProxy != null) {
try {
mSystemUiProxy.handleImageBundleAsScreenshot(screenImageBundle, locationInScreen,
- visibleInsets, task);
+ visibleInsets, task);
} catch (RemoteException e) {
Log.w(TAG, "Failed call handleImageBundleAsScreenshot");
}
}
}
+
+ @Override
+ public void startOneHandedMode() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startOneHandedMode();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startOneHandedMode", e);
+ }
+ }
+ }
+
+ @Override
+ public void stopOneHandedMode() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.stopOneHandedMode();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopOneHandedMode", e);
+ }
+ }
+ }
+
+ @Override
+ public void expandNotificationPanel() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.expandNotificationPanel();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call expandNotificationPanel", e);
+ }
+ }
+ }
+
+ @Override
+ public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
+ PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) {
+ if (mSystemUiProxy != null) {
+ try {
+ return mSystemUiProxy.startSwipePipToHome(componentName, activityInfo,
+ pictureInPictureParams, launcherRotation, shelfHeight);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startSwipePipToHome", e);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.stopSwipePipToHome(componentName, destinationBounds);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call stopSwipePipToHome");
+ }
+ }
+ }
+
+ @Override
+ public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.registerRemoteTransition(remoteTransition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ }
+
+ @Override
+ public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.unregisterRemoteTransition(remoteTransition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ }
+
+ @Override
+ public void registerSplitScreenListener(ISplitScreenListener listener) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.registerSplitScreenListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerSplitScreenListener");
+ }
+ }
+ }
+
+ @Override
+ public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.unregisterSplitScreenListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call unregisterSplitScreenListener");
+ }
+ }
+ }
+
+ @Override
+ public void setSideStageVisibility(boolean visible) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setSideStageVisibility(visible);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setSideStageVisibility");
+ }
+ }
+ }
+
+ @Override
+ public void exitSplitScreen() {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.exitSplitScreen();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call exitSplitScreen");
+ }
+ }
+ }
+
+ @Override
+ public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.exitSplitScreenOnHide(exitSplitScreenOnHide);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call exitSplitScreen");
+ }
+ }
+ }
+
+ @Override
+ public void startTask(int taskId, int stage, int position, Bundle options) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startTask(taskId, stage, position, options);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startTask");
+ }
+ }
+ }
+
+ @Override
+ public void startShortcut(String packageName, String shortcutId, int stage, int position,
+ Bundle options, UserHandle user) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startShortcut(packageName, shortcutId, stage, position, options,
+ user);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startShortcut");
+ }
+ }
+ }
+
+ @Override
+ public void startIntent(PendingIntent intent, Intent fillInIntent, int stage,
+ int position, Bundle options) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.startIntent(intent, fillInIntent, stage, position,
+ options);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call startIntent");
+ }
+ }
+ }
+
+ @Override
+ public void removeFromSideStage(int taskId) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.removeFromSideStage(taskId);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call removeFromSideStage");
+ }
+ }
+ }
+
+ /**
+ * Sets listener to get callbacks when launching a task.
+ */
+ @Override
+ public void setStartingWindowListener(IStartingWindowListener listener) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.setStartingWindowListener(listener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call setStartingWindowListener", e);
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index cad51f4..02c2763 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -17,10 +17,14 @@
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.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
+import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
+import android.os.SystemProperties;
import android.util.Log;
import androidx.annotation.UiThread;
@@ -29,9 +33,15 @@
import com.android.launcher3.config.FeatureFlags;
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 java.util.function.Consumer;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
+ public static final boolean ENABLE_SHELL_TRANSITIONS =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
@@ -39,14 +49,19 @@
// Temporary until we can hook into gesture state events
private GestureState mLastGestureState;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
+ private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
+ private Context mCtx;
+ TaskAnimationManager(Context ctx) {
+ mCtx = ctx;
+ }
/**
* Preloads the recents animation.
*/
public void preloadRecentsAnimation(Intent intent) {
// Pass null animation handler to indicate this start is for preloading
UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(intent, null, null, null, null));
+ .startRecentsActivity(intent, 0, null, null, null));
}
/**
@@ -88,22 +103,21 @@
@Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- if (thumbnailData != null) {
- // If a screenshot is provided, switch to the screenshot before cleaning up
- activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
- () -> cleanUpRecentsAnimation(thumbnailData));
- } else {
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
- }
+ cleanUpRecentsAnimation();
}
@Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
+ cleanUpRecentsAnimation();
}
@Override
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ if (mLaunchOtherTaskHandler != null
+ && mLastGestureState.getEndTarget() == RECENTS) {
+ mLaunchOtherTaskHandler.accept(appearedTaskTarget);
+ return;
+ }
if (mController != null) {
if (mLastAppearedTaskTarget == null
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
@@ -116,10 +130,19 @@
}
}
});
+ final long eventTime = gestureState.getSwipeUpStartTimeMs();
mCallbacks.addListener(gestureState);
mCallbacks.addListener(listener);
- UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(intent, null, mCallbacks, null, null));
+
+ if (ENABLE_SHELL_TRANSITIONS) {
+ RemoteTransitionCompat transition = new RemoteTransitionCompat(mCallbacks,
+ mController != null ? mController.getController() : null);
+ Bundle options = ActivityOptionsCompat.makeRemoteTransition(transition).toBundle();
+ mCtx.startActivity(intent, options);
+ } else {
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(intent, eventTime, mCallbacks, null, null));
+ }
gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
return mCallbacks;
}
@@ -138,6 +161,15 @@
}
/**
+ * The passed-in handler is used to render side task launch animation in recents in live tile
+ * mode.
+ */
+ public void setLaunchOtherTaskInLiveTileModeHandler(
+ Consumer<RemoteAnimationTargetCompat> handler) {
+ mLaunchOtherTaskHandler = handler;
+ }
+
+ /**
* Finishes the running recents animation.
*/
public void finishRunningRecentsAnimation(boolean toHome) {
@@ -146,7 +178,7 @@
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome
: mController::finishAnimationToApp);
- cleanUpRecentsAnimation(null /* canceledThumbnail */);
+ cleanUpRecentsAnimation();
}
}
@@ -173,12 +205,7 @@
/**
* Cleans up the recents animation entirely.
*/
- private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
- // Clean up the screenshot if necessary
- if (mController != null && canceledThumbnail != null) {
- mController.cleanupScreenshot();
- }
-
+ private void cleanUpRecentsAnimation() {
// Release all the target leashes
if (mTargets != null) {
mTargets.release();
@@ -194,6 +221,7 @@
mTargets = null;
mLastGestureState = null;
mLastAppearedTaskTarget = null;
+ mLaunchOtherTaskHandler = null;
}
public void dump() {
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 7ff799e..dd0ed8f 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -17,19 +17,18 @@
import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.app.ActivityManager.TaskDescription;
import android.content.Context;
import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityManager;
@@ -41,15 +40,15 @@
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskDescriptionCompat;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -57,7 +56,7 @@
*/
public class TaskIconCache {
- private final Handler mBackgroundHandler;
+ private final Executor mBgExecutor;
private final AccessibilityManager mAccessibilityManager;
private final Context mContext;
@@ -65,9 +64,9 @@
private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
private final IconProvider mIconProvider;
- public TaskIconCache(Context context, Looper backgroundLooper) {
+ public TaskIconCache(Context context, Executor bgExecutor) {
mContext = context;
- mBackgroundHandler = new Handler(backgroundLooper);
+ mBgExecutor = bgExecutor;
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
Resources res = context.getResources();
@@ -83,31 +82,27 @@
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
- public IconLoadRequest updateIconInBackground(Task task, Consumer<Task> callback) {
+ public CancellableTask updateIconInBackground(Task task, Consumer<Task> callback) {
Preconditions.assertUIThread();
if (task.icon != null) {
// Nothing to load, the icon is already loaded
callback.accept(task);
return null;
}
-
- IconLoadRequest request = new IconLoadRequest(mBackgroundHandler) {
+ CancellableTask<TaskCacheEntry> request = new CancellableTask<TaskCacheEntry>() {
@Override
- public void run() {
- TaskCacheEntry entry = getCacheEntry(task);
- if (isCanceled()) {
- // We don't call back to the provided callback in this case
- return;
- }
- MAIN_EXECUTOR.execute(() -> {
- task.icon = entry.icon;
- task.titleDescription = entry.contentDescription;
- callback.accept(task);
- onEnd();
- });
+ public TaskCacheEntry getResultOnBg() {
+ return getCacheEntry(task);
+ }
+
+ @Override
+ public void handleResult(TaskCacheEntry result) {
+ task.icon = result.icon;
+ task.titleDescription = result.contentDescription;
+ callback.accept(task);
}
};
- Utilities.postAsyncCallback(mBackgroundHandler, request);
+ mBgExecutor.execute(request);
return request;
}
@@ -120,9 +115,8 @@
}
void invalidateCacheEntries(String pkg, UserHandle handle) {
- Utilities.postAsyncCallback(mBackgroundHandler,
- () -> mIconCache.removeAll(key ->
- pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
+ mBgExecutor.execute(() -> mIconCache.removeAll(key ->
+ pkg.equals(key.getPackageName()) && handle.getIdentifier() == key.userId));
}
@WorkerThread
@@ -171,9 +165,8 @@
key.getComponent(), key.userId);
}
if (activityInfo != null) {
- entry.contentDescription = ActivityManagerWrapper.getInstance()
- .getBadgedContentDescription(activityInfo, task.key.userId,
- task.taskDescription);
+ entry.contentDescription = getBadgedContentDescription(
+ activityInfo, task.key.userId, task.taskDescription);
}
}
@@ -181,6 +174,21 @@
return entry;
}
+ private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
+ PackageManager pm = mContext.getPackageManager();
+ String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
+ if (TextUtils.isEmpty(taskLabel)) {
+ taskLabel = Utilities.trim(info.loadLabel(pm));
+ }
+
+ String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
+ String badgedApplicationLabel = userId != UserHandle.myUserId()
+ ? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
+ : applicationLabel;
+ return applicationLabel.equals(taskLabel)
+ ? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
+ }
+
@WorkerThread
private Drawable getDefaultIcon(int userId) {
synchronized (mDefaultIcons) {
@@ -208,12 +216,6 @@
}
}
- public static abstract class IconLoadRequest extends HandlerRunnable {
- IconLoadRequest(Handler handler) {
- super(handler, null);
- }
- }
-
private static class TaskCacheEntry {
public Drawable icon;
public String contentDescription = "";
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
similarity index 81%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
rename to quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 36579ec..8636130 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -18,6 +18,7 @@
import static android.view.Surface.ROTATION_0;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_NO_THUMBNAIL;
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
@@ -30,6 +31,7 @@
import android.view.View;
import android.widget.Toast;
+import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.android.launcher3.BaseActivity;
@@ -41,6 +43,7 @@
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.OverviewActionsView;
+import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -119,12 +122,11 @@
*/
public static class TaskOverlay<T extends OverviewActionsView> {
- private final Context mApplicationContext;
+ protected final Context mApplicationContext;
protected final TaskThumbnailView mThumbnailView;
private T mActionsView;
- private ImageActionsApi mImageApi;
- private boolean mIsAllowedByPolicy;
+ protected ImageActionsApi mImageApi;
protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
@@ -150,24 +152,23 @@
if (thumbnail != null) {
getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
- final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
+ getActionsView().setCallbacks(new OverlayUICallbacksImpl(isAllowedByPolicy, task));
+ }
+ }
- getActionsView().setCallbacks(new OverlayUICallbacks() {
- @Override
- public void onShare() {
- if (isAllowedByPolicy) {
- mImageApi.startShareActivity();
- } else {
- showBlockedByPolicyMessage();
- }
- }
-
- @SuppressLint("NewApi")
- @Override
- public void onScreenshot() {
- saveScreenshot(task);
- }
- });
+ /**
+ * End rendering live tile in Overview.
+ *
+ * @param callback callback to run, after switching to screenshot
+ */
+ public void endLiveTileMode(@NonNull Runnable callback) {
+ if (LIVE_TILE.get()) {
+ RecentsView recentsView = mThumbnailView.getTaskView().getRecentsView();
+ recentsView.switchToScreenshot(
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */, callback));
+ } else {
+ callback.run();
}
}
@@ -175,7 +176,7 @@
* Called to save screenshot of the task thumbnail.
*/
@SuppressLint("NewApi")
- private void saveScreenshot(Task task) {
+ protected void saveScreenshot(Task task) {
if (mThumbnailView.isRealSnapshot()) {
mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
@@ -204,6 +205,12 @@
}
/**
+ * Sets full screen progress to the task overlay.
+ */
+ public void setFullscreenProgress(float progress) {
+ }
+
+ /**
* Gets the system shortcut for the screenshot that will be added to the task menu.
*/
public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
@@ -233,7 +240,7 @@
return mThumbnailView.getScaledInsets();
}
- private void showBlockedByPolicyMessage() {
+ protected void showBlockedByPolicyMessage() {
Toast.makeText(
mThumbnailView.getContext(),
R.string.blocked_by_policy,
@@ -255,6 +262,29 @@
dismissTaskMenuView(mActivity);
}
}
+
+ protected class OverlayUICallbacksImpl implements OverlayUICallbacks {
+ protected final boolean mIsAllowedByPolicy;
+ protected final Task mTask;
+
+ public OverlayUICallbacksImpl(boolean isAllowedByPolicy, Task task) {
+ mIsAllowedByPolicy = isAllowedByPolicy;
+ mTask = task;
+ }
+
+ public void onShare() {
+ if (mIsAllowedByPolicy) {
+ endLiveTileMode(() -> mImageApi.startShareActivity(null));
+ } else {
+ showBlockedByPolicyMessage();
+ }
+ }
+
+ @SuppressLint("NewApi")
+ public void onScreenshot() {
+ endLiveTileMode(() -> saveScreenshot(mTask));
+ }
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
similarity index 94%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
rename to quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index ff051b6..65bb0f3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -18,11 +18,9 @@
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
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 static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -40,7 +38,6 @@
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.InstantAppResolver;
import com.android.quickstep.views.RecentsView;
@@ -229,9 +226,6 @@
@Override
protected boolean onActivityStarted(BaseDraggingActivity activity) {
- SystemUiProxy.INSTANCE.get(activity).onSplitScreenInvoked();
- activity.getUserEventDispatcher().logActionOnControl(TAP,
- LauncherLogProto.ControlType.SPLIT_SCREEN_TARGET);
return true;
}
};
@@ -310,16 +304,11 @@
TaskShortcutFactory WELLBEING = (activity, view) ->
WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
- TaskShortcutFactory SCREENSHOT = (activity, tv) -> {
- if (ENABLE_OVERVIEW_ACTIONS.get()) {
- return tv.getThumbnail().getTaskOverlay()
- .getScreenshotShortcut(activity, tv.getItemInfo());
- }
- return null;
- };
+ TaskShortcutFactory SCREENSHOT = (activity, tv) -> tv.getThumbnail().getTaskOverlay()
+ .getScreenshotShortcut(activity, tv.getItemInfo());
TaskShortcutFactory MODAL = (activity, tv) -> {
- if (ENABLE_OVERVIEW_ACTIONS.get() && ENABLE_OVERVIEW_SELECTIONS.get()) {
+ if (ENABLE_OVERVIEW_SELECTIONS.get()) {
return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
}
return null;
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 2b7a8ec..a8a0219 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -15,17 +15,12 @@
*/
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.Handler;
-import android.os.Looper;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -33,11 +28,12 @@
import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.ArrayList;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
public class TaskThumbnailCache {
- private final Handler mBackgroundHandler;
+ private final Executor mBgExecutor;
private final int mCacheSize;
private final TaskKeyLruCache<ThumbnailData> mCache;
@@ -94,8 +90,8 @@
}
}
- public TaskThumbnailCache(Context context, Looper backgroundLooper) {
- mBackgroundHandler = new Handler(backgroundLooper);
+ public TaskThumbnailCache(Context context, Executor bgExecutor) {
+ mBgExecutor = bgExecutor;
mHighResLoadingState = new HighResLoadingState(context);
Resources res = context.getResources();
@@ -130,7 +126,7 @@
* @param callback The callback to receive the task after its data has been populated.
* @return A cancelable handle to the request
*/
- public ThumbnailLoadRequest updateThumbnailInBackground(
+ public CancellableTask updateThumbnailInBackground(
Task task, Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
@@ -142,14 +138,13 @@
return null;
}
-
return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
task.thumbnail = t;
callback.accept(t);
});
}
- private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean lowResolution,
+ private CancellableTask updateThumbnailInBackground(TaskKey key, boolean lowResolution,
Consumer<ThumbnailData> callback) {
Preconditions.assertUIThread();
@@ -160,26 +155,20 @@
return null;
}
- ThumbnailLoadRequest request = new ThumbnailLoadRequest(mBackgroundHandler,
- lowResolution) {
+ CancellableTask<ThumbnailData> request = new CancellableTask<ThumbnailData>() {
@Override
- public void run() {
- ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
+ public ThumbnailData getResultOnBg() {
+ return ActivityManagerWrapper.getInstance().getTaskThumbnail(
key.id, lowResolution);
+ }
- MAIN_EXECUTOR.execute(() -> {
- if (isCanceled()) {
- // We don't call back to the provided callback in this case
- return;
- }
-
- mCache.put(key, thumbnail);
- callback.accept(thumbnail);
- onEnd();
- });
+ @Override
+ public void handleResult(ThumbnailData result) {
+ mCache.put(key, result);
+ callback.accept(result);
}
};
- Utilities.postAsyncCallback(mBackgroundHandler, request);
+ mBgExecutor.execute(request);
return request;
}
@@ -218,15 +207,6 @@
return mEnableTaskSnapshotPreloading && mHighResLoadingState.mVisible;
}
- public static abstract class ThumbnailLoadRequest extends HandlerRunnable {
- public final boolean mLowResolution;
-
- ThumbnailLoadRequest(Handler handler, boolean lowResolution) {
- super(handler, null);
- mLowResolution = lowResolution;
- }
- }
-
/**
* @return Whether device supports low-res thumbnails. Low-res files are an optimization
* for faster load times of snapshots. Devices can optionally disable low-res files so that
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index 04b488d..c9db153 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,6 +16,8 @@
package com.android.quickstep;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -27,6 +29,7 @@
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;
@@ -86,4 +89,12 @@
}
return false;
}
+
+ /**
+ * Requests that the system close any open system windows (including other SystemUI).
+ */
+ public static void closeSystemWindowsAsync(String reason) {
+ UI_HELPER_EXECUTOR.execute(
+ () -> ActivityManagerWrapper.getInstance().closeSystemWindows(reason));
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
new file mode 100644
index 0000000..aa1b8e1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
+import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.launcher3.anim.Interpolators.clampToProgress;
+import static com.android.launcher3.statehandlers.DepthController.DEPTH;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+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.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.RectF;
+import android.os.Build;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+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;
+
+/**
+ * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
+ */
+@TargetApi(Build.VERSION_CODES.R)
+public final class TaskViewUtils {
+
+ private TaskViewUtils() {}
+
+ /**
+ * Try to find a TaskView that corresponds with the component of the launched view.
+ *
+ * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
+ * Otherwise, we will assume we are using a normal app transition, but it's possible that the
+ * 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) {
+ if (v instanceof TaskView) {
+ TaskView taskView = (TaskView) v;
+ return recentsView.isTaskViewVisible(taskView) ? taskView : null;
+ }
+
+ // It's possible that the launched view can still be resolved to a visible task view, check
+ // the task id of the opening task and see if we can find a match.
+ if (v.getTag() instanceof ItemInfo) {
+ ItemInfo itemInfo = (ItemInfo) v.getTag();
+ ComponentName componentName = itemInfo.getTargetComponent();
+ int userId = itemInfo.user.getIdentifier();
+ if (componentName != null) {
+ for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
+ TaskView taskView = recentsView.getTaskViewAt(i);
+ if (recentsView.isTaskViewVisible(taskView)) {
+ Task.TaskKey key = taskView.getTask().key;
+ if (componentName.equals(key.getComponent()) && userId == key.userId) {
+ return taskView;
+ }
+ }
+ }
+ }
+ }
+
+ if (targets == null) {
+ return null;
+ }
+ // Resolve the opening task id
+ int openingTaskId = -1;
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == MODE_OPENING) {
+ openingTaskId = target.taskId;
+ break;
+ }
+ }
+
+ // If there is no opening task id, fall back to the normal app icon launch animation
+ if (openingTaskId == -1) {
+ return null;
+ }
+
+ // If the opening task id is not currently visible in overview, then fall back to normal app
+ // icon launch animation
+ TaskView taskView = recentsView.getTaskView(openingTaskId);
+ if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
+ return null;
+ }
+ return taskView;
+ }
+
+ public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+ PendingAnimation out) {
+ boolean isRunningTask = v.isRunningTask();
+ TransformParams params = null;
+ TaskViewSimulator tsv = null;
+ if (LIVE_TILE.get() && isRunningTask) {
+ params = v.getRecentsView().getLiveTileParams();
+ tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
+ }
+ createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
+ depthController, out, params, tsv);
+ }
+
+ /**
+ * Creates an animation that controls the window of the opening targets for the recents launch
+ * animation.
+ */
+ public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
+ PendingAnimation out, @Nullable TransformParams params,
+ @Nullable TaskViewSimulator tsv) {
+ boolean isQuickSwitch = v.isEndQuickswitchCuj();
+ v.setEndQuickswitchCuj(false);
+
+ boolean inLiveTileMode = LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
+ final RemoteAnimationTargets targets =
+ new RemoteAnimationTargets(appTargets, wallpaperTargets,
+ inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
+
+ if (params == null) {
+ SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
+ targets.addReleaseCheck(applier);
+
+ params = new TransformParams()
+ .setSyncTransactionApplier(applier)
+ .setTargetSet(targets);
+ }
+
+ final RecentsView recentsView = v.getRecentsView();
+ int taskIndex = recentsView.indexOfChild(v);
+ Context context = v.getContext();
+ DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+ boolean parallaxCenterAndAdjacentTask =
+ taskIndex != recentsView.getCurrentPage() && !(dp.isTablet
+ && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
+ float gridProgress = recentsView.getGridProgress();
+ float gridTranslationSecondary = recentsView.getGridTranslationSecondary(taskIndex);
+ int startScroll = recentsView.getScrollOffset(taskIndex);
+
+ TaskViewSimulator topMostSimulator = null;
+
+ if (tsv == null && targets.apps.length > 0) {
+ tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
+ tsv.setDp(dp);
+
+ // RecentsView never updates the display rotation until swipe-up so the value may
+ // be stale. Use the display value instead.
+ int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
+ tsv.getOrientationState().update(displayRotation, displayRotation);
+
+ tsv.setPreview(targets.apps[targets.apps.length - 1]);
+ tsv.fullScreenProgress.value = 0;
+ tsv.recentsViewScale.value = 1;
+ tsv.gridProgress.value = gridProgress;
+ tsv.gridTranslationSecondary.value = gridTranslationSecondary;
+ tsv.setScroll(startScroll);
+
+ // Fade in the task during the initial 20% of the animation
+ out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
+ clampToProgress(LINEAR, 0, 0.2f));
+ }
+
+ if (tsv != null) {
+ out.setFloat(tsv.fullScreenProgress,
+ AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(tsv.recentsViewScale,
+ AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
+ out.setFloat(tsv.gridProgress, AnimatedFloat.VALUE, 0, TOUCH_RESPONSE_INTERPOLATOR);
+ out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
+
+ TaskViewSimulator finalTsv = tsv;
+ TransformParams finalParams = params;
+ out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
+ topMostSimulator = tsv;
+ }
+
+ if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
+ out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
+
+ TaskViewSimulator simulatorToCopy = topMostSimulator;
+ simulatorToCopy.apply(params);
+
+ // Mt represents the overall transformation on the thumbnailView relative to the
+ // Launcher's rootView
+ // K(t) represents transformation on the running window by the taskViewSimulator at
+ // any time t.
+ // at t = 0, we know that the simulator matches the thumbnailView. So if we apply K(0)`
+ // on the Launcher's rootView, the thumbnailView would match the full running task
+ // window. If we apply "K(0)` K(t)" thumbnailView will match the final transformed
+ // window at any time t. This gives the overall matrix on thumbnailView to be:
+ // Mt K(0)` K(t)
+ // During animation we apply transformation on the thumbnailView (and not the rootView)
+ // to follow the TaskViewSimulator. So the final matrix applied on the thumbnailView is:
+ // Mt K(0)` K(t) Mt`
+ TaskThumbnailView ttv = v.getThumbnail();
+ RectF tvBounds = new RectF(0, 0, ttv.getWidth(), ttv.getHeight());
+ float[] tvBoundsMapped = new float[]{0, 0, ttv.getWidth(), ttv.getHeight()};
+ getDescendantCoordRelativeToAncestor(ttv, ttv.getRootView(), tvBoundsMapped, false);
+ RectF tvBoundsInRoot = new RectF(
+ tvBoundsMapped[0], tvBoundsMapped[1],
+ tvBoundsMapped[2], tvBoundsMapped[3]);
+
+ Matrix mt = new Matrix();
+ mt.setRectToRect(tvBounds, tvBoundsInRoot, ScaleToFit.FILL);
+
+ Matrix mti = new Matrix();
+ mt.invert(mti);
+
+ Matrix k0i = new Matrix();
+ simulatorToCopy.getCurrentMatrix().invert(k0i);
+
+ Matrix animationMatrix = new Matrix();
+ out.addOnFrameCallback(() -> {
+ animationMatrix.set(mt);
+ animationMatrix.postConcat(k0i);
+ animationMatrix.postConcat(simulatorToCopy.getCurrentMatrix());
+ animationMatrix.postConcat(mti);
+ ttv.setAnimationMatrix(animationMatrix);
+ });
+
+ out.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ ttv.setAnimationMatrix(null);
+ }
+ });
+ }
+
+ out.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (isQuickSwitch) {
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ targets.release();
+ super.onAnimationEnd(animation);
+ }
+ });
+
+ if (depthController != null) {
+ out.setFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(context),
+ TOUCH_RESPONSE_INTERPOLATOR);
+ }
+ }
+
+ public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
+ @NonNull RemoteAnimationTargetCompat[] appTargets,
+ @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
+ @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
+ @NonNull DepthController depthController) {
+ boolean skipLauncherChanges = !launcherClosing;
+
+ TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
+ PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
+ createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
+ depthController, pa);
+
+ Animator childStateAnimation = null;
+ // Found a visible recents task that matches the opening app, lets launch the app from there
+ Animator launcherAnim;
+ final AnimatorListenerAdapter windowAnimEndListener;
+ if (launcherClosing) {
+ Context context = v.getContext();
+ DeviceProfile dp = BaseActivity.fromContext(context).getDeviceProfile();
+ launcherAnim = dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()
+ ? ObjectAnimator.ofFloat(recentsView, RecentsView.CONTENT_ALPHA, 0)
+ : 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 onAnimationEnd(Animator animation) {
+ recentsView.post(() -> {
+ stateManager.moveToRestState();
+ stateManager.reapplyState();
+ });
+ }
+ };
+ } else {
+ AnimatorPlaybackController controller =
+ stateManager.createAnimationToNewWorkspace(NORMAL,
+ RECENTS_LAUNCH_DURATION);
+ controller.dispatchOnStart();
+ childStateAnimation = controller.getTarget();
+ launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+ windowAnimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ stateManager.goToState(NORMAL, false);
+ }
+ };
+ }
+ pa.add(launcherAnim);
+ if (LIVE_TILE.get() && recentsView.getRunningTaskIndex() != -1) {
+ pa.addOnFrameCallback(recentsView::redrawLiveTile);
+ }
+ anim.play(pa.buildAnim());
+
+ // Set the current animation first, before adding windowAnimEndListener. Setting current
+ // animation adds some listeners which need to be called before windowAnimEndListener
+ // (the ordering of listeners matter in this case).
+ stateManager.setCurrentAnimation(anim, childStateAnimation);
+ anim.addListener(windowAnimEndListener);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
similarity index 82%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
rename to quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7b91001..fc805d0 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -20,11 +20,10 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS;
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.DEFAULT_STATE;
-import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -38,17 +37,23 @@
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.Choreographer;
+import android.view.Display;
import android.view.InputEvent;
import android.view.MotionEvent;
+import android.view.Surface;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
@@ -58,16 +63,15 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.tracing.nano.LauncherTraceProto;
-import com.android.launcher3.tracing.nano.TouchInteractionServiceProto;
+import com.android.launcher3.tracing.LauncherTraceProto;
+import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
@@ -75,6 +79,7 @@
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
+import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
@@ -91,6 +96,7 @@
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -100,31 +106,13 @@
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.LinkedList;
-import java.util.List;
-
-/**
- * Wrapper around a list for processing arguments.
- */
-class ArgList extends LinkedList<String> {
- public ArgList(List<String> l) {
- super(l);
- }
-
- public String peekArg() {
- return peekFirst();
- }
-
- public String nextArg() {
- return pollFirst().toLowerCase();
- }
-}
/**
* Service connected by system-UI for handling touch interaction.
*/
@TargetApi(Build.VERSION_CODES.R)
public class TouchInteractionService extends Service implements PluginListener<OverscrollPlugin>,
- ProtoTraceable<LauncherTraceProto> {
+ ProtoTraceable<LauncherTraceProto.Builder> {
private static final String TAG = "TouchInteractionService";
@@ -139,6 +127,9 @@
*/
private static final int SYSTEM_ACTION_ID_ALL_APPS = 14;
+ public static final boolean ENABLE_PER_WINDOW_INPUT_ROTATION =
+ SystemProperties.getBoolean("persist.debug.per_window_input_rotation", false);
+
private int mBackGestureNotificationCounter = -1;
@Nullable
private OverscrollPlugin mOverscrollPlugin;
@@ -153,12 +144,17 @@
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
+ mDeviceState.runOnUserUnlocked(() -> {
+ final BaseActivityInterface ai =
+ mOverviewComponentObserver.getActivityInterface();
+ if (ai == null) return;
+ ai.onOverviewServiceBound();
+ });
});
sIsInitialized = true;
}
@BinderThread
- @Override
public void onOverviewToggle() {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
mOverviewCommandHelper.onOverviewToggle();
@@ -182,7 +178,7 @@
@BinderThread
@Override
public void onTip(int actionType, int viewType) {
- mOverviewCommandHelper.onTip(actionType, viewType);
+ // Please delete this method from the interface
}
@BinderThread
@@ -206,18 +202,7 @@
@BinderThread
public void onBackAction(boolean completed, int downX, int downY, boolean isButton,
boolean gestureSwipeLeft) {
- if (mOverviewComponentObserver == null) {
- return;
- }
-
- final BaseActivityInterface activityInterface =
- mOverviewComponentObserver.getActivityInterface();
- UserEventDispatcher.newInstance(getBaseContext()).logActionBack(completed, downX, downY,
- isButton, gestureSwipeLeft, activityInterface.getContainerType());
-
- if (completed && !isButton && shouldNotifyBackGesture()) {
- UI_HELPER_EXECUTOR.execute(TouchInteractionService.this::tryNotifyBackGesture);
- }
+ // Remove this method from the interface
}
@BinderThread
@@ -237,23 +222,6 @@
WindowBounds wb = new WindowBounds(bounds, insets);
MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
}
-
- /** Deprecated methods **/
- public void onQuickStep(MotionEvent motionEvent) { }
-
- public void onQuickScrubEnd() { }
-
- public void onQuickScrubProgress(float progress) { }
-
- public void onQuickScrubStart() { }
-
- public void onPreMotionEvent(int downHitTarget) { }
-
- public void onMotionEvent(MotionEvent ev) {
- ev.recycle();
- }
-
- public void onBind(ISystemUiProxy iSystemUiProxy) { }
};
private static boolean sConnected = false;
@@ -268,9 +236,9 @@
return sIsInitialized;
}
- private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
+ private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
this::createLauncherSwipeHandler;
- private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
+ private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
this::createFallbackSwipeHandler;
private ActivityManagerWrapper mAM;
@@ -289,6 +257,8 @@
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
+ private DisplayManager mDisplayManager;
+
@Override
public void onCreate() {
super.onCreate();
@@ -297,10 +267,12 @@
mMainChoreographer = Choreographer.getInstance();
mAM = ActivityManagerWrapper.getInstance();
mDeviceState = new RecentsAnimationDeviceState(this);
- mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
- mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+ mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);
+ mDeviceState.addOneHandedModeChangedCallback(this::onOneHandedModeOverlayChanged);
+ mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
ProtoTracer.INSTANCE.get(this).add(this);
+ mDisplayManager = getSystemService(DisplayManager.class);
sConnected = true;
}
@@ -318,13 +290,17 @@
private void initInputMonitor() {
disposeEventHandlers();
- if (mDeviceState.isButtonNavMode() || !SystemUiProxy.INSTANCE.get(this).isActive()) {
+
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.TIS_NO_EVENTS, "initInputMonitor: isButtonMode? "
+ + mDeviceState.isButtonNavMode());
+ }
+
+ if (mDeviceState.isButtonNavMode()) {
return;
}
- Bundle bundle = SystemUiProxy.INSTANCE.get(this).monitorGestureInput("swipe-up",
- mDeviceState.getDisplayId());
- mInputMonitorCompat = InputMonitorCompat.fromBundle(bundle, KEY_EXTRA_INPUT_MONITOR);
+ mInputMonitorCompat = new InputMonitorCompat("swipe-up", mDeviceState.getDisplayId());
mInputEventReceiver = mInputMonitorCompat.getInputReceiver(Looper.getMainLooper(),
mMainChoreographer, this::onInputEvent);
@@ -339,9 +315,16 @@
resetHomeBounceSeenOnQuickstepEnabledFirstTime();
}
+ /**
+ * Called when the one handed mode overlay package changes, to recreate touch region.
+ */
+ private void onOneHandedModeOverlayChanged(int newGesturalHeight) {
+ initInputMonitor();
+ }
+
@UiThread
public void onUserUnlocked() {
- mTaskAnimationManager = new TaskAnimationManager();
+ mTaskAnimationManager = new TaskAnimationManager(this);
mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
mOverviewComponentObserver);
@@ -392,7 +375,7 @@
getString(R.string.all_apps_label),
getString(R.string.all_apps_label),
PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
- PendingIntent.FLAG_UPDATE_CURRENT));
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
} else {
am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
@@ -402,15 +385,20 @@
@UiThread
private void onSystemUiFlagsChanged() {
if (mDeviceState.isUserUnlocked()) {
- SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
- mDeviceState.getSystemUiStateFlags());
+ int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
+ SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
mOverviewComponentObserver.onSystemUiStateChanged();
+ mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
+ systemUiStateFlags);
// Update the tracing state
- if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) {
- ProtoTracer.INSTANCE.get(TouchInteractionService.this).start();
+ if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
+ Log.d(TAG, "Starting tracing.");
+ ProtoTracer.INSTANCE.get(this).start();
} else {
- ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
+ Log.d(TAG, "Stopping tracing. Dumping to file="
+ + ProtoTracer.INSTANCE.get(this).getTraceFile());
+ ProtoTracer.INSTANCE.get(this).stop();
}
}
}
@@ -434,7 +422,7 @@
disposeEventHandlers();
mDeviceState.destroy();
SystemUiProxy.INSTANCE.get(this).setProxy(null);
- ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
+ ProtoTracer.INSTANCE.get(this).stop();
ProtoTracer.INSTANCE.get(this).remove(this);
getSystemService(AccessibilityManager.class)
@@ -456,6 +444,15 @@
return;
}
MotionEvent event = (MotionEvent) ev;
+ if (ENABLE_PER_WINDOW_INPUT_ROTATION) {
+ final Display display = mDisplayManager.getDisplay(mDeviceState.getDisplayId());
+ int rotation = display.getRotation();
+ Point sz = new Point();
+ display.getRealSize(sz);
+ if (rotation != Surface.ROTATION_0) {
+ event.transform(InputChannelCompat.createRotationMatrix(rotation, sz.x, sz.y));
+ }
+ }
TestLogging.recordMotionEvent(
TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);
@@ -483,6 +480,7 @@
// onConsumerInactive and wipe the previous gesture state
GestureState prevGestureState = new GestureState(mGestureState);
GestureState newGestureState = createGestureState(mGestureState);
+ newGestureState.setSwipeUpStartTimeMs(SystemClock.uptimeMillis());
mConsumer.onConsumerAboutToBeSwitched();
mGestureState = newGestureState;
mConsumer = newConsumer(prevGestureState, mGestureState, event);
@@ -500,10 +498,21 @@
this,
mGestureState,
InputConsumer.NO_OP, mInputMonitorCompat,
- mOverviewComponentObserver.assistantGestureIsConstrained());
+ mDeviceState,
+ event);
+ } else if (mDeviceState.canTriggerOneHandedAction(event)
+ && !mDeviceState.isOneHandedModeActive()) {
+ // Consume gesture event for triggering one handed feature.
+ mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
+ InputConsumer.NO_OP, mInputMonitorCompat);
} else {
mUncheckedConsumer = InputConsumer.NO_OP;
}
+ } else if (mDeviceState.canTriggerOneHandedAction(event)
+ && !mDeviceState.isOneHandedModeActive()) {
+ // Consume gesture event for triggering one handed feature.
+ mUncheckedConsumer = new OneHandedModeInputConsumer(this, mDeviceState,
+ InputConsumer.NO_OP, mInputMonitorCompat);
} else {
mUncheckedConsumer = InputConsumer.NO_OP;
}
@@ -529,15 +538,21 @@
}
}
- boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
+ boolean cancelGesture = mGestureState.getActivityInterface() != null
+ && mGestureState.getActivityInterface().shouldCancelCurrentGesture();
+ boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
&& mConsumer != null
&& !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
+ if (cancelGesture) {
+ event.setAction(ACTION_CANCEL);
+ }
mUncheckedConsumer.onMotionEvent(event);
if (cleanUpConsumer) {
reset();
}
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
+ ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
}
private GestureState createGestureState(GestureState previousGestureState) {
@@ -586,12 +601,8 @@
}
if (mDeviceState.isFullyGesturalNavMode()) {
if (mDeviceState.canTriggerAssistantAction(event, newGestureState.getRunningTask())) {
- base = new AssistantInputConsumer(
- this,
- newGestureState,
- base,
- mInputMonitorCompat,
- mOverviewComponentObserver.assistantGestureIsConstrained());
+ base = new AssistantInputConsumer(this, newGestureState, base, mInputMonitorCompat,
+ mDeviceState, event);
}
if (FeatureFlags.ENABLE_QUICK_CAPTURE_GESTURE.get()) {
@@ -627,6 +638,11 @@
base = new ScreenPinnedInputConsumer(this, newGestureState);
}
+ if (mDeviceState.canTriggerOneHandedAction(event)) {
+ base = new OneHandedModeInputConsumer(this, mDeviceState, base,
+ mInputMonitorCompat);
+ }
+
if (mDeviceState.isAccessibilityMenuAvailable()) {
base = new AccessibilityInputConsumer(this, mDeviceState, base,
mInputMonitorCompat);
@@ -635,15 +651,16 @@
if (mDeviceState.isScreenPinningActive()) {
base = mResetGestureInputConsumer;
}
+
+ if (mDeviceState.canTriggerOneHandedAction(event)) {
+ base = new OneHandedModeInputConsumer(this, mDeviceState, base,
+ mInputMonitorCompat);
+ }
}
return base;
}
private void handleOrientationSetup(InputConsumer baseInputConsumer) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.1");
- }
-
baseInputConsumer.notifyOrientationSetup();
}
@@ -670,17 +687,16 @@
runningComponent != null && runningComponent.equals(homeComponent);
}
- if (gestureState.getRunningTask() == null) {
+ if (LIVE_TILE.get() && gestureState.getActivityInterface().isInLiveTileMode()) {
+ return createOverviewInputConsumer(
+ previousGestureState, gestureState, event, forceOverviewInputConsumer);
+ } else if (gestureState.getRunningTask() == null) {
return mResetGestureInputConsumer;
} else if (previousGestureState.isRunningAnimationToLauncher()
|| gestureState.getActivityInterface().isResumed()
|| forceOverviewInputConsumer) {
return createOverviewInputConsumer(
previousGestureState, gestureState, event, forceOverviewInputConsumer);
- } else if (ENABLE_QUICKSTEP_LIVE_TILE.get()
- && gestureState.getActivityInterface().isInLiveTileMode()) {
- return createOverviewInputConsumer(
- previousGestureState, gestureState, event, forceOverviewInputConsumer);
} else if (mDeviceState.isGestureBlockedActivity(gestureState.getRunningTask())) {
return mResetGestureInputConsumer;
} else {
@@ -691,7 +707,7 @@
private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
MotionEvent event) {
- final BaseSwipeUpHandler.Factory factory;
+ final AbsSwipeUpHandler.Factory factory;
if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
factory = mFallbackSwipeHandlerFactory;
} else {
@@ -703,7 +719,7 @@
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
return new OtherActivityInputConsumer(this, mDeviceState, mTaskAnimationManager,
gestureState, shouldDefer, this::onConsumerInactive,
- mInputMonitorCompat, disableHorizontalSwipe, factory);
+ mInputMonitorCompat, mInputEventReceiver, disableHorizontalSwipe, factory);
}
private InputConsumer createDeviceLockedInputConsumer(GestureState gestureState) {
@@ -725,8 +741,9 @@
if (activity.getRootView().hasWindowFocus()
|| previousGestureState.isRunningAnimationToLauncher()
- || (FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
- && forceOverviewInputConsumer)) {
+ || (ASSISTANT_GIVES_LAUNCHER_FOCUS.get()
+ && forceOverviewInputConsumer)
+ || (LIVE_TILE.get()) && gestureState.getActivityInterface().isInLiveTileMode()) {
return new OverviewInputConsumer(gestureState, activity, mInputMonitorCompat,
false /* startingInActivityBounds */);
} else {
@@ -750,6 +767,11 @@
private void reset() {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
mGestureState = DEFAULT_STATE;
+ // By default, use batching of the input events, but check receiver before using in the rare
+ // case that the monitor was disposed before the swipe settled
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.setBatchingEnabled(true);
+ }
}
private void preloadOverview(boolean fromInit) {
@@ -772,13 +794,7 @@
mOverviewComponentObserver.getActivityInterface();
final Intent overviewIntent = new Intent(
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
- if (activityInterface.getCreatedActivity() == null) {
- // Make sure that UI states will be initialized.
- activityInterface.createActivityInitListener((wasVisible) -> {
- AppLaunchTracker.INSTANCE.get(TouchInteractionService.this);
- return false;
- }).register(overviewIntent);
- } else if (fromInit) {
+ if (activityInterface.getCreatedActivity() != null && fromInit) {
// The activity has been created before the initialization of overview service. It is
// usually happens when booting or launcher is the top activity, so we should already
// have the latest state.
@@ -802,6 +818,13 @@
}
if (mOverviewComponentObserver.canHandleConfigChanges(activity.getComponentName(),
activity.getResources().getConfiguration().diff(newConfig))) {
+ // Since navBar gestural height are different between portrait and landscape,
+ // can handle orientation changes and refresh navigation gestural region through
+ // onOneHandedModeChanged()
+ int newGesturalHeight = ResourceUtils.getNavbarSize(
+ ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
+ getApplicationContext().getResources());
+ mDeviceState.onOneHandedModeChanged(newGesturalHeight);
return;
}
@@ -811,10 +834,10 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] rawArgs) {
if (rawArgs.length > 0 && Utilities.IS_DEBUG_DEVICE) {
- ArgList args = new ArgList(Arrays.asList(rawArgs));
- switch (args.nextArg()) {
+ LinkedList<String> args = new LinkedList(Arrays.asList(rawArgs));
+ switch (args.pollFirst()) {
case "cmd":
- if (args.peekArg() == null) {
+ if (args.peekFirst() == null) {
printAvailableCommands(pw);
} else {
onCommand(pw, args);
@@ -845,8 +868,7 @@
pw.println(" mConsumer=" + mConsumer.getName());
ActiveGestureLog.INSTANCE.dump("", pw);
pw.println("ProtoTrace:");
- pw.println(" file="
- + ProtoTracer.INSTANCE.get(TouchInteractionService.this).getTraceFile());
+ pw.println(" file=" + ProtoTracer.INSTANCE.get(this).getTraceFile());
}
}
@@ -855,21 +877,21 @@
pw.println(" clear-touch-log: Clears the touch interaction log");
}
- private void onCommand(PrintWriter pw, ArgList args) {
- switch (args.nextArg()) {
+ private void onCommand(PrintWriter pw, LinkedList<String> args) {
+ switch (args.pollFirst()) {
case "clear-touch-log":
ActiveGestureLog.INSTANCE.clear();
break;
}
}
- private BaseSwipeUpHandler createLauncherSwipeHandler(
+ private AbsSwipeUpHandler createLauncherSwipeHandler(
GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
}
- private BaseSwipeUpHandler createFallbackSwipeHandler(
+ private AbsSwipeUpHandler createFallbackSwipeHandler(
GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
@@ -902,11 +924,16 @@
}
@Override
- public void writeToProto(LauncherTraceProto proto) {
- if (proto.touchInteractionService == null) {
- proto.touchInteractionService = new TouchInteractionServiceProto();
+ public void writeToProto(LauncherTraceProto.Builder proto) {
+ TouchInteractionServiceProto.Builder serviceProto =
+ TouchInteractionServiceProto.newBuilder();
+ serviceProto.setServiceConnected(true);
+
+ if (mOverviewComponentObserver != null) {
+ mOverviewComponentObserver.writeToProto(serviceProto);
}
- proto.touchInteractionService.serviceConnected = true;
- proto.touchInteractionService.serviceConnected = true;
+ mConsumer.writeToProto(serviceProto);
+
+ proto.setTouchInteractionService(serviceProto);
}
}
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
new file mode 100644
index 0000000..184ab17
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.os.Handler;
+import android.view.View;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.system.ViewRootImplCompat;
+
+import java.util.function.BooleanSupplier;
+import java.util.function.LongConsumer;
+
+/**
+ * Utility class for helpful methods related to {@link View} objects.
+ */
+public class ViewUtils {
+
+ /** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */
+ public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) {
+ return postFrameDrawn(view, onFinishRunnable, () -> false);
+ }
+
+ /**
+ * Inject some addition logic in order to make sure that the view is updated smoothly post
+ * draw, and allow addition task to be run after view update.
+ *
+ * @param onFinishRunnable runnable to be run right after the view finishes drawing.
+ */
+ public static boolean postFrameDrawn(
+ View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
+ return new FrameHandler(view, onFinishRunnable, canceled).schedule();
+ }
+
+ private static class FrameHandler implements LongConsumer {
+
+ final ViewRootImplCompat mViewRoot;
+ final Runnable mFinishCallback;
+ final BooleanSupplier mCancelled;
+ final Handler mHandler;
+
+ int mDeferFrameCount = 1;
+
+ FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
+ mViewRoot = new ViewRootImplCompat(view);
+ mFinishCallback = finishCallback;
+ mCancelled = cancelled;
+ mHandler = new Handler();
+ }
+
+ @Override
+ public void accept(long l) {
+ Utilities.postAsyncCallback(mHandler, this::onFrame);
+ }
+
+ private void onFrame() {
+ if (mCancelled.getAsBoolean()) {
+ return;
+ }
+
+ if (mDeferFrameCount > 0) {
+ mDeferFrameCount--;
+ schedule();
+ return;
+ }
+
+ if (mFinishCallback != null) {
+ mFinishCallback.run();
+ }
+ }
+
+ private boolean schedule() {
+ if (mViewRoot.isValid()) {
+ mViewRoot.registerRtFrameCallback(this);
+ mViewRoot.getView().invalidate();
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
index be3fdde..a4a7bd3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackNavBarTouchController.java
@@ -21,7 +21,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.SysUINavigationMode;
@@ -43,7 +43,7 @@
SysUINavigationMode.Mode sysUINavigationMode = SysUINavigationMode.getMode(mActivity);
if (sysUINavigationMode == SysUINavigationMode.Mode.NO_BUTTON) {
NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode,
- DefaultDisplay.INSTANCE.get(mActivity).getInfo());
+ DisplayController.getDefaultDisplay(mActivity).getInfo());
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
true /* disableHorizontalSwipe */, navBarPosition,
null /* onInterceptTouch */, this);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
similarity index 84%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index ffe9d6a..13f6137 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.fallback;
+import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
@@ -23,10 +24,13 @@
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
+import android.util.Log;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.testing.TestProtocol;
import com.android.quickstep.FallbackActivityInterface;
+import com.android.quickstep.GestureState;
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
@@ -63,12 +67,6 @@
mActivity.startHome();
}
- @Override
- public boolean shouldUseMultiWindowTaskSizeStrategy() {
- // Just use the activity task size for multi-window as well.
- return false;
- }
-
/**
* When starting gesture interaction from home, we add a temporary invisible tile corresponding
* to the home task. This allows us to handle quick-switch similarly to a quick-switching
@@ -80,14 +78,14 @@
}
/**
- * When the gesture ends and recents view become interactive, we also remove the temporary
+ * When the gesture ends and we're going to recents view, we also remove the temporary
* invisible tile added for the home task. This also pushes the remaining tiles back
* to the center.
*/
@Override
- public void onGestureAnimationEnd() {
- super.onGestureAnimationEnd();
- if (mHomeTaskInfo != null) {
+ public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+ super.onGestureEndTargetCalculated(endTarget);
+ if (mHomeTaskInfo != null && endTarget == RECENTS) {
TaskView tv = getTaskView(mHomeTaskInfo.taskId);
if (tv != null) {
PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
@@ -107,23 +105,27 @@
}
@Override
- protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+ protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
if (mHomeTaskInfo != null && runningTaskInfo != null &&
mHomeTaskInfo.taskId == runningTaskInfo.taskId
&& getTaskViewCount() == 0) {
- // Do not add a dummy task if we are running over home with empty recents, so that we
- // show the empty recents message instead of showing a dummy task and later removing it.
+ // Do not add a stub task if we are running over home with empty recents, so that we
+ // show the empty recents message instead of showing a stub task and later removing it.
return false;
}
- return super.shouldAddDummyTaskView(runningTaskInfo);
+ return super.shouldAddStubTaskView(runningTaskInfo);
}
@Override
protected void applyLoadPlan(ArrayList<Task> tasks) {
- // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
+ // When quick-switching on 3p-launcher, we add a "stub" tile corresponding to Launcher
// as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
// track the index of the next task appropriately, as if we are switching on any other app.
if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId && !tasks.isEmpty()) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED,
+ "FallbackRecentsView.applyLoadPlan: running task is home");
+ }
// Check if the task list has running task
boolean found = false;
for (Task t : tasks) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java b/quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java
rename to quickstep/src/com/android/quickstep/fallback/RecentsDragLayer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsState.java
rename to quickstep/src/com/android/quickstep/fallback/RecentsState.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
rename to quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
similarity index 82%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 89e6931..a3cd7df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -25,12 +25,6 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPLEFT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction.UPRIGHT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.FLING;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.SWIPE_NOOP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.NAVBAR;
import android.animation.ValueAnimator;
import android.content.Context;
@@ -47,13 +41,15 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.InputMonitorCompat;
+import java.util.function.Consumer;
+
/**
* Touch consumer for handling events to launch assistant from launcher
*/
@@ -80,7 +76,6 @@
private float mTimeFraction;
private long mDragTime;
private float mLastProgress;
- private int mDirection;
private BaseActivityInterface mActivityInterface;
private final float mDragDistThreshold;
@@ -89,19 +84,18 @@
private final int mAngleThreshold;
private final float mSquaredSlop;
private final Context mContext;
- private final GestureDetector mGestureDetector;
- private final boolean mIsAssistGestureConstrained;
+ private final Consumer<MotionEvent> mGestureDetector;
public AssistantInputConsumer(
Context context,
GestureState gestureState,
InputConsumer delegate,
InputMonitorCompat inputMonitor,
- boolean isAssistGestureConstrained) {
+ RecentsAnimationDeviceState deviceState,
+ MotionEvent startEvent) {
super(delegate, inputMonitor);
final Resources res = context.getResources();
mContext = context;
- mIsAssistGestureConstrained = isAssistGestureConstrained;
mDragDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
mFlingDistThreshold = res.getDimension(R.dimen.gestures_assistant_fling_threshold);
mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
@@ -112,7 +106,11 @@
mSquaredSlop = slop * slop;
mActivityInterface = gestureState.getActivityInterface();
- mGestureDetector = new GestureDetector(context, new AssistantGestureListener());
+ boolean flingDisabled = deviceState.isAssistantGestureIsConstrained()
+ || deviceState.isInDeferredGestureRegion(startEvent);
+ mGestureDetector = flingDisabled
+ ? ev -> { }
+ : new GestureDetector(context, new AssistantGestureListener())::onTouchEvent;
}
@Override
@@ -197,8 +195,6 @@
if (mState != STATE_DELEGATE_ACTIVE && !mLaunchedAssistant) {
ValueAnimator animator = ValueAnimator.ofFloat(mLastProgress, 0)
.setDuration(RETRACT_ANIMATION_DURATION_MS);
- UserEventDispatcher.newInstance(mContext).logActionOnContainer(
- SWIPE_NOOP, mDirection, NAVBAR);
animator.addUpdateListener(valueAnimator -> {
float progress = (float) valueAnimator.getAnimatedValue();
SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(progress);
@@ -211,7 +207,7 @@
break;
}
- mGestureDetector.onTouchEvent(ev);
+ mGestureDetector.accept(ev);
if (mState != STATE_ACTIVE) {
mDelegate.onMotionEvent(ev);
@@ -223,29 +219,26 @@
mLastProgress = Math.min(mDistance * 1f / mDragDistThreshold, 1) * mTimeFraction;
if (mDistance >= mDragDistThreshold && mTimeFraction >= 1) {
SystemUiProxy.INSTANCE.get(mContext).onAssistantGestureCompletion(0);
- startAssistantInternal(SWIPE);
-
- Bundle args = new Bundle();
- args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
- args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
- SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
- mLaunchedAssistant = true;
+ startAssistantInternal();
} else {
SystemUiProxy.INSTANCE.get(mContext).onAssistantProgress(mLastProgress);
}
}
}
- private void startAssistantInternal(int gestureType) {
- UserEventDispatcher.newInstance(mContext)
- .logActionOnContainer(gestureType, mDirection, NAVBAR);
-
+ private void startAssistantInternal() {
BaseDraggingActivity launcherActivity = mActivityInterface.getCreatedActivity();
if (launcherActivity != null) {
launcherActivity.getRootView().performHapticFeedback(
13, // HapticFeedbackConstants.GESTURE_END
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+
+ Bundle args = new Bundle();
+ args.putInt(OPA_BUNDLE_TRIGGER, OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE);
+ args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
+ SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
+ mLaunchedAssistant = true;
}
/**
@@ -253,7 +246,6 @@
*/
private boolean isValidAssistantGestureAngle(float deltaX, float deltaY) {
float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
- mDirection = angle > 90 ? UPLEFT : UPRIGHT;
// normalize so that angle is measured clockwise from horizontal in the bottom right corner
// and counterclockwise from horizontal in the bottom left corner
@@ -264,20 +256,14 @@
private class AssistantGestureListener extends SimpleOnGestureListener {
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- if (!mIsAssistGestureConstrained
- && isValidAssistantGestureAngle(velocityX, -velocityY)
+ if (isValidAssistantGestureAngle(velocityX, -velocityY)
&& mDistance >= mFlingDistThreshold
&& !mLaunchedAssistant
&& mState != STATE_DELEGATE_ACTIVE) {
mLastProgress = 1;
SystemUiProxy.INSTANCE.get(mContext).onAssistantGestureCompletion(
(float) Math.sqrt(velocityX * velocityX + velocityY * velocityY));
- startAssistantInternal(FLING);
-
- Bundle args = new Bundle();
- args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_GESTURE);
- SystemUiProxy.INSTANCE.get(mContext).startAssistant(args);
- mLaunchedAssistant = true;
+ startAssistantInternal();
}
return true;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
similarity index 88%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
index 67a15a7..8da2fd3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DelegateInputConsumer.java
@@ -4,6 +4,7 @@
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.tracing.InputConsumerProto;
import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat;
@@ -53,4 +54,9 @@
mDelegate.onMotionEvent(event);
event.recycle();
}
+
+ @Override
+ public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
+ mDelegate.writeToProtoInternal(inputConsumerProto);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
similarity index 93%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index a676390..85ecab1 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -19,9 +19,11 @@
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.quickstep.BaseSwipeUpHandlerV2.MIN_PROGRESS_FOR_OVERVIEW;
+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.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
@@ -35,13 +37,12 @@
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
@@ -60,7 +61,7 @@
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
/**
- * A dummy input consumer used when the device is still locked, e.g. from secure camera.
+ * A placeholder input consumer used when the device is still locked, e.g. from secure camera.
*/
public class DeviceLockedInputConsumer implements InputConsumer,
RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy {
@@ -115,7 +116,7 @@
R.dimen.device_locked_y_offset);
// Do not use DeviceProfile as the user data might be locked
- mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
+ mDisplaySize = DisplayController.getDefaultDisplay(context).getInfo().realSize;
// Init states
mStateCallback = new MultiStateCallback(STATE_NAMES);
@@ -180,12 +181,11 @@
*/
private void finishTouchTracking(MotionEvent ev) {
if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
+ mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
float velocityY = mVelocityTracker.getYVelocity();
float flingThreshold = mContext.getResources()
- .getDimension(R.dimen.quickstep_fling_threshold_velocity);
+ .getDimension(R.dimen.quickstep_fling_threshold_speed);
boolean dismissTask;
if (Math.abs(velocityY) > flingThreshold) {
@@ -204,9 +204,7 @@
public void onAnimationEnd(Animator animation) {
if (dismissTask) {
// For now, just start the home intent so user is prompted to unlock the device.
- mContext.startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mContext.startActivity(createHomeIntent());
mHomeLaunched = true;
}
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
new file mode 100644
index 0000000..cd69cf1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.inputconsumers;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+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.Utilities.squaredHypot;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.view.MotionEvent;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.DisplayController;
+import com.android.quickstep.InputConsumer;
+import com.android.quickstep.RecentsAnimationDeviceState;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.InputMonitorCompat;
+
+/**
+ * Touch consumer for handling gesture event to launch one handed
+ * One handed gestural in quickstep only active on NO_BUTTON, TWO_BUTTONS, and portrait mode
+ */
+public class OneHandedModeInputConsumer extends DelegateInputConsumer {
+
+ private static final String TAG = "OneHandedModeInputConsumer";
+ private static final int ANGLE_MAX = 150;
+ private static final int ANGLE_MIN = 30;
+
+ private final Context mContext;
+ private final DisplayController.DisplayHolder mDisplayHolder;
+ private final Point mDisplaySize;
+ private final RecentsAnimationDeviceState mDeviceState;
+
+ private final float mDragDistThreshold;
+ private final float mSquaredSlop;
+
+ private final int mNavBarSize;
+
+ private final PointF mDownPos = new PointF();
+ private final PointF mLastPos = new PointF();
+
+ private boolean mPassedSlop;
+ private boolean mIsStopGesture;
+
+ public OneHandedModeInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+ InputConsumer delegate, InputMonitorCompat inputMonitor) {
+ super(delegate, inputMonitor);
+ mContext = context;
+ mDisplayHolder = DisplayController.getDefaultDisplay(mContext);
+ mDeviceState = deviceState;
+ mDragDistThreshold = context.getResources().getDimensionPixelSize(
+ R.dimen.gestures_onehanded_drag_threshold);
+ mSquaredSlop = Utilities.squaredTouchSlop(context);
+ mDisplaySize = mDisplayHolder.getInfo().realSize;
+ mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE,
+ mContext.getResources());
+ }
+
+ @Override
+ public int getType() {
+ return TYPE_ONE_HANDED | mDelegate.getType();
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ break;
+ }
+ case ACTION_MOVE: {
+ if (mState == STATE_DELEGATE_ACTIVE) {
+ break;
+ }
+ if (!mDelegate.allowInterceptByParent()) {
+ mState = STATE_DELEGATE_ACTIVE;
+ break;
+ }
+
+ mLastPos.set(ev.getX(), ev.getY());
+ if (!mPassedSlop) {
+ if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
+ > mSquaredSlop) {
+ if ((!mDeviceState.isOneHandedModeActive() && isValidStartAngle(
+ mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))
+ || (mDeviceState.isOneHandedModeActive() && isValidExitAngle(
+ mDownPos.x - mLastPos.x, mDownPos.y - mLastPos.y))) {
+ // To avoid mis-trigger when motion not touch system gesture region.
+ mPassedSlop = isInSystemGestureRegion(mLastPos);
+ setActive(ev);
+ } else {
+ mState = STATE_DELEGATE_ACTIVE;
+ }
+ }
+ } else {
+ float distance = (float) Math.hypot(mLastPos.x - mDownPos.x,
+ mLastPos.y - mDownPos.y);
+ if (distance > mDragDistThreshold && mPassedSlop) {
+ mIsStopGesture = true;
+ }
+ }
+ break;
+ }
+ case ACTION_UP: {
+ if (mLastPos.y >= mDownPos.y && mPassedSlop) {
+ onStartGestureDetected();
+ } else if (mIsStopGesture) {
+ onStopGestureDetected();
+ }
+ clearState();
+ break;
+ }
+ case ACTION_CANCEL:
+ clearState();
+ break;
+ }
+
+ if (mState != STATE_ACTIVE) {
+ mDelegate.onMotionEvent(ev);
+ }
+ }
+
+ private void clearState() {
+ mPassedSlop = false;
+ mState = STATE_INACTIVE;
+ mIsStopGesture = false;
+ }
+
+ private void onStartGestureDetected() {
+ if (mDeviceState.isOneHandedModeEnabled()) {
+ if (!mDeviceState.isOneHandedModeActive()) {
+ SystemUiProxy.INSTANCE.get(mContext).startOneHandedMode();
+ }
+ } else if (mDeviceState.isSwipeToNotificationEnabled()) {
+ SystemUiProxy.INSTANCE.get(mContext).expandNotificationPanel();
+ }
+ }
+
+ private void onStopGestureDetected() {
+ if (!mDeviceState.isOneHandedModeEnabled() || !mDeviceState.isOneHandedModeActive()) {
+ return;
+ }
+
+ SystemUiProxy.INSTANCE.get(mContext).stopOneHandedMode();
+ }
+
+ private boolean isInSystemGestureRegion(PointF lastPos) {
+ final int navBarUpperBound = mDisplaySize.y - mNavBarSize;
+ return mDeviceState.isGesturalNavMode() && lastPos.y > navBarUpperBound;
+ }
+
+ private boolean isValidStartAngle(float deltaX, float deltaY) {
+ final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
+ return angle > -(ANGLE_MAX) && angle < -(ANGLE_MIN);
+ }
+
+ private boolean isValidExitAngle(float deltaX, float deltaY) {
+ final float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
+ return angle > ANGLE_MIN && angle < ANGLE_MAX;
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
similarity index 89%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 6259f1f..5baf518 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -28,6 +28,7 @@
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
import static com.android.launcher3.Utilities.squaredHypot;
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.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
@@ -37,10 +38,12 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.PointF;
+import android.hardware.display.DisplayManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import android.view.Display;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
@@ -50,22 +53,26 @@
import com.android.launcher3.R;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
+import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.AbsSwipeUpHandler;
+import com.android.quickstep.AbsSwipeUpHandler.Factory;
import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.BaseSwipeUpHandler.Factory;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TouchInteractionService;
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;
import java.util.function.Consumer;
@@ -91,9 +98,10 @@
private RecentsAnimationCallbacks mActiveCallbacks;
private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
private final InputMonitorCompat mInputMonitorCompat;
+ private final InputEventReceiver mInputEventReceiver;
private final BaseActivityInterface mActivityInterface;
- private final BaseSwipeUpHandler.Factory mHandlerFactory;
+ private final AbsSwipeUpHandler.Factory mHandlerFactory;
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
@@ -101,13 +109,15 @@
private VelocityTracker mVelocityTracker;
- private BaseSwipeUpHandler mInteractionHandler;
+ private AbsSwipeUpHandler mInteractionHandler;
private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private int mActivePointerId = INVALID_POINTER_ID;
+ private int mLastRotation = -1;
+
// Distance after which we start dragging the window.
private final float mTouchSlop;
@@ -125,6 +135,8 @@
// Might be displacement in X or Y, depending on the direction we are swiping from the nav bar.
private float mStartDisplacement;
+ private final DisplayManager mDisplayManager;
+
private Handler mMainThreadHandler;
private Runnable mCancelRecentsAnimationRunnable = () -> {
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
@@ -134,8 +146,8 @@
public OtherActivityInputConsumer(Context base, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
boolean isDeferredDownTarget, Consumer<OtherActivityInputConsumer> onCompleteCallback,
- InputMonitorCompat inputMonitorCompat, boolean disableHorizontalSwipe,
- Factory handlerFactory) {
+ InputMonitorCompat inputMonitorCompat, InputEventReceiver inputEventReceiver,
+ boolean disableHorizontalSwipe, Factory handlerFactory) {
super(base);
mDeviceState = deviceState;
mNavBarPosition = mDeviceState.getNavBarPosition();
@@ -153,6 +165,7 @@
mOnCompleteCallback = onCompleteCallback;
mVelocityTracker = VelocityTracker.obtain();
mInputMonitorCompat = inputMonitorCompat;
+ mInputEventReceiver = inputEventReceiver;
boolean continuingPreviousGesture = mTaskAnimationManager.isRecentsAnimationRunning();
mIsDeferredDownTarget = !continuingPreviousGesture && isDeferredDownTarget;
@@ -166,6 +179,7 @@
mPassedPilferInputSlop = mPassedWindowMoveSlop = continuingPreviousGesture;
mDisableHorizontalSwipe = !mPassedPilferInputSlop && disableHorizontalSwipe;
mRotationTouchHelper = mDeviceState.getRotationTouchHelper();
+ mDisplayManager = getSystemService(DisplayManager.class);
}
@Override
@@ -191,6 +205,17 @@
return;
}
+ if (TouchInteractionService.ENABLE_PER_WINDOW_INPUT_ROTATION) {
+ final Display display = mDisplayManager.getDisplay(mDeviceState.getDisplayId());
+ final int rotation = display.getRotation();
+ if (rotation != mLastRotation) {
+ // If rotation changes, reset tracking to avoid degenerate velocities.
+ mLastPos.set(ev.getX(), ev.getY());
+ mVelocityTracker.clear();
+ mLastRotation = rotation;
+ }
+ }
+
// Proxy events to recents view
if (mPassedWindowMoveSlop && mInteractionHandler != null
&& !mRecentsViewDispatcher.hasConsumer()) {
@@ -214,6 +239,9 @@
switch (ev.getActionMasked()) {
case ACTION_DOWN: {
+ // Until we detect the gesture, handle events as we receive them
+ mInputEventReceiver.setBatchingEnabled(false);
+
Object traceToken = TraceHelper.INSTANCE.beginSection(DOWN_EVT,
FLAG_CHECK_FOR_RACE_CONDITIONS);
mActivePointerId = ev.getPointerId(0);
@@ -350,10 +378,11 @@
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
+ // Once we detect the gesture, we can enable batching to reduce further updates
+ mInputEventReceiver.setBatchingEnabled(true);
mActivityInterface.closeOverlay();
- ActivityManagerWrapper.getInstance().closeSystemWindows(
- CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
// Notify the handler that the gesture has actually started
mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
@@ -365,7 +394,7 @@
mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
mTaskAnimationManager.isRecentsAnimationRunning());
mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
- mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler::onMotionPauseChanged);
+ mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
mInteractionHandler.initWhenReady(intent);
@@ -393,8 +422,7 @@
if (ev.getActionMasked() == ACTION_CANCEL) {
mInteractionHandler.onGestureCancelled();
} else {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+ mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
float velocity = mNavBarPosition.isRightEdge()
@@ -470,4 +498,11 @@
public boolean allowInterceptByParent() {
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
}
+
+ @Override
+ public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.writeToProto(inputConsumerProto);
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OverscrollInputConsumer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
similarity index 87%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
index 9bfe84f..cee3363 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewInputConsumer.java
@@ -15,10 +15,9 @@
*/
package com.android.quickstep.inputconsumers;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -32,8 +31,8 @@
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.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
/**
@@ -80,9 +79,6 @@
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
}
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
- }
boolean handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
@@ -91,8 +87,7 @@
mTargetHandledTouch = true;
if (!mStartingInActivityBounds) {
mActivityInterface.closeOverlay();
- ActivityManagerWrapper.getInstance()
- .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+ TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
@@ -104,7 +99,7 @@
@Override
public void onKeyEvent(KeyEvent ev) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
mActivity.dispatchKeyEvent(ev);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
similarity index 79%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 1c77a05..864e08d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,9 +15,12 @@
*/
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 android.content.ActivityNotFoundException;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
@@ -27,9 +30,6 @@
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationDeviceState;
@@ -79,20 +79,19 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
- mContext.startActivity(mGestureState.getHomeIntent());
+ try {
+ mContext.startActivity(mGestureState.getHomeIntent());
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ mContext.startActivity(createHomeIntent());
+ }
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
- int pageIndex = -1; // This number doesn't reflect workspace page index.
- // It only indicates that launcher client screen was shown.
- int containerType = (mGestureState != null && mGestureState.getEndTarget() != null)
+ int state = (mGestureState != null && mGestureState.getEndTarget() != null)
? mGestureState.getEndTarget().containerType
- : LauncherLogProto.ContainerType.WORKSPACE;
- activity.getUserEventDispatcher().logActionOnContainer(
- wasFling ? Touch.FLING : Touch.SWIPE, Direction.UP, containerType, pageIndex);
- activity.getUserEventDispatcher().setPreviousHomeGesture(true);
+ : LAUNCHER_STATE_HOME;
activity.getStatsLogManager().logger()
- .withSrcState(LAUNCHER_STATE_HOME)
- .withDstState(LAUNCHER_STATE_HOME)
+ .withSrcState(LAUNCHER_STATE_BACKGROUND)
+ .withDstState(state)
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder()
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/ResetGestureInputConsumer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
similarity index 79%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index b9827ff..a8bf333 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -42,18 +42,16 @@
mMotionPauseMinDisplacement = context.getResources().getDimension(
R.dimen.motion_pause_detector_min_displacement_from_app);
mMotionPauseDetector = new MotionPauseDetector(context, true /* makePauseHarderToTrigger*/);
- mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
- if (isPaused) {
- SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
- BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
- .getCreatedActivity();
- if (launcherActivity != null) {
- launcherActivity.getRootView().performHapticFeedback(
- HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- }
- mMotionPauseDetector.clear();
+ mMotionPauseDetector.setOnMotionPauseListener(() -> {
+ SystemUiProxy.INSTANCE.get(context).stopScreenPinning();
+ BaseDraggingActivity launcherActivity = gestureState.getActivityInterface()
+ .getCreatedActivity();
+ if (launcherActivity != null) {
+ launcherActivity.getRootView().performHapticFeedback(
+ HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
+ mMotionPauseDetector.clear();
});
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
rename to quickstep/src/com/android/quickstep/inputconsumers/SysUiOverlayInputConsumer.java
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
index 6862f07..5c81e5f 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialController.java
@@ -104,7 +104,13 @@
hideFeedback();
hideHandCoachingAnimation();
showRippleEffect(
- () -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
+ () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(ASSISTANT_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
diff --git a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
index 70181fb..16886ec 100644
--- a/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/AssistantGestureTutorialFragment.java
@@ -24,7 +24,7 @@
/** Shows the Home gesture interactive tutorial. */
public class AssistantGestureTutorialFragment extends TutorialFragment {
@Override
- int getHandAnimationResId() {
+ Integer getHandAnimationResId() {
return R.drawable.assistant_gesture;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index 921e568..161e015 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -130,7 +130,13 @@
hideFeedback();
hideHandCoachingAnimation();
showRippleEffect(
- () -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
+ () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
case BACK_CANCELLED_FROM_LEFT:
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
index bef50ea..41db684 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialFragment.java
@@ -24,7 +24,7 @@
/** Shows the Back gesture interactive tutorial. */
public class BackGestureTutorialFragment extends TutorialFragment {
@Override
- int getHandAnimationResId() {
+ Integer getHandAnimationResId() {
return R.drawable.back_gesture;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
index e4b348e..9489bac 100644
--- a/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/EdgeBackGestureHandler.java
@@ -143,6 +143,10 @@
return false;
}
+ boolean onInterceptTouch(MotionEvent motionEvent) {
+ return isWithinTouchRegion((int) motionEvent.getX(), (int) motionEvent.getY());
+ }
+
private boolean isWithinTouchRegion(int x, int y) {
// Disallow if too far from the edge
if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) {
diff --git a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index f8d9d8d..8b6777b 100644
--- a/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -15,8 +15,6 @@
*/
package com.android.quickstep.interaction;
-import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
-
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
@@ -25,11 +23,14 @@
import android.view.View;
import android.view.Window;
+import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType;
+import java.util.ArrayDeque;
+import java.util.Deque;
import java.util.List;
/** Shows the gesture interactive sandbox in full screen mode. */
@@ -37,6 +38,9 @@
private static final String LOG_TAG = "GestureSandboxActivity";
+ private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
+
+ private Deque<TutorialType> mTutorialSteps;
private TutorialFragment mFragment;
@Override
@@ -45,7 +49,9 @@
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity);
- mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras()));
+ Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
+ mTutorialSteps = getTutorialSteps(args);
+ mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit();
@@ -72,17 +78,65 @@
}
}
- private TutorialType getTutorialType(Bundle extras) {
- TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
+ savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
+ super.onSaveInstanceState(savedInstanceState);
+ }
- if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
- return defaultType;
+ /** Returns true iff there aren't anymore tutorial types to display to the user. */
+ public boolean isTutorialComplete() {
+ return mTutorialSteps.isEmpty();
+ }
+
+ /**
+ * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
+ *
+ * If there is no following step, the tutorial is closed.
+ */
+ public void continueTutorial() {
+ if (isTutorialComplete()) {
+ mFragment.closeTutorial();
+ return;
}
- try {
- return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
- } catch (IllegalArgumentException e) {
- return defaultType;
+ mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.gesture_tutorial_fragment_container, mFragment)
+ .runOnCommit(() -> mFragment.onAttachedToWindow())
+ .commit();
+ }
+
+ private String[] getTutorialStepNames() {
+ String[] tutorialStepNames = new String[mTutorialSteps.size()];
+
+ int i = 0;
+ for (TutorialType tutorialStep : mTutorialSteps) {
+ tutorialStepNames[i++] = tutorialStep.name();
}
+
+ return tutorialStepNames;
+ }
+
+ private Deque<TutorialType> getTutorialSteps(Bundle extras) {
+ Deque<TutorialType> defaultSteps = new ArrayDeque<>();
+ defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
+
+ if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
+ return defaultSteps;
+ }
+
+ String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
+
+ if (tutorialStepNames == null) {
+ return defaultSteps;
+ }
+
+ Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
+ for (String tutorialStepName : tutorialStepNames) {
+ tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
+ }
+
+ return tutorialSteps;
}
private void hideSystemUI() {
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
index 0edabd4..95352d1 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialController.java
@@ -94,8 +94,13 @@
case HOME_NAVIGATION:
switch (result) {
case HOME_GESTURE_COMPLETED: {
- animateFakeTaskViewHome(finalVelocity, () ->
- mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
+ animateFakeTaskViewHome(finalVelocity, () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
}
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
diff --git a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
index e2a9d12..63595a5 100644
--- a/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/HomeGestureTutorialFragment.java
@@ -21,7 +21,7 @@
/** Shows the Home gesture interactive tutorial. */
public class HomeGestureTutorialFragment extends TutorialFragment {
@Override
- int getHandAnimationResId() {
+ Integer getHandAnimationResId() {
return R.drawable.home_gesture;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 81c4d0c..a9a9e2a 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -56,7 +56,7 @@
/** Utility class to handle Home and Assistant gestures. */
public class NavBarGestureHandler implements OnTouchListener,
- TriggerSwipeUpTouchTracker.OnSwipeUpListener {
+ TriggerSwipeUpTouchTracker.OnSwipeUpListener, MotionPauseDetector.OnMotionPauseListener {
private static final String LOG_TAG = "NavBarGestureHandler";
private static final long RETRACT_GESTURE_ANIMATION_DURATION_MS = 300;
@@ -144,7 +144,6 @@
if (mGestureCallback == null || mAssistantGestureActive) {
return;
}
- finalVelocity.set(finalVelocity.x / 1000, finalVelocity.y / 1000);
if (mTouchCameFromNavBar) {
mGestureCallback.onNavBarGestureAttempted(wasFling
? HOME_GESTURE_COMPLETED : OVERVIEW_GESTURE_COMPLETED, finalVelocity);
@@ -182,7 +181,7 @@
mLaunchedAssistant = false;
mSwipeUpTouchTracker.init();
mMotionPauseDetector.clear();
- mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
+ mMotionPauseDetector.setOnMotionPauseListener(this);
break;
case MotionEvent.ACTION_MOVE:
mLastPos.set(event.getX(), event.getY());
@@ -220,7 +219,6 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mMotionPauseDetector.clear();
- mMotionPauseDetector.setOnMotionPauseListener(null);
if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
mGestureCallback.onNavBarGestureAttempted(
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
@@ -252,10 +250,20 @@
return intercepted;
}
- protected void onMotionPauseChanged(boolean isPaused) {
- if (isPaused) {
- VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
- }
+ boolean onInterceptTouch(MotionEvent event) {
+ return mAssistantLeftRegion.contains(event.getX(), event.getY())
+ || mAssistantRightRegion.contains(event.getX(), event.getY())
+ || event.getY() >= mDisplaySize.y - mBottomGestureHeight;
+ }
+
+ @Override
+ public void onMotionPauseChanged(boolean isPaused) {
+ mGestureCallback.onMotionPaused(isPaused);
+ }
+
+ @Override
+ public void onMotionPauseDetected() {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
}
/**
@@ -309,6 +317,9 @@
/** Called whenever any touch is completed. */
void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity);
+ /** Called when a motion stops or resumes */
+ default void onMotionPaused(boolean isPaused) {}
+
/** Indicates how far a touch originating in the nav bar has moved from the nav bar. */
default void setNavBarGestureProgress(@Nullable Float displacement) {}
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index c636eba..45cbd0b 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -104,8 +104,13 @@
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
break;
case OVERVIEW_GESTURE_COMPLETED:
- fadeOutFakeTaskView(true, () ->
- mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
+ fadeOutFakeTaskView(true, () -> {
+ if (mTutorialFragment.isTutorialComplete()) {
+ mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
+ } else {
+ mTutorialFragment.continueTutorial();
+ }
+ });
break;
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
case HOME_OR_OVERVIEW_CANCELLED:
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
index 3357b70..93200bb 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialFragment.java
@@ -21,7 +21,7 @@
/** Shows the Overview gesture interactive tutorial. */
public class OverviewGestureTutorialFragment extends TutorialFragment {
@Override
- int getHandAnimationResId() {
+ Integer getHandAnimationResId() {
return R.drawable.overview_gesture;
}
diff --git a/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
new file mode 100644
index 0000000..db1afc2
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/RootSandboxLayout.java
@@ -0,0 +1,44 @@
+/*
+ * 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.interaction;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.RelativeLayout;
+
+import androidx.fragment.app.FragmentManager;
+
+/** Root layout that TutorialFragment uses to intercept motion events. */
+public class RootSandboxLayout extends RelativeLayout {
+ public RootSandboxLayout(Context context) {
+ super(context);
+ }
+
+ public RootSandboxLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public RootSandboxLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent motionEvent) {
+ return ((TutorialFragment) FragmentManager.findFragment(this))
+ .onInterceptTouch(motionEvent);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java b/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java
new file mode 100644
index 0000000..80ffe66
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java
@@ -0,0 +1,44 @@
+/*
+ * 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.interaction;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.graphics.LauncherPreviewRenderer;
+
+/** Renders a fake Launcher for use in the Sandbox. */
+class SandboxLauncherRenderer extends LauncherPreviewRenderer {
+ SandboxLauncherRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
+ super(context, idp, migrated);
+ }
+
+ @Override
+ public boolean shouldShowRealLauncherPreview() {
+ return false;
+ }
+
+ @Override
+ public boolean shouldShowQsb() {
+ return false;
+ }
+
+ @Override
+ public View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
+ return null;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
new file mode 100644
index 0000000..4c1a595
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialController.java
@@ -0,0 +1,102 @@
+/*
+ * 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.interaction;
+
+import android.graphics.PointF;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureResult;
+import com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult;
+
+/** A {@link TutorialController} for the Sandbox Mode. */
+public class SandboxModeTutorialController extends SwipeUpGestureTutorialController {
+
+ SandboxModeTutorialController(SandboxModeTutorialFragment fragment, TutorialType tutorialType) {
+ super(fragment, tutorialType);
+ }
+
+ @Nullable
+ @Override
+ Integer getTitleStringId() {
+ return R.string.sandbox_mode_title;
+ }
+
+ @Nullable
+ @Override
+ Integer getSubtitleStringId() {
+ return R.string.sandbox_mode_subtitle;
+ }
+
+ @Nullable
+ @Override
+ Integer getActionButtonStringId() {
+ return null;
+ }
+
+ @Override
+ void onActionButtonClicked(View button) {
+ mTutorialFragment.closeTutorial();
+ }
+
+ @Override
+ public void onBackGestureAttempted(BackGestureResult result) {
+ switch (result) {
+ case BACK_COMPLETED_FROM_LEFT:
+ case BACK_COMPLETED_FROM_RIGHT:
+ showRippleEffect(null);
+ showFeedback(R.string.sandbox_mode_back_gesture_feedback_successful);
+ break;
+ case BACK_CANCELLED_FROM_RIGHT:
+ showFeedback(R.string.back_gesture_feedback_cancelled_right_edge);
+ break;
+ case BACK_CANCELLED_FROM_LEFT:
+ showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
+ break;
+ case BACK_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ showFeedback(R.string.sandbox_mode_back_gesture_feedback_swipe_too_far_from_edge);
+ break;
+ }
+ }
+
+ @Override
+ public void onNavBarGestureAttempted(NavBarGestureResult result, PointF finalVelocity) {
+ switch (result) {
+ case ASSISTANT_COMPLETED:
+ showRippleEffect(null);
+ showFeedback(R.string.sandbox_mode_assistant_gesture_feedback_successful);
+ break;
+ case HOME_GESTURE_COMPLETED:
+ animateFakeTaskViewHome(finalVelocity, () -> {
+ showFeedback(R.string.sandbox_mode_home_gesture_feedback_successful);
+ });
+ break;
+ case OVERVIEW_GESTURE_COMPLETED:
+ fadeOutFakeTaskView(true, () -> {
+ showFeedback(R.string.sandbox_mode_overview_gesture_feedback_successful);
+ });
+ break;
+ case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
+ case HOME_OR_OVERVIEW_CANCELLED:
+ case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ case OVERVIEW_NOT_STARTED_TOO_FAR_FROM_EDGE:
+ showFeedback(R.string.home_gesture_feedback_swipe_too_far_from_edge);
+ break;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
new file mode 100644
index 0000000..955a2f7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/SandboxModeTutorialFragment.java
@@ -0,0 +1,43 @@
+/*
+ * 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.interaction;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.quickstep.interaction.TutorialController.TutorialType;
+
+/** Shows the general navigation gesture sandbox environment. */
+public class SandboxModeTutorialFragment extends TutorialFragment {
+
+ @Override
+ TutorialController createController(TutorialType type) {
+ return new SandboxModeTutorialController(this, type);
+ }
+
+ @Override
+ Class<? extends TutorialController> getControllerClass() {
+ return SandboxModeTutorialController.class;
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ if (motionEvent.getAction() == MotionEvent.ACTION_DOWN && mTutorialController != null) {
+ mTutorialController.setRippleHotspot(motionEvent.getX(), motionEvent.getY());
+ }
+ return super.onTouch(view, motionEvent);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index a157314..68c63bf 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -16,9 +16,9 @@
package com.android.quickstep.interaction;
import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
@@ -61,16 +61,21 @@
@TargetApi(Build.VERSION_CODES.R)
abstract class SwipeUpGestureTutorialController extends TutorialController {
- private final ViewSwipeUpAnimation mViewSwipeUpAnimation;
+
+ private static final int FAKE_PREVIOUS_TASK_MARGIN = Utilities.dpToPx(12);
+
+ private final ViewSwipeUpAnimation mTaskViewSwipeUpAnimation;
private float mFakeTaskViewRadius;
private Rect mFakeTaskViewRect = new Rect();
private RunningWindowAnim mRunningWindowAnim;
+ private boolean mShowTasks = false;
+ private boolean mShowPreviousTasks = false;
SwipeUpGestureTutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
super(tutorialFragment, tutorialType);
RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(mContext);
OverviewComponentObserver observer = new OverviewComponentObserver(mContext, deviceState);
- mViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
+ mTaskViewSwipeUpAnimation = new ViewSwipeUpAnimation(mContext, deviceState,
new GestureState(observer, -1));
observer.onDestroy();
deviceState.destroy();
@@ -83,16 +88,22 @@
.getWindowInsets()
.getInsets(WindowInsets.Type.systemBars());
dp.updateInsets(new Rect(insets.left, insets.top, insets.right, insets.bottom));
- mViewSwipeUpAnimation.initDp(dp);
+ mTaskViewSwipeUpAnimation.initDp(dp);
mFakeTaskViewRadius = QuickStepContract.getWindowCornerRadius(mContext.getResources());
- mFakeTaskView.setClipToOutline(true);
- mFakeTaskView.setOutlineProvider(new ViewOutlineProvider() {
+
+ ViewOutlineProvider outlineProvider = new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(mFakeTaskViewRect, mFakeTaskViewRadius);
}
- });
+ };
+
+ mFakeTaskView.setClipToOutline(true);
+ mFakeTaskView.setOutlineProvider(outlineProvider);
+
+ mFakePreviousTaskView.setClipToOutline(true);
+ mFakePreviousTaskView.setOutlineProvider(outlineProvider);
}
private void cancelRunningAnimation() {
@@ -114,16 +125,22 @@
mFakeIconView.setVisibility(View.INVISIBLE);
mFakeTaskView.setVisibility(View.INVISIBLE);
mFakeTaskView.setAlpha(1);
+ mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+ mFakePreviousTaskView.setAlpha(1);
+ mShowTasks = false;
+ mShowPreviousTasks = false;
mRunningWindowAnim = null;
}
};
if (toOverviewFirst) {
- anim.setFloat(mViewSwipeUpAnimation.getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
+ anim.setFloat(mTaskViewSwipeUpAnimation
+ .getCurrentShift(), AnimatedFloat.VALUE, 1, ACCEL);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation, boolean isReverse) {
PendingAnimation fadeAnim = new PendingAnimation(300);
fadeAnim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+ fadeAnim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
fadeAnim.addListener(resetTaskView);
AnimatorSet animset = fadeAnim.buildAnim();
animset.setStartDelay(100);
@@ -133,6 +150,7 @@
});
} else {
anim.setViewAlpha(mFakeTaskView, 0, ACCEL);
+ anim.setViewAlpha(mFakePreviousTaskView, 0, ACCEL);
anim.setViewAlpha(mFakeIconView, 0, ACCEL);
anim.addListener(resetTaskView);
}
@@ -148,8 +166,10 @@
hideFeedback();
hideHandCoachingAnimation();
cancelRunningAnimation();
+ mFakePreviousTaskView.setVisibility(View.INVISIBLE);
+ mShowPreviousTasks = false;
RectFSpringAnim rectAnim =
- mViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
+ mTaskViewSwipeUpAnimation.handleSwipeUpToHome(finalVelocity);
// After home animation finishes, fade out and run onEndRunnable.
rectAnim.addAnimatorListener(AnimationSuccessListener.forRunnable(
() -> fadeOutFakeTaskView(false, onEndRunnable)));
@@ -161,11 +181,31 @@
if (displacement == null || mTutorialType == HOME_NAVIGATION_COMPLETE
|| mTutorialType == OVERVIEW_NAVIGATION_COMPLETE) {
mFakeTaskView.setVisibility(View.INVISIBLE);
+ mFakePreviousTaskView.setVisibility(View.INVISIBLE);
} else {
+ mShowTasks = true;
mFakeTaskView.setVisibility(View.VISIBLE);
- if (mRunningWindowAnim == null) {
- mViewSwipeUpAnimation.updateDisplacement(displacement);
+ if (mShowPreviousTasks) {
+ mFakePreviousTaskView.setVisibility(View.VISIBLE);
}
+ if (mRunningWindowAnim == null) {
+ mTaskViewSwipeUpAnimation.updateDisplacement(displacement);
+ }
+ }
+ }
+
+ @Override
+ public void onMotionPaused(boolean unused) {
+ if (mShowTasks) {
+ if (!mShowPreviousTasks) {
+ mFakePreviousTaskView.setTranslationX(
+ -(2 * mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN));
+ mFakePreviousTaskView.animate()
+ .setDuration(300)
+ .translationX(-(mFakePreviousTaskView.getWidth() + FAKE_PREVIOUS_TASK_MARGIN))
+ .start();
+ }
+ mShowPreviousTasks = true;
}
}
@@ -232,6 +272,7 @@
false /* isVerticalBarLayout */);
mFakeIconView.setAlpha(1);
mFakeTaskView.setAlpha(getWindowAlpha(progress));
+ mFakePreviousTaskView.setAlpha(getWindowAlpha(progress));
}
@Override
@@ -258,9 +299,11 @@
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();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 73f1f8c..12d2efc 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -25,8 +25,10 @@
import android.widget.TextView;
import androidx.annotation.CallSuper;
+import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.views.ClipIconView;
import com.android.quickstep.interaction.EdgeBackGestureHandler.BackGestureAttemptCallback;
@@ -47,11 +49,13 @@
final TextView mTitleTextView;
final TextView mSubtitleTextView;
final TextView mFeedbackView;
+ final View mLauncherView;
final ClipIconView mFakeIconView;
final View mFakeTaskView;
+ final View mFakePreviousTaskView;
final View mRippleView;
final RippleDrawable mRippleDrawable;
- final TutorialHandAnimation mHandCoachingAnimation;
+ @Nullable final TutorialHandAnimation mHandCoachingAnimation;
final ImageView mHandCoachingView;
final Button mActionTextButton;
final Button mActionButton;
@@ -62,14 +66,17 @@
mTutorialType = tutorialType;
mContext = mTutorialFragment.getContext();
- View rootView = tutorialFragment.getRootView();
+ RootSandboxLayout rootView = tutorialFragment.getRootView();
mCloseButton = rootView.findViewById(R.id.gesture_tutorial_fragment_close_button);
mCloseButton.setOnClickListener(button -> mTutorialFragment.closeTutorial());
mTitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_title_view);
mSubtitleTextView = rootView.findViewById(R.id.gesture_tutorial_fragment_subtitle_view);
mFeedbackView = rootView.findViewById(R.id.gesture_tutorial_fragment_feedback_view);
+ mLauncherView = getMockLauncherView();
mFakeIconView = rootView.findViewById(R.id.gesture_tutorial_fake_icon_view);
mFakeTaskView = rootView.findViewById(R.id.gesture_tutorial_fake_task_view);
+ mFakePreviousTaskView =
+ rootView.findViewById(R.id.gesture_tutorial_fake_previous_task_view);
mRippleView = rootView.findViewById(R.id.gesture_tutorial_ripple_view);
mRippleDrawable = (RippleDrawable) mRippleView.getBackground();
mHandCoachingAnimation = tutorialFragment.getHandAnimation();
@@ -82,6 +89,17 @@
mHideFeedbackRunnable =
() -> mFeedbackView.animate().alpha(0).setDuration(FEEDBACK_ANIMATION_MS)
.withEndAction(this::showHandCoachingAnimation).start();
+
+ if (mLauncherView != null) {
+ rootView.addView(mLauncherView, 0);
+ }
+ if (mContext != null) {
+ rootView.setBackground(mContext.getDrawable(getMockWallpaperResId()));
+ mFakeTaskView.setBackground(mContext.getDrawable(getMockAppTaskThumbnailResId()));
+ mFakePreviousTaskView.setBackground(
+ mContext.getDrawable(getMockPreviousAppTaskThumbnailResId()));
+ mFakeIconView.setBackground(mContext.getDrawable(getMockAppIconResId()));
+ }
}
void setTutorialType(TutorialType tutorialType) {
@@ -108,6 +126,33 @@
return null;
}
+ @DrawableRes
+ protected int getMockAppTaskThumbnailResId() {
+ return R.drawable.default_sandbox_app_task_thumbnail;
+ }
+
+ @DrawableRes
+ protected int getMockPreviousAppTaskThumbnailResId() {
+ return R.drawable.default_sandbox_app_previous_task_thumbnail;
+ }
+
+ @Nullable
+ public View getMockLauncherView() {
+ InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext);
+
+ return new SandboxLauncherRenderer(mContext, dp, true).getRenderedView();
+ }
+
+ @DrawableRes
+ public int getMockAppIconResId() {
+ return R.drawable.default_sandbox_app_icon;
+ }
+
+ @DrawableRes
+ public int getMockWallpaperResId() {
+ return R.drawable.default_sandbox_wallpaper;
+ }
+
void showFeedback(int resId) {
hideHandCoachingAnimation();
mFeedbackView.setText(resId);
@@ -143,13 +188,16 @@
void onActionTextButtonClicked(View button) {}
void showHandCoachingAnimation() {
- if (isComplete()) {
+ if (isComplete() || mHandCoachingAnimation == null) {
return;
}
mHandCoachingAnimation.startLoopedAnimation(mTutorialType);
}
void hideHandCoachingAnimation() {
+ if (mHandCoachingAnimation == null) {
+ return;
+ }
mHandCoachingAnimation.stop();
mHandCoachingView.setVisibility(View.INVISIBLE);
}
@@ -165,6 +213,9 @@
} else {
showHandCoachingAnimation();
}
+ if (mLauncherView != null) {
+ mLauncherView.setVisibility(isComplete() ? View.INVISIBLE : View.VISIBLE);
+ }
}
private void updateTitles() {
@@ -219,6 +270,7 @@
OVERVIEW_NAVIGATION,
OVERVIEW_NAVIGATION_COMPLETE,
ASSISTANT,
- ASSISTANT_COMPLETE
+ ASSISTANT_COMPLETE,
+ SANDBOX_MODE
}
}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
index 9a8264d..413387e 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialFragment.java
@@ -41,8 +41,8 @@
TutorialType mTutorialType;
@Nullable TutorialController mTutorialController = null;
- View mRootView;
- TutorialHandAnimation mHandCoachingAnimation;
+ RootSandboxLayout mRootView;
+ @Nullable TutorialHandAnimation mHandCoachingAnimation = null;
EdgeBackGestureHandler mEdgeBackGestureHandler;
NavBarGestureHandler mNavBarGestureHandler;
@@ -52,6 +52,7 @@
fragment = new BackGestureTutorialFragment();
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
}
+
Bundle args = new Bundle();
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
fragment.setArguments(args);
@@ -74,13 +75,17 @@
case ASSISTANT:
case ASSISTANT_COMPLETE:
return new AssistantGestureTutorialFragment();
+ case SANDBOX_MODE:
+ return new SandboxModeTutorialFragment();
default:
Log.e(LOG_TAG, "Failed to find an appropriate fragment for " + tutorialType.name());
}
return null;
}
- abstract int getHandAnimationResId();
+ @Nullable Integer getHandAnimationResId() {
+ return null;
+ }
abstract TutorialController createController(TutorialType type);
@@ -107,15 +112,19 @@
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- mRootView = inflater.inflate(R.layout.gesture_tutorial_fragment, container, false);
+ mRootView = (RootSandboxLayout) inflater.inflate(
+ R.layout.gesture_tutorial_fragment, container, false);
mRootView.setOnApplyWindowInsetsListener((view, insets) -> {
Insets systemInsets = insets.getInsets(WindowInsets.Type.systemBars());
mEdgeBackGestureHandler.setInsets(systemInsets.left, systemInsets.right);
return insets;
});
mRootView.setOnTouchListener(this);
- mHandCoachingAnimation = new TutorialHandAnimation(getContext(), mRootView,
- getHandAnimationResId());
+ Integer handAnimationResId = getHandAnimationResId();
+ if (handAnimationResId != null) {
+ mHandCoachingAnimation =
+ new TutorialHandAnimation(getContext(), mRootView, handAnimationResId);
+ }
return mRootView;
}
@@ -128,18 +137,27 @@
@Override
public void onPause() {
super.onPause();
- mHandCoachingAnimation.stop();
+
+ if (mHandCoachingAnimation != null) {
+ mHandCoachingAnimation.stop();
+ }
}
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
- // Note: Using logical or to ensure both functions get called.
+ // Note: Using logical-or to ensure both functions get called.
return mEdgeBackGestureHandler.onTouch(view, motionEvent)
| mNavBarGestureHandler.onTouch(view, motionEvent);
}
+ boolean onInterceptTouch(MotionEvent motionEvent) {
+ // Note: Using logical-or to ensure both functions get called.
+ return mEdgeBackGestureHandler.onInterceptTouch(motionEvent)
+ | mNavBarGestureHandler.onInterceptTouch(motionEvent);
+ }
+
void onAttachedToWindow() {
- mEdgeBackGestureHandler.setViewGroupParent((ViewGroup) getRootView());
+ mEdgeBackGestureHandler.setViewGroupParent(getRootView());
}
void onDetachedFromWindow() {
@@ -164,14 +182,28 @@
super.onSaveInstanceState(savedInstanceState);
}
- View getRootView() {
+ RootSandboxLayout getRootView() {
return mRootView;
}
- TutorialHandAnimation getHandAnimation() {
+ @Nullable TutorialHandAnimation getHandAnimation() {
return mHandCoachingAnimation;
}
+ void continueTutorial() {
+ if (!(getContext() instanceof GestureSandboxActivity)) {
+ closeTutorial();
+ return;
+ }
+ GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
+
+ if (gestureSandboxActivity == null) {
+ closeTutorial();
+ return;
+ }
+ gestureSandboxActivity.continueTutorial();
+ }
+
void closeTutorial() {
FragmentActivity activity = getActivity();
if (activity != null) {
@@ -182,4 +214,13 @@
void startSystemNavigationSetting() {
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
}
+
+ boolean isTutorialComplete() {
+ if (!(getContext() instanceof GestureSandboxActivity)) {
+ return true;
+ }
+ GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
+
+ return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
new file mode 100644
index 0000000..f336bf5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -0,0 +1,181 @@
+/*
+ * 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.logging;
+
+import static com.android.launcher3.InvariantDeviceProfile.KEY_MIGRATION_SRC_HOTSEAT_COUNT;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_5;
+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;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DOT_ENABLED;
+import static com.android.launcher3.model.QuickstepModelDelegate.LAST_PREDICTION_ENABLED_STATE;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.TypedArray;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.R;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.util.SettingsCache;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Utility class to log launcher settings changes
+ */
+public class SettingsChangeLogger implements
+ NavigationModeChangeListener, OnSharedPreferenceChangeListener {
+
+ private static final String TAG = "SettingsChangeLogger";
+ private static final String ROOT_TAG = "androidx.preference.PreferenceScreen";
+ private static final String BOOLEAN_PREF = "SwitchPreference";
+
+ private final Context mContext;
+ private final ArrayMap<String, LoggablePref> mLoggablePrefs;
+
+ private Mode mNavMode;
+ private boolean mNotificationDotsEnabled;
+
+ public SettingsChangeLogger(Context context) {
+ mContext = context;
+ mLoggablePrefs = loadPrefKeys(context);
+ mNavMode = SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+
+ getPrefs(context).registerOnSharedPreferenceChangeListener(this);
+ getDevicePrefs(context).registerOnSharedPreferenceChangeListener(this);
+
+ SettingsCache mSettingsCache = SettingsCache.INSTANCE.get(context);
+ mSettingsCache.register(NOTIFICATION_BADGING_URI,
+ this::onNotificationDotsChanged);
+ mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI);
+ }
+
+ private static ArrayMap<String, LoggablePref> loadPrefKeys(Context context) {
+ XmlPullParser parser = context.getResources().getXml(R.xml.launcher_preferences);
+ ArrayMap<String, LoggablePref> result = new ArrayMap<>();
+
+ try {
+ AutoInstallsLayout.beginDocument(parser, ROOT_TAG);
+ final int depth = parser.getDepth();
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG
+ || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ if (BOOLEAN_PREF.equals(parser.getName())) {
+ TypedArray a = context.obtainStyledAttributes(
+ Xml.asAttributeSet(parser), R.styleable.LoggablePref);
+ String key = a.getString(R.styleable.LoggablePref_android_key);
+ LoggablePref pref = new LoggablePref();
+ pref.defaultValue =
+ a.getBoolean(R.styleable.LoggablePref_android_defaultValue, true);
+ pref.eventIdOn = a.getInt(R.styleable.LoggablePref_logIdOn, 0);
+ pref.eventIdOff = a.getInt(R.styleable.LoggablePref_logIdOff, 0);
+ if (pref.eventIdOff > 0 && pref.eventIdOn > 0) {
+ result.put(key, pref);
+ }
+ }
+ }
+ } catch (XmlPullParserException | IOException e) {
+ Log.e(TAG, "Error parsing preference xml", e);
+ }
+ return result;
+ }
+
+ private void onNotificationDotsChanged(boolean isDotsEnabled) {
+ mNotificationDotsEnabled = isDotsEnabled;
+ dispatchUserEvent();
+ }
+
+ @Override
+ public void onNavigationModeChanged(Mode newMode) {
+ mNavMode = newMode;
+ dispatchUserEvent();
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (LAST_PREDICTION_ENABLED_STATE.equals(key) || KEY_MIGRATION_SRC_HOTSEAT_COUNT.equals(key)
+ || mLoggablePrefs.containsKey(key)) {
+ dispatchUserEvent();
+ }
+ }
+
+ private void dispatchUserEvent() {
+ StatsLogger logger = StatsLogManager.newInstance(mContext).logger()
+ .withInstanceId(new InstanceIdSequence().newInstanceId());
+
+ logger.log(mNotificationDotsEnabled
+ ? LAUNCHER_NOTIFICATION_DOT_ENABLED
+ : LAUNCHER_NOTIFICATION_DOT_DISABLED);
+ logger.log(mNavMode.launcherEvent);
+ logger.log(getDevicePrefs(mContext).getBoolean(LAST_PREDICTION_ENABLED_STATE, true)
+ ? LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED
+ : LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED);
+
+ SharedPreferences prefs = getPrefs(mContext);
+ StatsLogManager.LauncherEvent gridSizeChangedEvent = null;
+ switch (prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, -1)) {
+ case 5:
+ gridSizeChangedEvent = LAUNCHER_GRID_SIZE_5;
+ break;
+ case 4:
+ gridSizeChangedEvent = LAUNCHER_GRID_SIZE_4;
+ break;
+ case 3:
+ gridSizeChangedEvent = LAUNCHER_GRID_SIZE_3;
+ break;
+ case 2:
+ gridSizeChangedEvent = LAUNCHER_GRID_SIZE_2;
+ break;
+ default:
+ // Ignore illegal input.
+ break;
+ }
+ if (gridSizeChangedEvent != null) {
+ logger.log(gridSizeChangedEvent);
+ }
+ mLoggablePrefs.forEach((key, lp) -> logger.log(() ->
+ prefs.getBoolean(key, lp.defaultValue) ? lp.eventIdOn : lp.eventIdOff));
+ }
+
+ private static class LoggablePref {
+ public boolean defaultValue;
+ public int eventIdOn;
+ public int eventIdOff;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index 256ad82..66c24c8 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,24 +16,26 @@
package com.android.quickstep.logging;
-import static android.text.format.DateUtils.DAY_IN_MILLIS;
-import static android.text.format.DateUtils.formatElapsedTime;
+import static androidx.core.util.Preconditions.checkNotNull;
+import static androidx.core.util.Preconditions.checkState;
-import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
+import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__ALLAPPS;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME;
import static com.android.systemui.shared.system.SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__OVERVIEW;
-import static java.lang.System.currentTimeMillis;
-
import android.content.Context;
import android.util.Log;
+import android.view.View;
-import androidx.annotation.Nullable;
+import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
+import androidx.slice.SliceItem;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
@@ -43,25 +45,23 @@
import com.android.launcher3.logger.LauncherAtom.FolderIcon;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
+import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.logging.InstanceId;
-import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
-import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.LogConfig;
+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;
/**
* This class calls StatsLog compile time generated methods.
@@ -77,14 +77,16 @@
private static final String TAG = "StatsLog";
private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.STATSLOG);
-
- private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure.
private static final int DEFAULT_PAGE_INDEX = -2;
private static final int FOLDER_HIERARCHY_OFFSET = 100;
private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
+ private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
+
+ public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
+ new CopyOnWriteArrayList<>();
private final Context mContext;
@@ -93,89 +95,15 @@
}
@Override
- public StatsLogger logger() {
- return new StatsCompatLogger();
+ protected StatsLogger createLogger() {
+ return new StatsCompatLogger(mContext);
}
/**
- * Logs a ranking event and accompanying {@link InstanceId} and package name.
+ * Synchronously writes an itemInfo to stats log
*/
- @Override
- public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
- int position) {
- SysUiStatsLog.write(SysUiStatsLog.RANKING_SELECTED,
- rankingEvent.getId() /* event_id = 1; */,
- packageName /* package_name = 2; */,
- instanceId.getId() /* instance_id = 3; */,
- position /* position_picked = 4; */);
- }
-
- /**
- * Logs impression of the current workspace with additional launcher events.
- */
- @Override
- public void logSnapshot(List<EventEnum> extraEvents) {
- LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
- new SnapshotWorker(extraEvents));
- }
-
- private class SnapshotWorker extends BaseModelUpdateTask {
- private final InstanceId mInstanceId;
- private final List<EventEnum> mExtraEvents;
-
- SnapshotWorker(List<EventEnum> extraEvents) {
- mInstanceId = new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
- .newInstanceId();
- this.mExtraEvents = extraEvents;
- }
-
- @Override
- public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- long lastSnapshotTimeMillis = getDevicePrefs(mContext)
- .getLong(LAST_SNAPSHOT_TIME_MILLIS, 0);
- // Log snapshot only if previous snapshot was older than a day
- if (currentTimeMillis() - lastSnapshotTimeMillis < DAY_IN_MILLIS) {
- if (IS_VERBOSE) {
- String elapsedTime = formatElapsedTime(
- (currentTimeMillis() - lastSnapshotTimeMillis) / 1000);
- Log.d(TAG, String.format(
- "Skipped snapshot logging since previous snapshot was %s old.",
- elapsedTime));
- }
- return;
- }
-
- IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
- ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
- ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
- for (ItemInfo info : workspaceItems) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- writeSnapshot(atomInfo, mInstanceId);
- }
- for (FolderInfo fInfo : folders) {
- try {
- ArrayList<WorkspaceItemInfo> folderContents =
- (ArrayList) Executors.MAIN_EXECUTOR.submit(fInfo.contents::clone).get();
- for (ItemInfo info : folderContents) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(fInfo);
- writeSnapshot(atomInfo, mInstanceId);
- }
- } catch (Exception e) {
- }
- }
- for (ItemInfo info : appWidgets) {
- LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
- writeSnapshot(atomInfo, mInstanceId);
- }
- mExtraEvents
- .forEach(eventName -> logger().withInstanceId(mInstanceId).log(eventName));
-
- getDevicePrefs(mContext).edit()
- .putLong(LAST_SNAPSHOT_TIME_MILLIS, currentTimeMillis()).apply();
- }
- }
-
- private void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
+ @WorkerThread
+ public static void writeSnapshot(LauncherAtom.ItemInfo info, InstanceId instanceId) {
if (IS_VERBOSE) {
Log.d(TAG, String.format("\nwriteSnapshot(%d):\n%s", instanceId.getId(), info));
}
@@ -209,6 +137,8 @@
private static class StatsCompatLogger implements StatsLogger {
private static final ItemInfo DEFAULT_ITEM_INFO = new ItemInfo();
+
+ private Context mContext;
private ItemInfo mItemInfo = DEFAULT_ITEM_INFO;
private InstanceId mInstanceId = DEFAULT_INSTANCE_ID;
private OptionalInt mRank = OptionalInt.empty();
@@ -218,6 +148,11 @@
private Optional<FromState> mFromState = Optional.empty();
private Optional<ToState> mToState = Optional.empty();
private Optional<String> mEditText = Optional.empty();
+ private SliceItem mSliceItem;
+
+ StatsCompatLogger(Context context) {
+ mContext = context;
+ }
@Override
public StatsLogger withItemInfo(ItemInfo itemInfo) {
@@ -255,10 +190,8 @@
@Override
public StatsLogger withContainerInfo(ContainerInfo containerInfo) {
- if (mItemInfo != DEFAULT_ITEM_INFO) {
- throw new IllegalArgumentException(
+ checkState(mItemInfo == DEFAULT_ITEM_INFO,
"ItemInfo and ContainerInfo are mutual exclusive; cannot log both.");
- }
this.mContainerInfo = Optional.of(containerInfo);
return this;
}
@@ -282,41 +215,81 @@
}
@Override
+ public StatsLogger withSliceItem(@NonNull SliceItem sliceItem) {
+ this.mSliceItem = checkNotNull(sliceItem, "expected valid sliceItem but received null");
+ checkState(mItemInfo == DEFAULT_ITEM_INFO,
+ "ItemInfo and SliceItem are mutual exclusive; cannot log both.");
+ return this;
+ }
+
+ @Override
public void log(EventEnum event) {
if (!Utilities.ATLEAST_R) {
return;
}
+ LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
- if (mItemInfo.container < 0) {
- // Item is not within a folder. Write to StatsLog in same thread.
- write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState,
- mDstState);
+ if (mSliceItem != null) {
+ Executors.MODEL_EXECUTOR.execute(
+ () -> {
+ LauncherAtom.ItemInfo.Builder itemInfoBuilder =
+ LauncherAtom.ItemInfo.newBuilder().setSlice(
+ LauncherAtom.Slice.newBuilder().setUri(
+ mSliceItem.getSlice().getUri().toString()));
+ mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
+ write(event, applyOverwrites(itemInfoBuilder.build()));
+ });
+ 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)
+ Executors.MODEL_EXECUTOR.execute(
+ () -> write(event, applyOverwrites(mItemInfo.buildProto())));
} else {
// Item is inside the folder, fetch folder info in a BG thread
// and then write to StatsLog.
- LauncherAppState.getInstanceNoCreate().getModel().enqueueModelUpdateTask(
+ appState.getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel,
AllAppsList apps) {
FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
- write(event, mInstanceId,
- applyOverwrites(mItemInfo.buildProto(folderInfo)),
- mSrcState, mDstState);
+ write(event, applyOverwrites(mItemInfo.buildProto(folderInfo)));
}
});
}
}
+ @Override
+ public void sendToInteractionJankMonitor(EventEnum event, View view) {
+ if (!(event instanceof LauncherEvent)) {
+ return;
+ }
+ switch ((LauncherEvent) event) {
+ case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN:
+ InteractionJankMonitorWrapper.begin(
+ view,
+ InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+ break;
+ case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
+ InteractionJankMonitorWrapper.end(
+ InteractionJankMonitorWrapper.CUJ_ALL_APPS_SCROLL);
+ break;
+ default:
+ break;
+ }
+ }
+
private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
- LauncherAtom.ItemInfo.Builder itemInfoBuilder =
- (LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
+ LauncherAtom.ItemInfo.Builder itemInfoBuilder = atomInfo.toBuilder();
mRank.ifPresent(itemInfoBuilder::setRank);
mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
- FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder
+ FolderIcon.Builder folderIconBuilder = itemInfoBuilder
.getFolderIcon()
.toBuilder();
mFromState.ifPresent(folderIconBuilder::setFromLabelState);
@@ -327,8 +300,11 @@
return itemInfoBuilder.build();
}
- private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
- int srcState, int dstState) {
+ @WorkerThread
+ private void write(EventEnum event, LauncherAtom.ItemInfo atomInfo) {
+ InstanceId instanceId = mInstanceId;
+ int srcState = mSrcState;
+ int dstState = mDstState;
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
@@ -341,6 +317,10 @@
atomInfo));
}
+ for (StatsLogConsumer consumer : LOGS_CONSUMER) {
+ consumer.consume(event, atomInfo);
+ }
+
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
@@ -376,6 +356,14 @@
return info.getContainerInfo().getPredictedHotseatContainer().getCardinality();
case SEARCH_RESULT_CONTAINER:
return info.getContainerInfo().getSearchResultContainer().getQueryLength();
+ case EXTENDED_CONTAINERS:
+ ExtendedContainers extendedCont = info.getContainerInfo().getExtendedContainers();
+ if (extendedCont.getContainerCase() == DEVICE_SEARCH_RESULT_CONTAINER) {
+ DeviceSearchResultContainer deviceSearchResultCont = extendedCont
+ .getDeviceSearchResultContainer();
+ return deviceSearchResultCont.hasQueryLength() ? deviceSearchResultCont
+ .getQueryLength() : -1;
+ }
default:
return info.getFolderIcon().getCardinality();
}
@@ -391,6 +379,8 @@
return info.getWidget().getPackageName();
case TASK:
return info.getTask().getPackageName();
+ case SEARCH_ACTION_ITEM:
+ return info.getSearchActionItem().getPackageName();
default:
return null;
}
@@ -406,6 +396,10 @@
return info.getWidget().getComponentName();
case TASK:
return info.getTask().getComponentName();
+ case SEARCH_ACTION_ITEM:
+ return info.getSearchActionItem().getTitle();
+ case SLICE:
+ return info.getSlice().getUri();
default:
return null;
}
@@ -474,6 +468,9 @@
} else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
.getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
+ } else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
+ return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
+ + EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
} else {
return info.getContainerInfo().getContainerCase().getNumber();
}
@@ -491,7 +488,16 @@
return "ALLAPPS";
default:
return "INVALID";
-
}
}
+
+
+ /**
+ * Interface to get stats log while it is dispatched to the system
+ */
+ public interface StatsLogConsumer {
+
+ @WorkerThread
+ void consume(EventEnum event, LauncherAtom.ItemInfo atomInfo);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
deleted file mode 100644
index 9ca7f23..0000000
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ /dev/null
@@ -1,86 +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.quickstep.logging;
-
-import android.content.Context;
-import android.util.Log;
-
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
-import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
-import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
-import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
-import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
-
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.systemui.shared.system.MetricsLoggerCompat;
-
-/**
- * This class handles AOSP MetricsLogger function calls and logging around
- * quickstep interactions.
- */
-@SuppressWarnings("unused")
-public class UserEventDispatcherExtension extends UserEventDispatcher {
-
- public static final int ALL_APPS_PREDICTION_TIPS = 2;
-
- private static final String TAG = "UserEventDispatcher";
-
- public UserEventDispatcherExtension(Context context) { }
-
- public void logStateChangeAction(int action, int dir, int downX, int downY,
- int srcChildTargetType, int srcParentContainerType,
- int dstContainerType, int pageIndex) {
- new MetricsLoggerCompat().visibility(MetricsLoggerCompat.OVERVIEW_ACTIVITY,
- dstContainerType == LauncherLogProto.ContainerType.TASKSWITCHER);
- super.logStateChangeAction(action, dir, downX, downY, srcChildTargetType,
- srcParentContainerType, dstContainerType, pageIndex);
- }
-
- public void logActionTip(int actionType, int viewType) {
- LauncherLogProto.Action action = new LauncherLogProto.Action();
- LauncherLogProto.Target target = new LauncherLogProto.Target();
- switch(actionType) {
- case VISIBLE:
- action.type = LauncherLogProto.Action.Type.TIP;
- target.type = LauncherLogProto.Target.Type.CONTAINER;
- target.containerType = LauncherLogProto.ContainerType.TIP;
- break;
- case DISMISS:
- action.type = LauncherLogProto.Action.Type.TOUCH;
- action.touch = LauncherLogProto.Action.Touch.TAP;
- target.type = LauncherLogProto.Target.Type.CONTROL;
- target.controlType = CANCEL_TARGET;
- break;
- default:
- Log.e(TAG, "Unexpected action type = " + actionType);
- }
-
- switch(viewType) {
- case RECENTS_QUICK_SCRUB_ONBOARDING_TIP:
- target.tipType = LauncherLogProto.TipType.QUICK_SCRUB_TEXT;
- break;
- case RECENTS_SWIPE_UP_ONBOARDING_TIP:
- target.tipType = LauncherLogProto.TipType.SWIPE_UP_TEXT;
- break;
- default:
- Log.e(TAG, "Unexpected viewType = " + viewType);
- }
- LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
- dispatchUserEvent(event, null);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java b/quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/ActiveGestureLog.java
rename to quickstep/src/com/android/quickstep/util/ActiveGestureLog.java
diff --git a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
index deb70e0..7f94839 100644
--- a/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
+++ b/quickstep/src/com/android/quickstep/util/AnimatorControllerWithResistance.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -38,7 +37,6 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.views.RecentsView;
/**
@@ -49,12 +47,6 @@
*/
public class AnimatorControllerWithResistance {
- /**
- * How much farther we can drag past overview in 2-button mode, as a factor of the distance
- * it takes to drag from an app to overview.
- */
- public static final float TWO_BUTTON_EXTRA_DRAG_FACTOR = 0.25f;
-
private enum RecentsResistanceParams {
FROM_APP(0.75f, 0.5f, 1f),
FROM_OVERVIEW(1f, 0.75f, 0.5f);
@@ -161,12 +153,6 @@
LauncherActivityInterface.INSTANCE.calculateTaskSize(params.context, params.dp, startRect,
orientationHandler);
long distanceToCover = startRect.bottom;
- boolean isTwoButtonMode = SysUINavigationMode.getMode(params.context) == TWO_BUTTONS;
- if (isTwoButtonMode) {
- // We can only drag a small distance past overview, not to the top of the screen.
- distanceToCover = (long)
- ((params.dp.heightPx - startRect.bottom) * TWO_BUTTON_EXTRA_DRAG_FACTOR);
- }
PendingAnimation resistAnim = params.resistAnim != null
? params.resistAnim
: new PendingAnimation(distanceToCover * 2);
@@ -178,43 +164,35 @@
/ (params.dp.heightPx - startRect.bottom);
// This is what the scale would be at the end of the drag if we didn't apply resistance.
float endScale = params.startScale - prevScaleRate * distanceToCover;
- final TimeInterpolator scaleInterpolator;
- if (isTwoButtonMode) {
- // We are bounded by the distance of the drag, so we don't need to apply resistance.
- scaleInterpolator = LINEAR;
- } else {
- // Create an interpolator that resists the scale so the scale doesn't get smaller than
- // RECENTS_SCALE_MAX_RESIST.
- float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist,
- params.startScale, endScale);
- float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist,
- params.startScale, endScale);
- scaleInterpolator = t -> {
- if (t < startResist) {
- return t;
- }
- float resistProgress = Utilities.getProgress(t, startResist, 1);
- resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress);
- return startResist + resistProgress * (maxResist - startResist);
- };
- }
+ // Create an interpolator that resists the scale so the scale doesn't get smaller than
+ // RECENTS_SCALE_MAX_RESIST.
+ float startResist = Utilities.getProgress(params.resistanceParams.scaleStartResist,
+ params.startScale, endScale);
+ float maxResist = Utilities.getProgress(params.resistanceParams.scaleMaxResist,
+ params.startScale, endScale);
+ final TimeInterpolator scaleInterpolator = t -> {
+ if (t < startResist) {
+ return t;
+ }
+ float resistProgress = Utilities.getProgress(t, startResist, 1);
+ resistProgress = RECENTS_SCALE_RESIST_INTERPOLATOR.getInterpolation(resistProgress);
+ return startResist + resistProgress * (maxResist - startResist);
+ };
resistAnim.addFloat(params.scaleTarget, params.scaleProperty, params.startScale, endScale,
scaleInterpolator);
- if (!isTwoButtonMode) {
- // Compute where the task view would be based on the end scale, if we didn't translate.
- RectF endRectF = new RectF(startRect);
- Matrix temp = new Matrix();
- temp.setScale(params.resistanceParams.scaleMaxResist,
- params.resistanceParams.scaleMaxResist, pivot.x, pivot.y);
- temp.mapRect(endRectF);
- // Translate such that the task view touches the top of the screen when drag does.
- float endTranslation = endRectF.top
- * orientationHandler.getSecondaryTranslationDirectionFactor()
- * params.resistanceParams.translationFactor;
- resistAnim.addFloat(params.translationTarget, params.translationProperty,
- params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
- }
+ // Compute where the task view would be based on the end scale.
+ RectF endRectF = new RectF(startRect);
+ Matrix temp = new Matrix();
+ temp.setScale(params.resistanceParams.scaleMaxResist,
+ params.resistanceParams.scaleMaxResist, pivot.x, pivot.y);
+ temp.mapRect(endRectF);
+ // Translate such that the task view touches the top of the screen when drag does.
+ float endTranslation = endRectF.top
+ * orientationHandler.getSecondaryTranslationDirectionFactor()
+ * params.resistanceParams.translationFactor;
+ resistAnim.addFloat(params.translationTarget, params.translationProperty,
+ params.startTranslation, endTranslation, RECENTS_TRANSLATE_RESIST_INTERPOLATOR);
return resistAnim;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java b/quickstep/src/com/android/quickstep/util/AssistantUtilities.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/AssistantUtilities.java
rename to quickstep/src/com/android/quickstep/util/AssistantUtilities.java
diff --git a/quickstep/src/com/android/quickstep/util/CancellableTask.java b/quickstep/src/com/android/quickstep/util/CancellableTask.java
new file mode 100644
index 0000000..a6e2e81
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/CancellableTask.java
@@ -0,0 +1,68 @@
+/*
+ * 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 com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+
+/**
+ * Utility class to executore a task on background and post the result on UI thread
+ */
+public abstract class CancellableTask<T> implements Runnable {
+
+ private boolean mCancelled = false;
+
+ @Override
+ public final void run() {
+ if (mCancelled) {
+ return;
+ }
+ T result = getResultOnBg();
+ if (mCancelled) {
+ return;
+ }
+ MAIN_EXECUTOR.execute(() -> {
+ if (mCancelled) {
+ return;
+ }
+ handleResult(result);
+ });
+ }
+
+ /**
+ * Called on the worker thread to process the request. The return object is passed to
+ * {@link #handleResult(Object)}
+ */
+ @WorkerThread
+ public abstract T getResultOnBg();
+
+ /**
+ * Called on the UI thread to handle the final result.
+ * @param result
+ */
+ @UiThread
+ public abstract void handleResult(T result);
+
+ /**
+ * Cancels the request. If it is called before {@link #handleResult(Object)}, that method
+ * will not be called
+ */
+ public void cancel() {
+ mCancelled = true;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index e998e9a..d022085 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -22,15 +22,19 @@
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.app.prediction.AppTarget;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Picture;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.net.Uri;
import android.util.Log;
@@ -71,6 +75,34 @@
}
/**
+ * 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);
+ context.startActivity(intent);
+ }
+
+ /**
* Launch the activity to share image.
*/
@UiThread
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
rename to quickstep/src/com/android/quickstep/util/InputConsumerProxy.java
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index f7bd1e2..8834dc2 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -15,16 +15,12 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
-
import android.content.Context;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
@@ -45,16 +41,10 @@
public static int getShelfTrackingDistance(Context context, DeviceProfile dp,
PagedOrientationHandler orientationHandler) {
// Track the bottom of the window.
- if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
- Rect taskSize = new Rect();
- LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize,
- orientationHandler);
- return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
- }
- int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
- int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
- R.dimen.task_card_vert_space);
- return shelfHeight + spaceBetweenShelfAndRecents;
+ Rect taskSize = new Rect();
+ LauncherActivityInterface.INSTANCE.calculateTaskSize(
+ context, dp, taskSize, orientationHandler);
+ return orientationHandler.getDistanceToBottomOfRect(dp, taskSize);
}
/**
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index f60f7ad..8151d41 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -15,17 +15,14 @@
*/
package com.android.quickstep.util;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_LSQ_VELOCITY_PROVIDER;
-
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.compat.AccessibilityManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
/**
* Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
@@ -52,7 +49,7 @@
private final Alarm mForcePauseTimeout;
private final boolean mMakePauseHarderToTrigger;
private final Context mContext;
- private final VelocityProvider mVelocityProvider;
+ private final SystemVelocityProvider mVelocityProvider;
private Float mPreviousVelocity = null;
@@ -86,14 +83,10 @@
mSpeedSlow = res.getDimension(R.dimen.motion_pause_detector_speed_slow);
mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "creating alarm");
- }
mForcePauseTimeout = new Alarm();
mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
mMakePauseHarderToTrigger = makePauseHarderToTrigger;
- mVelocityProvider = ENABLE_LSQ_VELOCITY_PROVIDER.get()
- ? new LSqVelocityProvider(axis) : new LinearVelocityProvider(axis);
+ mVelocityProvider = new SystemVelocityProvider(axis);
}
/**
@@ -125,14 +118,11 @@
* @param pointerIndex Index for the pointer being tracked in the motion event
*/
public void addPosition(MotionEvent ev, int pointerIndex) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "setting alarm");
- }
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
? HARDER_TRIGGER_TIMEOUT
: FORCE_PAUSE_TIMEOUT);
- Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
- if (newVelocity != null && mPreviousVelocity != null) {
+ float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
+ if (mPreviousVelocity != null) {
checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
}
mPreviousVelocity = newVelocity;
@@ -175,20 +165,24 @@
}
private void updatePaused(boolean isPaused) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "updatePaused: " + isPaused);
- }
if (mDisallowPause) {
isPaused = false;
}
if (mIsPaused != isPaused) {
mIsPaused = isPaused;
+ boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
if (mIsPaused) {
AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
mHasEverBeenPaused = true;
}
if (mOnMotionPauseListener != null) {
- mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+ if (isFirstDetectedPause) {
+ mOnMotionPauseListener.onMotionPauseDetected();
+ }
+ // Null check again as onMotionPauseDetected() maybe have called clear().
+ if (mOnMotionPauseListener != null) {
+ mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
+ }
}
}
}
@@ -199,9 +193,6 @@
setOnMotionPauseListener(null);
mIsPaused = mHasEverBeenPaused = false;
mSlowStartTime = 0;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "canceling alarm");
- }
mForcePauseTimeout.cancelAlarm();
}
@@ -210,195 +201,39 @@
}
public interface OnMotionPauseListener {
- void onMotionPauseChanged(boolean isPaused);
+ /** Called only the first time motion pause is detected. */
+ void onMotionPauseDetected();
+ /** Called every time motion changes from paused to not paused and vice versa. */
+ default void onMotionPauseChanged(boolean isPaused) { }
}
- /**
- * Interface to abstract out velocity calculations
- */
- protected interface VelocityProvider {
+ private static class SystemVelocityProvider {
+
+ private final VelocityTracker mVelocityTracker;
+ private final int mAxis;
+
+ SystemVelocityProvider(int axis) {
+ mVelocityTracker = VelocityTracker.obtain();
+ mAxis = axis;
+ }
/**
* Adds a new motion events, and returns the velocity at this point, or null if
* the velocity is not available
*/
- Float addMotionEvent(MotionEvent ev, int pointer);
+ public float addMotionEvent(MotionEvent ev, int pointer) {
+ mVelocityTracker.addMovement(ev);
+ mVelocityTracker.computeCurrentVelocity(1); // px / ms
+ return mAxis == MotionEvent.AXIS_X
+ ? mVelocityTracker.getXVelocity(pointer)
+ : mVelocityTracker.getYVelocity(pointer);
+ }
/**
* Clears all stored motion event records
*/
- void clear();
- }
-
- private static class LinearVelocityProvider implements VelocityProvider {
-
- private Long mPreviousTime = null;
- private Float mPreviousPosition = null;
-
- private final int mAxis;
-
- LinearVelocityProvider(int axis) {
- mAxis = axis;
- }
-
- @Override
- public Float addMotionEvent(MotionEvent ev, int pointer) {
- long time = ev.getEventTime();
- float position = ev.getAxisValue(mAxis, pointer);
- Float velocity = null;
-
- if (mPreviousTime != null && mPreviousPosition != null) {
- long changeInTime = Math.max(1, time - mPreviousTime);
- float changeInPosition = position - mPreviousPosition;
- velocity = changeInPosition / changeInTime;
- }
- mPreviousTime = time;
- mPreviousPosition = position;
- return velocity;
- }
-
- @Override
public void clear() {
- mPreviousTime = null;
- mPreviousPosition = null;
- }
- }
-
- /**
- * Java implementation of {@link android.view.VelocityTracker} using the Least Square (deg 2)
- * algorithm.
- */
- private static class LSqVelocityProvider implements VelocityProvider {
-
- // Maximum age of a motion event to be considered when calculating the velocity.
- private static final long HORIZON_MS = 100;
- // Number of samples to keep.
- private static final int HISTORY_SIZE = 20;
-
- // Position history are stored in a circular array
- private final long[] mHistoricTimes = new long[HISTORY_SIZE];
- private final float[] mHistoricPos = new float[HISTORY_SIZE];
- private int mHistoryCount = 0;
- private int mHistoryStart = 0;
-
- private final int mAxis;
-
- LSqVelocityProvider(int axis) {
- mAxis = axis;
- }
-
- @Override
- public void clear() {
- mHistoryCount = mHistoryStart = 0;
- }
-
- private void addPositionAndTime(long eventTime, float eventPosition) {
- mHistoricTimes[mHistoryStart] = eventTime;
- mHistoricPos[mHistoryStart] = eventPosition;
- mHistoryStart++;
- if (mHistoryStart >= HISTORY_SIZE) {
- mHistoryStart = 0;
- }
- mHistoryCount = Math.min(HISTORY_SIZE, mHistoryCount + 1);
- }
-
- @Override
- public Float addMotionEvent(MotionEvent ev, int pointer) {
- // Add all historic points
- int historyCount = ev.getHistorySize();
- for (int i = 0; i < historyCount; i++) {
- addPositionAndTime(
- ev.getHistoricalEventTime(i), ev.getHistoricalAxisValue(mAxis, pointer, i));
- }
-
- // Start index for the last position (about to be added)
- int eventStartIndex = mHistoryStart;
- addPositionAndTime(ev.getEventTime(), ev.getAxisValue(mAxis, pointer));
- return solveUnweightedLeastSquaresDeg2(eventStartIndex);
- }
-
- /**
- * Solves the instantaneous velocity.
- * Based on solveUnweightedLeastSquaresDeg2 in VelocityTracker.cpp
- */
- private Float solveUnweightedLeastSquaresDeg2(final int pointPos) {
- final long eventTime = mHistoricTimes[pointPos];
-
- float sxi = 0, sxiyi = 0, syi = 0, sxi2 = 0, sxi3 = 0, sxi2yi = 0, sxi4 = 0;
- int count = 0;
- for (int i = 0; i < mHistoryCount; i++) {
- int index = pointPos - i;
- if (index < 0) {
- index += HISTORY_SIZE;
- }
-
- long time = mHistoricTimes[index];
- long age = eventTime - time;
- if (age > HORIZON_MS) {
- break;
- }
- count++;
- float xi = -age;
-
- float yi = mHistoricPos[index];
- float xi2 = xi * xi;
- float xi3 = xi2 * xi;
- float xi4 = xi3 * xi;
- float xiyi = xi * yi;
- float xi2yi = xi2 * yi;
-
- sxi += xi;
- sxi2 += xi2;
- sxiyi += xiyi;
- sxi2yi += xi2yi;
- syi += yi;
- sxi3 += xi3;
- sxi4 += xi4;
- }
-
- if (count < 3) {
- // Too few samples
- switch (count) {
- case 2: {
- int endPos = pointPos - 1;
- if (endPos < 0) {
- endPos += HISTORY_SIZE;
- }
- long denominator = eventTime - mHistoricTimes[endPos];
- if (denominator != 0) {
- return (mHistoricPos[pointPos] - mHistoricPos[endPos]) / denominator;
- }
- }
- // fall through
- case 1:
- return 0f;
- default:
- return null;
- }
- }
-
- float Sxx = sxi2 - sxi * sxi / count;
- float Sxy = sxiyi - sxi * syi / count;
- float Sxx2 = sxi3 - sxi * sxi2 / count;
- float Sx2y = sxi2yi - sxi2 * syi / count;
- float Sx2x2 = sxi4 - sxi2 * sxi2 / count;
-
- float denominator = Sxx * Sx2x2 - Sxx2 * Sxx2;
- if (denominator == 0) {
- // division by 0 when computing velocity
- return null;
- }
- // Compute a
- // float numerator = Sx2y*Sxx - Sxy*Sxx2;
-
- // Compute b
- float numerator = Sxy * Sx2x2 - Sx2y * Sxx2;
- float b = numerator / denominator;
-
- // Compute c
- // float c = syi/count - b * sxi/count - a * sxi2/count;
-
- return b;
+ mVelocityTracker.clear();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/util/NavBarPosition.java b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
index 0a98e1b..449dba8 100644
--- a/quickstep/src/com/android/quickstep/util/NavBarPosition.java
+++ b/quickstep/src/com/android/quickstep/util/NavBarPosition.java
@@ -19,7 +19,7 @@
import android.view.Surface;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController.Info;
import com.android.quickstep.SysUINavigationMode;
/**
@@ -30,7 +30,7 @@
private final SysUINavigationMode.Mode mMode;
private final int mDisplayRotation;
- public NavBarPosition(SysUINavigationMode.Mode mode, DefaultDisplay.Info info) {
+ public NavBarPosition(SysUINavigationMode.Mode mode, Info info) {
mMode = mode;
mDisplayRotation = info.rotation;
}
diff --git a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
new file mode 100644
index 0000000..60c7add
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+
+import android.content.Context;
+
+import com.android.quickstep.SysUINavigationMode;
+
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/** A feature flag that listens to navigation mode changes. */
+public class NavigationModeFeatureFlag implements
+ SysUINavigationMode.NavigationModeChangeListener {
+
+ public static final NavigationModeFeatureFlag LIVE_TILE = new NavigationModeFeatureFlag(
+ ENABLE_QUICKSTEP_LIVE_TILE::get, mode -> mode.hasGestures);
+
+ private final Supplier<Boolean> mBasePredicate;
+ private final Predicate<SysUINavigationMode.Mode> mModePredicate;
+ private boolean mSupported;
+
+ private NavigationModeFeatureFlag(Supplier<Boolean> basePredicate,
+ Predicate<SysUINavigationMode.Mode> modePredicate) {
+ mBasePredicate = basePredicate;
+ mModePredicate = modePredicate;
+ }
+
+ public boolean get() {
+ return mBasePredicate.get() && mSupported;
+ }
+
+ public void initialize(Context context) {
+ onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context).getMode());
+ SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
+ }
+
+ @Override
+ public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+ mSupported = mModePredicate.test(newMode);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/OverviewToHomeAnim.java
rename to quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
diff --git a/quickstep/src/com/android/quickstep/util/ProtoTracer.java b/quickstep/src/com/android/quickstep/util/ProtoTracer.java
new file mode 100644
index 0000000..ef9586d
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ProtoTracer.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static com.android.launcher3.tracing.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_H_VALUE;
+import static com.android.launcher3.tracing.LauncherTraceFileProto.MagicNumber.MAGIC_NUMBER_L_VALUE;
+
+import android.content.Context;
+import android.os.SystemClock;
+
+import android.os.Trace;
+import com.android.launcher3.tracing.LauncherTraceProto;
+import com.android.launcher3.tracing.LauncherTraceEntryProto;
+import com.android.launcher3.tracing.LauncherTraceFileProto;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.systemui.shared.tracing.FrameProtoTracer;
+import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams;
+import com.android.systemui.shared.tracing.ProtoTraceable;
+import com.google.protobuf.MessageLite;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Queue;
+
+
+/**
+ * Controller for coordinating winscope proto tracing.
+ */
+public class ProtoTracer implements ProtoTraceParams<MessageLite.Builder,
+ LauncherTraceFileProto.Builder, LauncherTraceEntryProto.Builder,
+ LauncherTraceProto.Builder> {
+
+ public static final MainThreadInitializedObject<ProtoTracer> INSTANCE =
+ new MainThreadInitializedObject<>(ProtoTracer::new);
+
+ private static final String TAG = "ProtoTracer";
+ private static final long MAGIC_NUMBER_VALUE =
+ ((long) MAGIC_NUMBER_H_VALUE << 32) | MAGIC_NUMBER_L_VALUE;
+
+ private final Context mContext;
+ private final FrameProtoTracer<MessageLite.Builder, LauncherTraceFileProto.Builder,
+ LauncherTraceEntryProto.Builder, LauncherTraceProto.Builder> mProtoTracer;
+
+ public ProtoTracer(Context context) {
+ mContext = context;
+ mProtoTracer = new FrameProtoTracer<>(this);
+ }
+
+ @Override
+ public File getTraceFile() {
+ return new File(mContext.getFilesDir(), "launcher_trace.pb");
+ }
+
+ @Override
+ public LauncherTraceFileProto.Builder getEncapsulatingTraceProto() {
+ return LauncherTraceFileProto.newBuilder();
+ }
+
+ @Override
+ public LauncherTraceEntryProto.Builder updateBufferProto(
+ LauncherTraceEntryProto.Builder reuseObj,
+ ArrayList<ProtoTraceable<LauncherTraceProto.Builder>> traceables) {
+ Trace.beginSection("ProtoTracer.updateBufferProto");
+ LauncherTraceEntryProto.Builder proto = LauncherTraceEntryProto.newBuilder();
+ proto.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ LauncherTraceProto.Builder launcherProto = LauncherTraceProto.newBuilder();
+ for (ProtoTraceable t : traceables) {
+ t.writeToProto(launcherProto);
+ }
+ proto.setLauncher(launcherProto);
+ Trace.endSection();
+ return proto;
+ }
+
+ @Override
+ public byte[] serializeEncapsulatingProto(LauncherTraceFileProto.Builder encapsulatingProto,
+ Queue<LauncherTraceEntryProto.Builder> buffer) {
+ Trace.beginSection("ProtoTracer.serializeEncapsulatingProto");
+ encapsulatingProto.setMagicNumber(MAGIC_NUMBER_VALUE);
+ for (LauncherTraceEntryProto.Builder entry : buffer) {
+ encapsulatingProto.addEntry(entry);
+ }
+ byte[] bytes = encapsulatingProto.build().toByteArray();
+ Trace.endSection();
+ return bytes;
+ }
+
+ @Override
+ public byte[] getProtoBytes(MessageLite.Builder proto) {
+ return proto.build().toByteArray();
+ }
+
+ @Override
+ public int getProtoSize(MessageLite.Builder proto) {
+ return proto.build().getSerializedSize();
+ }
+
+ public void start() {
+ mProtoTracer.start();
+ }
+
+ public void stop() {
+ mProtoTracer.stop();
+ }
+
+ public void add(ProtoTraceable<LauncherTraceProto.Builder> traceable) {
+ mProtoTracer.add(traceable);
+ }
+
+ public void remove(ProtoTraceable<LauncherTraceProto.Builder> traceable) {
+ mProtoTracer.remove(traceable);
+ }
+
+ public void scheduleFrameUpdate() {
+ mProtoTracer.scheduleFrameUpdate();
+ }
+
+ public void update() {
+ mProtoTracer.update();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
index 9ed2bbe..1544f00 100644
--- a/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
+++ b/quickstep/src/com/android/quickstep/util/QuickstepOnboardingPrefs.java
@@ -21,19 +21,17 @@
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.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.content.SharedPreferences;
-import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
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;
+import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.views.AllAppsEduView;
@@ -41,9 +39,9 @@
/**
* Extends {@link OnboardingPrefs} for quickstep-specific onboarding data.
*/
-public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
+public class QuickstepOnboardingPrefs extends OnboardingPrefs<QuickstepLauncher> {
- public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs) {
+ public QuickstepOnboardingPrefs(QuickstepLauncher launcher, SharedPreferences sharedPrefs) {
super(launcher, sharedPrefs);
StateManager<LauncherState> stateManager = launcher.getStateManager();
@@ -65,44 +63,7 @@
});
}
- boolean shelfBounceSeen = getBoolean(SHELF_BOUNCE_SEEN);
- if (!shelfBounceSeen && ENABLE_OVERVIEW_ACTIONS.get()
- && removeShelfFromOverview(launcher)) {
- // There's no shelf in overview, so don't bounce it (can't get to all apps anyway).
- shelfBounceSeen = true;
- mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, shelfBounceSeen).apply();
- }
- if (!shelfBounceSeen) {
- stateManager.addStateListener(new StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- LauncherState prevState = stateManager.getLastState();
-
- if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
- hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
- mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
- stateManager.removeStateListener(this);
- }
- }
- });
- }
-
- if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
- stateManager.addStateListener(new StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- if (finalState == ALL_APPS) {
- if (incrementEventCount(ALL_APPS_COUNT)) {
- stateManager.removeStateListener(this);
- mLauncher.getScrimView().updateDragHandleVisibility();
- }
- }
- }
- });
- }
-
- if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() && !hasReachedMaxCount(
- HOTSEAT_DISCOVERY_TIP_COUNT)) {
+ if (!hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
stateManager.addStateListener(new StateListener<LauncherState>() {
boolean mFromAllApps = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
similarity index 85%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
rename to quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index 5b0d503..ba70bf7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -32,16 +32,11 @@
public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
private static final int MY_ANIM_COUNT = 2;
- protected static final int NEXT_INDEX = AtomicAnimationFactory.NEXT_INDEX + MY_ANIM_COUNT;
protected final ACTIVITY_TYPE mActivity;
- /**
- * @param extraAnims number of animations supported by the subclass. This should not include
- * the 2 animations supported by this class.
- */
- public RecentsAtomicAnimationFactory(ACTIVITY_TYPE activity, int extraAnims) {
- super(MY_ANIM_COUNT + extraAnims);
+ public RecentsAtomicAnimationFactory(ACTIVITY_TYPE activity) {
+ super(MY_ANIM_COUNT);
mActivity = activity;
}
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 81d24d7..215f05a 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -23,23 +23,18 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
+import static com.android.launcher3.util.SettingsCache.ROTATION_SETTING_URI;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
-import android.database.ContentObserver;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.Handler;
-import android.provider.Settings;
import android.util.Log;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
@@ -50,12 +45,13 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
import java.lang.annotation.Retention;
import java.util.function.IntConsumer;
@@ -71,14 +67,8 @@
public final class RecentsOrientedState implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "RecentsOrientedState";
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
- private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- updateAutoRotateSetting();
- }
- };
@Retention(SOURCE)
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
public @interface SurfaceRotation {}
@@ -122,15 +112,20 @@
| FLAG_SWIPE_UP_NOT_RUNNING;
private final Context mContext;
- private final ContentResolver mContentResolver;
private final SharedPreferences mSharedPrefs;
private final OrientationEventListener mOrientationListener;
+ private final SettingsCache mSettingsCache;
+ private final SettingsCache.OnChangeListener mRotationChangeListener =
+ isEnabled -> updateAutoRotateSetting();
private final Matrix mTmpMatrix = new Matrix();
private int mFlags;
private int mPreviousRotation = ROTATION_0;
+ // Combined int which encodes the full state.
+ private int mStateId = 0;
+
/**
* @param rotationChangeListener Callback for receiving rotation events when rotation watcher
* is enabled
@@ -139,7 +134,6 @@
public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
IntConsumer rotationChangeListener) {
mContext = context;
- mContentResolver = context.getContentResolver();
mSharedPrefs = Utilities.getPrefs(context);
mOrientationListener = new OrientationEventListener(context) {
@Override
@@ -158,10 +152,12 @@
Resources res = context.getResources();
int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
* res.getDisplayMetrics().densityDpi / DENSITY_DEVICE_STABLE;
- if (originalSmallestWidth < 600) {
+ if (originalSmallestWidth < 600 && !mContext.getResources().getBoolean(
+ R.bool.allow_rotation)) {
mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY;
}
mFlags |= FLAG_SWIPE_UP_NOT_RUNNING;
+ mSettingsCache = SettingsCache.INSTANCE.get(mContext);
initFlags();
}
@@ -171,7 +167,7 @@
*/
public boolean setRecentsRotation(@SurfaceRotation int recentsRotation) {
mRecentsRotation = recentsRotation;
- return update(mTouchRotation, mDisplayRotation);
+ return updateHandler();
}
/**
@@ -185,8 +181,7 @@
* Sets if the swipe up gesture is currently running or not
*/
public boolean setGestureActive(boolean isGestureActive) {
- setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
- return update(mTouchRotation, mDisplayRotation);
+ return setFlag(FLAG_SWIPE_UP_NOT_RUNNING, !isGestureActive);
}
/**
@@ -199,18 +194,17 @@
*/
public boolean update(
@SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
- mRecentsActivityRotation = inferRecentsActivityRotation(displayRotation);
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
mPreviousRotation = touchRotation;
+ return updateHandler();
+ }
- PagedOrientationHandler oldHandler = mOrientationHandler;
+ private boolean updateHandler() {
+ mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
if (mRecentsActivityRotation == mTouchRotation
|| (canRecentsActivityRotate() && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
- if (DEBUG) {
- Log.d(TAG, "current RecentsOrientedState: " + this);
- }
} else if (mTouchRotation == ROTATION_90) {
mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
} else if (mTouchRotation == ROTATION_270) {
@@ -221,19 +215,26 @@
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
}
- return oldHandler != mOrientationHandler;
+
+ int oldStateId = mStateId;
+ // Each SurfaceRotation value takes two bits
+ mStateId = (((((mFlags << 2)
+ | mDisplayRotation) << 2)
+ | mTouchRotation) << 3)
+ | (mRecentsRotation < 0 ? 7 : mRecentsRotation);
+ return mStateId != oldStateId;
}
@SurfaceRotation
private int inferRecentsActivityRotation(@SurfaceRotation int displayRotation) {
if (isRecentsActivityRotationAllowed()) {
- return mRecentsRotation < ROTATION_0 ? displayRotation : mRecentsRotation;
+ return mRecentsRotation < 0 ? displayRotation : mRecentsRotation;
} else {
return ROTATION_0;
}
}
- private void setFlag(int mask, boolean enabled) {
+ private boolean setFlag(int mask, boolean enabled) {
boolean wasRotationEnabled = !TestProtocol.sDisableSensorRotation
&& (mFlags & VALUE_ROTATION_WATCHER_ENABLED) == VALUE_ROTATION_WATCHER_ENABLED
&& !canRecentsActivityRotate();
@@ -255,6 +256,7 @@
}
});
}
+ return updateHandler();
}
@Override
@@ -265,8 +267,8 @@
}
private void updateAutoRotateSetting() {
- setFlag(FLAG_SYSTEM_ROTATION_ALLOWED, Settings.System.getInt(mContentResolver,
- Settings.System.ACCELEROMETER_ROTATION, 1) == 1);
+ setFlag(FLAG_SYSTEM_ROTATION_ALLOWED,
+ mSettingsCache.getValue(ROTATION_SETTING_URI, 1));
}
private void updateHomeRotationSetting() {
@@ -275,10 +277,7 @@
}
private void initFlags() {
- SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
- boolean rotationWatcherSupported = mOrientationListener.canDetectOrientation() &&
- currentMode != TWO_BUTTONS;
- setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, rotationWatcherSupported);
+ setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, mOrientationListener.canDetectOrientation());
// initialize external flags
updateAutoRotateSetting();
@@ -292,9 +291,7 @@
public void initListeners() {
if (isMultipleOrientationSupportedByDevice()) {
mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
- mContentResolver.registerContentObserver(
- Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
- false, mSystemAutoRotateObserver);
+ mSettingsCache.register(ROTATION_SETTING_URI, mRotationChangeListener);
}
initFlags();
}
@@ -305,7 +302,7 @@
public void destroyListeners() {
if (isMultipleOrientationSupportedByDevice()) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
- mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
+ mSettingsCache.unregister(ROTATION_SETTING_URI, mRotationChangeListener);
}
setRotationWatcherEnabled(false);
}
@@ -329,6 +326,13 @@
return mRecentsActivityRotation;
}
+ /**
+ * Returns an id that can be used to tracking internal changes
+ */
+ public int getStateId() {
+ return mStateId;
+ }
+
public boolean isMultipleOrientationSupportedByDevice() {
return (mFlags & MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE)
== MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE;
@@ -506,19 +510,43 @@
}
}
+ /**
+ * Contrary to {@link #postDisplayRotation}.
+ */
+ public static void preDisplayRotation(@SurfaceRotation int displayRotation,
+ float screenWidth, float screenHeight, Matrix out) {
+ switch (displayRotation) {
+ case ROTATION_0:
+ return;
+ case ROTATION_90:
+ out.postRotate(90);
+ out.postTranslate(screenWidth, 0);
+ break;
+ case ROTATION_180:
+ out.postRotate(180);
+ out.postTranslate(screenHeight, screenWidth);
+ break;
+ case ROTATION_270:
+ out.postRotate(270);
+ out.postTranslate(0, screenHeight);
+ break;
+ }
+ }
+
@NonNull
@Override
public String toString() {
boolean systemRotationOn = (mFlags & FLAG_SYSTEM_ROTATION_ALLOWED) != 0;
return "["
- + "this=" + extractObjectNameAndAddress(super.toString())
- + " mOrientationHandler=" +
- extractObjectNameAndAddress(mOrientationHandler.toString())
+ + "this=" + nameAndAddress(this)
+ + " mOrientationHandler=" + nameAndAddress(mOrientationHandler)
+ " mDisplayRotation=" + mDisplayRotation
+ " mTouchRotation=" + mTouchRotation
+ " mRecentsActivityRotation=" + mRecentsActivityRotation
+ + " mRecentsRotation=" + mRecentsRotation
+ " isRecentsActivityRotationAllowed=" + isRecentsActivityRotationAllowed()
+ " mSystemRotation=" + systemRotationOn
+ + " mStateId=" + mStateId
+ " mFlags=" + mFlags
+ "]";
}
@@ -534,4 +562,8 @@
? idp.landscapeProfile
: idp.portraitProfile;
}
+
+ private static String nameAndAddress(Object obj) {
+ return obj.getClass().getSimpleName() + "@" + obj.hashCode();
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java b/quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/RectFSpringAnim.java
rename to quickstep/src/com/android/quickstep/util/RectFSpringAnim.java
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 04308c8..3adb459 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -21,6 +21,8 @@
import android.os.Handler;
import com.android.launcher3.LauncherAnimationRunner;
+import com.android.launcher3.LauncherAnimationRunner.AnimationResult;
+import com.android.launcher3.WrappedAnimationRunnerImpl;
import com.android.launcher3.WrappedLauncherAnimationRunner;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -28,24 +30,29 @@
public abstract class RemoteAnimationProvider {
- LauncherAnimationRunner mAnimationRunner;
+ WrappedAnimationRunnerImpl mAnimationRunner;
public abstract AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets);
ActivityOptions toActivityOptions(Handler handler, long duration, Context context) {
- mAnimationRunner = new LauncherAnimationRunner(handler,
- false /* startAtFrontOfQueue */) {
+ mAnimationRunner = new WrappedAnimationRunnerImpl() {
+ @Override
+ public Handler getHandler() {
+ return handler;
+ }
@Override
- public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
- RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+ public void onCreateAnimation(int transit,
+ RemoteAnimationTargetCompat[] appTargets,
+ RemoteAnimationTargetCompat[] wallpaperTargets,
+ RemoteAnimationTargetCompat[] nonApps,
+ AnimationResult result) {
result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
}
};
final LauncherAnimationRunner wrapper = new WrappedLauncherAnimationRunner(
mAnimationRunner, false /* startAtFrontOfQueue */);
-
return ActivityOptionsCompat.makeRemoteAnimation(
new RemoteAnimationAdapterCompat(wrapper, duration, 0));
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
index a770e8e..176478f 100644
--- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
@@ -32,7 +32,7 @@
import androidx.annotation.UiThread;
import com.android.launcher3.R;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.WindowBounds;
import java.util.ArrayList;
@@ -77,7 +77,7 @@
WindowBounds bounds = new WindowBounds(wm.getBounds(),
new Rect(insets.left, insets.top, insets.right, insets.bottom));
- int rotation = DefaultDisplay.INSTANCE.get(context).getInfo().rotation;
+ int rotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
int halfDividerSize = context.getResources()
.getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
similarity index 97%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
rename to quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 4120331..932ff27 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -138,7 +139,7 @@
addDepthAnimationForState(launcher, NORMAL, ALPHA_DURATION_MS);
- mAnimators.play(launcher.getDragLayer().getScrim().createSysuiMultiplierAnim(0f, 1f)
+ mAnimators.play(launcher.getDragLayer().getSysUiScrim().createSysuiMultiplierAnim(0f, 1f)
.setDuration(ALPHA_DURATION_MS));
mAnimators.addListener(new AnimatorListenerAdapter() {
@Override
@@ -158,7 +159,8 @@
*/
private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
StateAnimationConfig config = new StateAnimationConfig();
- config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
+ config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER
+ | SKIP_TASKBAR;
config.duration = 0;
// setRecentsAttachedToAppWindow() will animate recents out.
launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/SurfaceTransactionApplier.java
rename to quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
new file mode 100644
index 0000000..0ce5072
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -0,0 +1,265 @@
+/*
+ * 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 com.android.systemui.shared.system.InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_PIP;
+
+import android.animation.Animator;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.systemui.shared.pip.PipSurfaceTransactionHelper;
+import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
+
+/**
+ * An {@link Animator} that animates an Activity to PiP (picture-in-picture) window when
+ * swiping up (in gesture navigation mode). Note that this class is derived from
+ * {@link com.android.wm.shell.pip.PipAnimationController.PipTransitionAnimator}.
+ *
+ * TODO: consider sharing this class including the animator and leash operations between
+ * Launcher and SysUI. Also, there should be one source of truth for the corner radius of the
+ * PiP window, which would ideally be on SysUI side as well.
+ */
+public class SwipePipToHomeAnimator extends ValueAnimator implements
+ ValueAnimator.AnimatorUpdateListener {
+ private static final String TAG = SwipePipToHomeAnimator.class.getSimpleName();
+
+ private final int mTaskId;
+ private final ComponentName mComponentName;
+ private final SurfaceControl mLeash;
+ private final Rect mAppBounds = new Rect();
+ private final Rect mStartBounds = new Rect();
+ private final Rect mDestinationBounds = new Rect();
+ private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
+
+ /** for calculating the transform in {@link #onAnimationUpdate(ValueAnimator)} */
+ private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect());
+ private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect());
+ private final Rect mSourceHintRectInsets;
+ private final Rect mSourceInsets = new Rect();
+
+ /** for rotation via {@link #setFromRotation(TaskViewSimulator, int)} */
+ private @RecentsOrientedState.SurfaceRotation int mFromRotation = Surface.ROTATION_0;
+ private final Rect mDestinationBoundsTransformed = new Rect();
+ private final Rect mDestinationBoundsAnimation = new Rect();
+
+ /**
+ * Flag to avoid the double-end problem since the leash would have been released
+ * after the first end call and any further operations upon it would lead to NPE.
+ */
+ private boolean mHasAnimationEnded;
+
+ /**
+ * @param taskId Task id associated with this animator, see also {@link #getTaskId()}
+ * @param componentName Component associated with this animator,
+ * see also {@link #getComponentName()}
+ * @param leash {@link SurfaceControl} this animator operates on
+ * @param sourceRectHint See the definition in {@link android.app.PictureInPictureParams}
+ * @param appBounds Bounds of the application, sourceRectHint is based on this bounds
+ * @param startBounds Bounds of the application when this animator starts. This can be
+ * different from the appBounds if user has swiped a certain distance and
+ * Launcher has performed transform on the leash.
+ * @param destinationBounds Bounds of the destination this animator ends to
+ */
+ public SwipePipToHomeAnimator(int taskId,
+ @NonNull ComponentName componentName,
+ @NonNull SurfaceControl leash,
+ @Nullable Rect sourceRectHint,
+ @NonNull Rect appBounds,
+ @NonNull Rect startBounds,
+ @NonNull Rect destinationBounds,
+ @NonNull View view) {
+ mTaskId = taskId;
+ mComponentName = componentName;
+ mLeash = leash;
+ mAppBounds.set(appBounds);
+ mStartBounds.set(startBounds);
+ mDestinationBounds.set(destinationBounds);
+ mDestinationBoundsTransformed.set(mDestinationBounds);
+ mDestinationBoundsAnimation.set(mDestinationBounds);
+ mSurfaceTransactionHelper = new PipSurfaceTransactionHelper();
+
+ if (sourceRectHint == null) {
+ mSourceHintRectInsets = null;
+ } else {
+ mSourceHintRectInsets = new Rect(sourceRectHint.left - appBounds.left,
+ sourceRectHint.top - appBounds.top,
+ appBounds.right - sourceRectHint.right,
+ appBounds.bottom - sourceRectHint.bottom);
+ }
+
+ addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ InteractionJankMonitorWrapper.begin(view, CUJ_APP_CLOSE_TO_PIP);
+ super.onAnimationStart(animation);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ InteractionJankMonitorWrapper.cancel(CUJ_APP_CLOSE_TO_PIP);
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ InteractionJankMonitorWrapper.end(CUJ_APP_CLOSE_TO_PIP);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mHasAnimationEnded) super.onAnimationEnd(animation);
+ SwipePipToHomeAnimator.this.onAnimationEnd();
+ }
+ });
+ addUpdateListener(this);
+ }
+
+ /** sets the from rotation if it's different from the target rotation. */
+ public void setFromRotation(TaskViewSimulator taskViewSimulator,
+ @RecentsOrientedState.SurfaceRotation int fromRotation) {
+ if (fromRotation != Surface.ROTATION_90 && fromRotation != Surface.ROTATION_270) {
+ Log.wtf(TAG, "Not a supported rotation, rotation=" + fromRotation);
+ return;
+ }
+ mFromRotation = fromRotation;
+ final Matrix matrix = new Matrix();
+ taskViewSimulator.applyWindowToHomeRotation(matrix);
+
+ // map the destination bounds into window space. mDestinationBounds is always calculated
+ // in the final home space and the animation runs in original window space.
+ final RectF transformed = new RectF(mDestinationBounds);
+ matrix.mapRect(transformed, new RectF(mDestinationBounds));
+ transformed.round(mDestinationBoundsTransformed);
+
+ // set the animation destination bounds for RectEvaluator calculation.
+ // bounds and insets are calculated as if the transition is from mAppBounds to
+ // mDestinationBoundsAnimation, separated from rotate / scale / position.
+ mDestinationBoundsAnimation.set(mAppBounds.left, mAppBounds.top,
+ mAppBounds.left + mDestinationBounds.width(),
+ mAppBounds.top + mDestinationBounds.height());
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ if (mHasAnimationEnded) return;
+
+ final float fraction = animator.getAnimatedFraction();
+ final Rect bounds = mRectEvaluator.evaluate(fraction, mStartBounds,
+ mDestinationBoundsAnimation);
+ final SurfaceControl.Transaction tx =
+ PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+ if (mSourceHintRectInsets == null) {
+ // no source rect hint been set, directly scale the window down
+ onAnimationScale(fraction, tx, bounds);
+ } else {
+ // scale and crop according to the source rect hint
+ onAnimationScaleAndCrop(fraction, tx, bounds);
+ }
+ mSurfaceTransactionHelper.resetCornerRadius(tx, mLeash);
+ tx.apply();
+ }
+
+ /** scale the window directly with no source rect hint being set */
+ private void onAnimationScale(float fraction, SurfaceControl.Transaction tx, Rect bounds) {
+ if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
+ final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
+ mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds,
+ rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
+ } else {
+ mSurfaceTransactionHelper.scale(tx, mLeash, mAppBounds, bounds);
+ }
+ }
+
+ /** scale and crop the window with source rect hint */
+ private void onAnimationScaleAndCrop(float fraction, SurfaceControl.Transaction tx,
+ Rect bounds) {
+ final Rect insets = mInsetsEvaluator.evaluate(fraction, mSourceInsets,
+ mSourceHintRectInsets);
+ if (mFromRotation == Surface.ROTATION_90 || mFromRotation == Surface.ROTATION_270) {
+ final RotatedPosition rotatedPosition = getRotatedPosition(fraction);
+ mSurfaceTransactionHelper.scaleAndRotate(tx, mLeash, mAppBounds, bounds, insets,
+ rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
+ } else {
+ mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mAppBounds, bounds, insets);
+ }
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public Rect getDestinationBounds() {
+ return mDestinationBounds;
+ }
+
+ private void onAnimationEnd() {
+ if (mHasAnimationEnded) return;
+
+ final SurfaceControl.Transaction tx =
+ PipSurfaceTransactionHelper.newSurfaceControlTransaction();
+ mSurfaceTransactionHelper.reset(tx, mLeash, mDestinationBoundsTransformed, mFromRotation);
+ tx.apply();
+ mHasAnimationEnded = true;
+ }
+
+ private RotatedPosition getRotatedPosition(float fraction) {
+ final float degree, positionX, positionY;
+ if (mFromRotation == Surface.ROTATION_90) {
+ degree = -90 * fraction;
+ positionX = fraction * (mDestinationBoundsTransformed.left - mAppBounds.left)
+ + mAppBounds.left;
+ positionY = fraction * (mDestinationBoundsTransformed.bottom - mAppBounds.top)
+ + mAppBounds.top;
+ } else {
+ degree = 90 * fraction;
+ positionX = fraction * (mDestinationBoundsTransformed.right - mAppBounds.left)
+ + mAppBounds.left;
+ positionY = fraction * (mDestinationBoundsTransformed.top - mAppBounds.top)
+ + mAppBounds.top;
+ }
+ return new RotatedPosition(degree, positionX, positionY);
+ }
+
+ private static class RotatedPosition {
+ private final float degree;
+ private final float positionX;
+ private final float positionY;
+
+ private RotatedPosition(float degree, float positionX, float positionY) {
+ this.degree = degree;
+ this.positionX = positionX;
+ this.positionY = positionY;
+ }
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java b/quickstep/src/com/android/quickstep/util/TaskCornerRadius.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskCornerRadius.java
rename to quickstep/src/com/android/quickstep/util/TaskCornerRadius.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
similarity index 67%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
rename to quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index c060145..9537247 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -15,14 +15,17 @@
*/
package com.android.quickstep.util;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.states.RotationHelper.deltaRotation;
import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
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;
-import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
@@ -30,7 +33,10 @@
import android.graphics.RectF;
import android.util.IntProperty;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -38,7 +44,6 @@
import com.android.quickstep.BaseActivityInterface;
import com.android.quickstep.views.RecentsView.ScrollState;
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
-import com.android.quickstep.views.TaskView;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -51,30 +56,35 @@
public static final IntProperty<TaskViewSimulator> SCROLL =
new IntProperty<TaskViewSimulator>("scroll") {
- @Override
- public void setValue(TaskViewSimulator simulator, int i) {
- simulator.setScroll(i);
- }
+ @Override
+ public void setValue(TaskViewSimulator simulator, int scroll) {
+ simulator.setScroll(scroll);
+ }
- @Override
- public Integer get(TaskViewSimulator simulator) {
- return simulator.mScrollState.scroll;
- }
- };
+ @Override
+ public Integer get(TaskViewSimulator simulator) {
+ return simulator.mScrollState.scroll;
+ }
+ };
private final Rect mTmpCropRect = new Rect();
private final RectF mTempRectF = new RectF();
private final float[] mTempPoint = new float[2];
- private final RecentsOrientedState mOrientationState;
private final Context mContext;
private final BaseActivityInterface mSizeStrategy;
+ @NonNull
+ private RecentsOrientedState mOrientationState;
+ private final boolean mIsRecentsRtl;
+
private final Rect mTaskRect = new Rect();
+ private boolean mDrawsBelowRecents;
private final PointF mPivot = new PointF();
private DeviceProfile mDp;
private final Matrix mMatrix = new Matrix();
+ private final Matrix mMatrixTmp = new Matrix();
private final Point mRunningTargetWindowPosition = new Point();
// Thumbnail view properties
@@ -85,17 +95,23 @@
// TaskView properties
private final FullscreenDrawParams mCurrentFullscreenParams;
- private float mCurveScale = 1;
+ public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat();
+ public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat();
+ public final AnimatedFloat gridTranslationSecondary = new AnimatedFloat();
// RecentsView properties
public final AnimatedFloat recentsViewScale = new AnimatedFloat();
public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat();
+ public final AnimatedFloat gridProgress = new AnimatedFloat();
private final ScrollState mScrollState = new ScrollState();
// Cached calculations
private boolean mLayoutValid = false;
private boolean mScrollValid = false;
+ private int mOrientationStateId;
+ private final int mTaskThumbnailPadding;
+ private final int mRowSpacing;
public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
mContext = context;
@@ -103,8 +119,12 @@
mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
mOrientationState.setGestureActive(true);
-
mCurrentFullscreenParams = new FullscreenDrawParams(context);
+ mOrientationStateId = mOrientationState.getStateId();
+ Resources resources = context.getResources();
+ mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources);
+ mTaskThumbnailPadding = (int) resources.getDimension(R.dimen.task_thumbnail_top_margin);
+ mRowSpacing = (int) resources.getDimension(R.dimen.recents_row_spacing);
}
/**
@@ -112,23 +132,14 @@
*/
public void setDp(DeviceProfile dp) {
mDp = dp;
- mOrientationState.setMultiWindowMode(mDp.isMultiWindowMode);
mLayoutValid = false;
}
/**
- * @see com.android.quickstep.views.RecentsView#setLayoutRotation(int, int)
+ * Sets the orientation state used for this animation
*/
- public void setLayoutRotation(int touchRotation, int displayRotation) {
- mOrientationState.update(touchRotation, displayRotation);
- mLayoutValid = false;
- }
-
- /**
- * @see com.android.quickstep.views.RecentsView#onConfigurationChanged(Configuration)
- */
- public void setRecentsRotation(int recentsRotation) {
- mOrientationState.setRecentsRotation(recentsRotation);
+ public void setOrientationState(@NonNull RecentsOrientedState orientationState) {
+ mOrientationState = orientationState;
mLayoutValid = false;
}
@@ -175,8 +186,12 @@
}
}
+ public void setDrawsBelowRecents(boolean drawsBelowRecents) {
+ mDrawsBelowRecents = drawsBelowRecents;
+ }
+
/**
- * Adds animation for all the components corresponding to transition from an app to overview
+ * Adds animation for all the components corresponding to transition from an app to overview.
*/
public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
@@ -184,6 +199,14 @@
}
/**
+ * Adds animation for all the components corresponding to transition from overview to the app.
+ */
+ public void addOverviewToAppAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+ pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 0, 1, interpolator);
+ pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, 1, getFullScreenScale(), interpolator);
+ }
+
+ /**
* Returns the current clipped/visible window bounds in the window coordinate space
*/
public RectF getCurrentCropRect() {
@@ -195,6 +218,18 @@
return mTempRectF;
}
+ /**
+ * Returns the current task bounds in the Launcher coordinate space.
+ */
+ public RectF getCurrentRect() {
+ RectF result = getCurrentCropRect();
+ mMatrixTmp.set(mMatrix);
+ preDisplayRotation(mOrientationState.getDisplayRotation(), mDp.widthPx, mDp.heightPx,
+ mMatrixTmp);
+ mMatrixTmp.mapRect(result);
+ return result;
+ }
+
public RecentsOrientedState getOrientationState() {
return mOrientationState;
}
@@ -226,8 +261,9 @@
if (mDp == null || mThumbnailPosition.isEmpty()) {
return;
}
- if (!mLayoutValid) {
+ if (!mLayoutValid || mOrientationStateId != mOrientationState.getStateId()) {
mLayoutValid = true;
+ mOrientationStateId = mOrientationState.getStateId();
getFullScreenScale();
mThumbnailData.rotation = mOrientationState.getDisplayRotation();
@@ -250,13 +286,13 @@
int start = mOrientationState.getOrientationHandler()
.getPrimaryValue(mTaskRect.left, mTaskRect.top);
mScrollState.screenCenter = start + mScrollState.scroll + mScrollState.halfPageSize;
- mScrollState.updateInterpolation(start);
- mCurveScale = TaskView.getCurveScaleForInterpolation(mScrollState.linearInterpolation);
+ mScrollState.updateInterpolation(mDp, start);
}
- float progress = Utilities.boundToRange(fullScreenProgress.value, 0, 1);
+ float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
mCurrentFullscreenParams.setProgress(
- progress, recentsViewScale.value, mTaskRect.width(), mDp, mPositionHelper);
+ fullScreenProgress, recentsViewScale.value, mTaskRect.width(), mDp,
+ mPositionHelper);
// Apply thumbnail matrix
RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
@@ -268,9 +304,30 @@
mMatrix.postTranslate(insets.left, insets.top);
mMatrix.postScale(scale, scale);
- // Apply TaskView matrix: translate, scale, scroll
+ float interpolatedGridProgress = ACCEL_DEACCEL.getInterpolation(gridProgress.value);
+
+ // Apply TaskView matrix: gridProgress
+ final int boxLength = (int) Math.max(taskWidth, taskHeight);
+ float availableHeight =
+ mTaskThumbnailPadding + taskHeight + mSizeStrategy.getOverviewActionsHeight(
+ mContext);
+ float rowHeight = (availableHeight - mRowSpacing) / 2;
+ float gridScale = rowHeight / (boxLength + mTaskThumbnailPadding);
+ scale = Utilities.mapRange(interpolatedGridProgress, 1f, gridScale);
+ mMatrix.postScale(scale, scale, mIsRecentsRtl ? 0 : taskWidth, 0);
+ float taskWidthDiff = taskWidth * (1 - gridScale);
+ float taskWidthOffset = mIsRecentsRtl ? taskWidthDiff : -taskWidthDiff;
+ mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+ Utilities.mapRange(interpolatedGridProgress, 0, taskWidthOffset));
+ mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
+ Utilities.mapRange(interpolatedGridProgress, 0, gridTranslationSecondary.value));
+
+ // Apply TaskView matrix: translate, scroll
mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
- mMatrix.postScale(mCurveScale, mCurveScale, taskWidth / 2, taskHeight / 2);
+ mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+ taskPrimaryTranslation.value);
+ mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
+ taskSecondaryTranslation.value);
mOrientationState.getOrientationHandler().set(
mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
@@ -294,7 +351,14 @@
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
builder.withMatrix(mMatrix)
.withWindowCrop(mTmpCropRect)
- .withCornerRadius(getCurrentCornerRadius());
+ .withCornerRadius(getCurrentCornerRadius())
+ .withShadowRadius(app.isTranslucent ? 0 : params.getShadowRadius());
+
+ if (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);
+ }
}
/**
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
similarity index 86%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
rename to quickstep/src/com/android/quickstep/util/TransformParams.java
index 0135f74..cdf5163 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -16,6 +16,7 @@
package com.android.quickstep.util;
import android.util.FloatProperty;
+import android.view.SurfaceControl;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
@@ -56,8 +57,10 @@
private float mProgress;
private float mTargetAlpha;
private float mCornerRadius;
+ private float mShadowRadius;
private RemoteAnimationTargets mTargetSet;
private SurfaceTransactionApplier mSyncTransactionApplier;
+ private SurfaceControl mRecentsSurface;
private BuilderProxy mHomeBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
private BuilderProxy mBaseBuilderProxy = BuilderProxy.ALWAYS_VISIBLE;
@@ -66,6 +69,7 @@
mProgress = 0;
mTargetAlpha = 1;
mCornerRadius = -1;
+ mShadowRadius = 0;
}
/**
@@ -89,6 +93,14 @@
}
/**
+ * Sets the shadow radius of the transformed window, in pixels.
+ */
+ public TransformParams setShadowRadius(float shadowRadius) {
+ mShadowRadius = shadowRadius;
+ return this;
+ }
+
+ /**
* Specifies the alpha of the transformed window. Default is 1.
*/
public TransformParams setTargetAlpha(float targetAlpha) {
@@ -138,6 +150,8 @@
public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
RemoteAnimationTargets targets = mTargetSet;
SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
+ 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);
@@ -165,6 +179,20 @@
return surfaceParams;
}
+ private static SurfaceControl getRecentsSurface(RemoteAnimationTargets targets) {
+ for (int i = 0; i < targets.unfilteredApps.length; i++) {
+ RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
+ if (app.mode == targets.targetMode) {
+ if (app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS) {
+ return app.leash.getSurfaceControl();
+ }
+ } else {
+ return app.leash.getSurfaceControl();
+ }
+ }
+ return null;
+ }
+
// Pubic getters so outside packages can read the values.
public float getProgress() {
@@ -179,6 +207,14 @@
return mCornerRadius;
}
+ public float getShadowRadius() {
+ return mShadowRadius;
+ }
+
+ public SurfaceControl getRecentsSurface() {
+ return mRecentsSurface;
+ }
+
public RemoteAnimationTargets getTargetSet() {
return mTargetSet;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
rename to quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
index 29b9558..7bbde30 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
+++ b/quickstep/src/com/android/quickstep/util/TriggerSwipeUpTouchTracker.java
@@ -21,13 +21,14 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import android.view.ViewConfiguration;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
/**
@@ -50,7 +51,8 @@
NavBarPosition navBarPosition, Runnable onInterceptTouch,
OnSwipeUpListener onSwipeUp) {
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
- mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
+ mMinFlingVelocity = context.getResources().getDimension(
+ R.dimen.quickstep_fling_threshold_speed);
mNavBarPosition = navBarPosition;
mDisableHorizontalSwipe = disableHorizontalSwipe;
mOnInterceptTouch = onInterceptTouch;
@@ -130,7 +132,7 @@
}
private void onGestureEnd(MotionEvent ev) {
- mVelocityTracker.computeCurrentVelocity(1000);
+ mVelocityTracker.computeCurrentVelocity(PX_PER_MS);
float velocityX = mVelocityTracker.getXVelocity();
float velocityY = mVelocityTracker.getYVelocity();
float velocity = mNavBarPosition.isRightEdge()
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
similarity index 98%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
rename to quickstep/src/com/android/quickstep/views/AllAppsEduView.java
index c06dd9c..97a239c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/AllAppsEduView.java
+++ b/quickstep/src/com/android/quickstep/views/AllAppsEduView.java
@@ -105,11 +105,6 @@
}
@Override
- public void logActionCommand(int command) {
- // TODO
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_ALL_APPS_EDU) != 0;
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
similarity index 60%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
rename to quickstep/src/com/android/quickstep/views/ClearAllButton.java
index fd74357..e7101cc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -43,14 +43,20 @@
private float mScrollAlpha = 1;
private float mContentAlpha = 1;
private float mVisibilityAlpha = 1;
+ private float mGridProgress = 1;
private boolean mIsRtl;
+ private final float mOriginalTranslationX, mOriginalTranslationY;
+ private float mNormalTranslationPrimary;
+ private float mGridTranslationPrimary;
private int mScrollOffset;
public ClearAllButton(Context context, AttributeSet attrs) {
super(context, attrs);
mIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+ mOriginalTranslationX = getTranslationX();
+ mOriginalTranslationY = getTranslationY();
}
@Override
@@ -97,9 +103,18 @@
return;
}
- float shift = Math.min(scrollState.scrollFromEdge, orientationSize);
- float translation = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift);
- orientationHandler.setPrimaryAndResetSecondaryTranslate(this, translation);
+ float shift;
+ if (mIsRtl) {
+ shift = Math.min(scrollState.scrollFromEdge, orientationSize);
+ } else {
+ shift = Math.min(scrollState.scrollFromEdge,
+ orientationSize + getGridTrans(mGridTranslationPrimary))
+ - getGridTrans(mGridTranslationPrimary);
+ }
+ mNormalTranslationPrimary = mIsRtl ? (mScrollOffset - shift) : (mScrollOffset + shift);
+ applyPrimaryTranslation();
+ orientationHandler.getSecondaryViewTranslate().set(this,
+ orientationHandler.getSecondaryValue(mOriginalTranslationX, mOriginalTranslationY));
mScrollAlpha = 1 - shift / orientationSize;
updateAlpha();
}
@@ -107,6 +122,48 @@
private void updateAlpha() {
final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
setAlpha(alpha);
- setClickable(alpha == 1);
+ setClickable(Math.min(alpha, 1) == 1);
+ }
+
+ public void setGridTranslationPrimary(float gridTranslationPrimary) {
+ mGridTranslationPrimary = gridTranslationPrimary;
+ applyPrimaryTranslation();
+ }
+
+ public float getScrollAdjustment(boolean gridEnabled) {
+ float scrollAdjustment = 0;
+ if (gridEnabled) {
+ scrollAdjustment += mGridTranslationPrimary;
+ }
+ return scrollAdjustment;
+ }
+
+ public float getOffsetAdjustment(boolean gridEnabled) {
+ return getScrollAdjustment(gridEnabled);
+ }
+
+ /**
+ * Moves ClearAllButton between carousel and 2 row grid.
+ *
+ * @param gridProgress 0 = carousel; 1 = 2 row grid.
+ */
+ public void setGridProgress(float gridProgress) {
+ mGridProgress = gridProgress;
+ applyPrimaryTranslation();
+ }
+
+ private void applyPrimaryTranslation() {
+ RecentsView recentsView = getRecentsView();
+ if (recentsView == null) {
+ return;
+ }
+
+ PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+ orientationHandler.getPrimaryViewTranslate().set(this,
+ mNormalTranslationPrimary + getGridTrans(mGridTranslationPrimary));
+ }
+
+ private float getGridTrans(float endTranslation) {
+ return mGridProgress > 0 ? endTranslation : 0;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
similarity index 76%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
rename to quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index b06d4bc..25ae055 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -17,6 +17,8 @@
package com.android.quickstep.views;
import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.CENTER_HORIZONTAL;
import static com.android.launcher3.Utilities.prefixTextWithIcon;
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
@@ -27,6 +29,7 @@
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.AppUsageLimit;
+import android.graphics.Outline;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
@@ -35,6 +38,9 @@
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.StringRes;
@@ -42,7 +48,6 @@
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.recents.model.Task;
import java.time.Duration;
@@ -62,6 +67,11 @@
private Task mTask;
private boolean mHasLimit;
private long mAppRemainingTimeMs;
+ private View mBanner;
+ private ViewOutlineProvider mOldBannerOutlineProvider;
+ private float mBannerOffsetPercentage;
+ private float mBannerAlpha = 1f;
+ private float mVerticalOffset = 0f;
public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
mActivity = activity;
@@ -69,18 +79,10 @@
mLauncherApps = activity.getSystemService(LauncherApps.class);
}
- private void setTaskFooter(View view) {
- View oldFooter = mTaskView.setFooter(TaskView.INDEX_DIGITAL_WELLBEING_TOAST, view);
- if (oldFooter != null) {
- oldFooter.setOnClickListener(null);
- mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, oldFooter);
- }
- }
-
private void setNoLimit() {
mHasLimit = false;
mTaskView.setContentDescription(mTask.titleDescription);
- setTaskFooter(null);
+ replaceBanner(null);
mAppRemainingTimeMs = 0;
}
@@ -91,7 +93,7 @@
mActivity, mTaskView);
toast.setText(prefixTextWithIcon(mActivity, R.drawable.ic_hourglass_top, getText()));
toast.setOnClickListener(this::openAppUsageSettings);
- setTaskFooter(toast);
+ replaceBanner(toast);
mTaskView.setContentDescription(
getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs));
@@ -217,8 +219,8 @@
view, 0, 0,
view.getWidth(), view.getHeight());
activity.startActivity(intent, options.toBundle());
- activity.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.ControlType.APP_USAGE_SETTINGS, view);
+
+ // TODO: add WW logging on the app usage settings click.
} catch (ActivityNotFoundException e) {
Log.e(TAG, "Failed to open app usage settings for task "
+ mTask.getTopComponent().getPackageName(), e);
@@ -234,4 +236,65 @@
getText(appRemainingTimeMs)) :
task.titleDescription;
}
+
+ private void replaceBanner(View view) {
+ resetOldBanner();
+ setBanner(view);
+ }
+
+ private void resetOldBanner() {
+ if (mBanner != null) {
+ mBanner.setOutlineProvider(mOldBannerOutlineProvider);
+ mTaskView.removeView(mBanner);
+ mBanner.setOnClickListener(null);
+ mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, mBanner);
+ }
+ }
+
+ private void setBanner(View view) {
+ mBanner = view;
+ if (view != null) {
+ setupAndAddBanner();
+ setBannerOutline();
+ }
+ }
+
+ private void setupAndAddBanner() {
+ FrameLayout.LayoutParams layoutParams =
+ (FrameLayout.LayoutParams) mBanner.getLayoutParams();
+ layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+ layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
+ mTaskView.getThumbnail().getLayoutParams()).bottomMargin;
+ mBanner.setTranslationY(mBannerOffsetPercentage * mBanner.getHeight());
+ mBanner.setAlpha(mBannerAlpha);
+ mTaskView.addView(mBanner);
+ }
+
+ private void setBannerOutline() {
+ mOldBannerOutlineProvider = mBanner.getOutlineProvider();
+ mBanner.setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ mOldBannerOutlineProvider.getOutline(view, outline);
+ outline.offset(0, Math.round(-view.getTranslationY() + mVerticalOffset));
+ }
+ });
+ mBanner.setClipToOutline(true);
+ }
+
+ void updateBannerOffset(float offsetPercentage, float verticalOffset) {
+ if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
+ mVerticalOffset = verticalOffset;
+ mBannerOffsetPercentage = offsetPercentage;
+ mBanner.setTranslationY(offsetPercentage * mBanner.getHeight() + mVerticalOffset);
+ mBanner.invalidateOutline();
+ }
+ }
+
+ void updateBannerAlpha(float alpha) {
+ if (mBanner != null && mBannerAlpha != alpha) {
+ mBannerAlpha = alpha;
+ mBanner.setAlpha(alpha);
+ }
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java b/quickstep/src/com/android/quickstep/views/IconView.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
rename to quickstep/src/com/android/quickstep/views/IconView.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
similarity index 67%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
rename to quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index b934c29..c62f3e2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,47 +15,36 @@
*/
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
+import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
-import android.os.UserHandle;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.Surface;
import android.widget.FrameLayout;
import com.android.launcher3.BaseQuickstepLauncher;
-import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.views.ScrimView;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.util.OverviewToHomeAnim;
-import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
-import com.android.systemui.shared.recents.model.Task;
/**
* {@link RecentsView} used in Launcher activity
@@ -64,8 +53,6 @@
public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
implements StateListener<LauncherState> {
- private final TransformParams mTransformParams = new TransformParams();
-
private RecentsExtraCard mRecentsExtraCardPlugin;
private RecentsExtraViewContainer mRecentsExtraViewContainer;
private PluginListener<RecentsExtraCard> mRecentsExtraCardPluginListener =
@@ -108,7 +95,7 @@
public void startHome() {
Runnable onReachedHome = () -> mActivity.getStateManager().goToState(NORMAL, false);
OverviewToHomeAnim overviewToHomeAnim = new OverviewToHomeAnim(mActivity, onReachedHome);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
switchToScreenshot(null,
() -> finishRecentsAnimation(true /* toRecents */,
() -> overviewToHomeAnim.animateWithVelocity(0)));
@@ -117,17 +104,6 @@
}
}
- @Override
- public void setTranslationY(float translationY) {
- super.setTranslationY(translationY);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- LauncherState state = mActivity.getStateManager().getState();
- if (state == OVERVIEW || state == ALL_APPS) {
- redrawLiveTile(false);
- }
- }
- }
-
/**
* Animates adjacent tasks and translate hotseat off screen as well.
*/
@@ -150,29 +126,10 @@
}
anim.play(ObjectAnimator.ofFloat(
mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
-
- ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt(
- mActivity.getScrimView(), ScrimView.DRAG_HANDLE_ALPHA, 0);
- dragHandleAnim.setInterpolator(Interpolators.ACCEL_2);
- anim.play(dragHandleAnim);
-
return anim;
}
@Override
- protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (tv.isRunningTask()) {
- mTransformParams.setProgress(1 - progress)
- .setSyncTransactionApplier(mSyncTransactionApplier);
- // TODO: Revisit live tiles
- } else {
- redrawLiveTile(true);
- }
- }
- }
-
- @Override
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
mActivity.getStateManager().goToState(NORMAL, false /* animate */);
@@ -184,65 +141,10 @@
}
@Override
- public void onTaskLaunched(Task task) {
- UserHandle user = UserHandle.of(task.key.userId);
- AppLaunchTracker.INSTANCE.get(getContext()).onStartApp(task.getTopComponent(), user,
- AppLaunchTracker.CONTAINER_OVERVIEW);
- }
-
- @Override
- public boolean shouldUseMultiWindowTaskSizeStrategy() {
- return TraceHelper.allowIpcs("isInMultiWindowMode", mActivity::isInMultiWindowMode);
- }
-
- @Override
- public void scrollTo(int x, int y) {
- super.scrollTo(x, y);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
- redrawLiveTile(true);
- }
- }
-
- @Override
- public TransformParams getLiveTileParams(
- boolean mightNeedToRefill) {
- if (!mEnableDrawingLiveTile || mRecentsAnimationController == null
- || mRecentsAnimationTargets == null) {
- return null;
- }
- TaskView taskView = getRunningTaskView();
- if (taskView != null) {
- taskView.getThumbnail().getGlobalVisibleRect(mTempRect);
- int offsetX = (int) (mTaskWidth * taskView.getScaleX() * getScaleX()
- - mTempRect.width());
- int offsetY = (int) (mTaskHeight * taskView.getScaleY() * getScaleY()
- - mTempRect.height());
- if (((mCurrentPage != 0) || mightNeedToRefill) && offsetX > 0) {
- if (mTempRect.left - offsetX < 0) {
- mTempRect.left -= offsetX;
- } else {
- mTempRect.right += offsetX;
- }
- }
- if (mightNeedToRefill && offsetY > 0) {
- mTempRect.top -= offsetY;
- }
- mTransformParams.setProgress(1f)
- .setTargetAlpha(taskView.getAlpha())
- .setSyncTransactionApplier(mSyncTransactionApplier)
- .setTargetSet(mRecentsAnimationTargets);
- }
- return mTransformParams;
- }
-
- @Override
public void reset() {
super.reset();
setLayoutRotation(Surface.ROTATION_0, Surface.ROTATION_0);
- // We are moving to home or some other UI with no recents. Switch back to the home client,
- // the home predictions should have been updated when the activity was resumed.
- PredictionUiStateManager.INSTANCE.get(getContext()).switchClient(Client.HOME);
}
@Override
@@ -258,6 +160,8 @@
reset();
}
setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
+ setOverviewGridEnabled(finalState.displayOverviewTasksAsGrid(mActivity));
+ setOverviewFullscreenEnabled(finalState.getOverviewFullscreenProgress() == 1);
setFreezeViewVisibility(false);
}
@@ -267,21 +171,15 @@
if (enabled) {
LauncherState state = mActivity.getStateManager().getState();
boolean hasClearAllButton = (state.getVisibleElements(mActivity)
- & OVERVIEW_BUTTONS) != 0;
+ & CLEAR_ALL_BUTTON) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
}
}
@Override
protected boolean shouldStealTouchFromSiblingsBelow(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // Allow touches to go through to the hotseat.
- Hotseat hotseat = mActivity.getHotseat();
- boolean touchingHotseat = hotseat.isShown()
- && mActivity.getDragLayer().isEventOverView(hotseat, ev, this);
- return !touchingHotseat;
- }
- return super.shouldStealTouchFromSiblingsBelow(ev);
+ return mActivity.getStateManager().getState().overviewUi
+ && super.shouldStealTouchFromSiblingsBelow(ev);
}
@Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
similarity index 85%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
rename to quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 1bf2fbf..1241982 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,9 +16,7 @@
package com.android.quickstep.views;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE;
-import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import android.content.Context;
import android.content.res.Configuration;
@@ -52,21 +50,15 @@
private final Rect mInsets = new Rect();
@IntDef(flag = true, value = {
- HIDDEN_UNSUPPORTED_NAVIGATION,
- HIDDEN_DISABLED_FEATURE,
HIDDEN_NON_ZERO_ROTATION,
HIDDEN_NO_TASKS,
- HIDDEN_GESTURE_RUNNING,
HIDDEN_NO_RECENTS})
@Retention(RetentionPolicy.SOURCE)
public @interface ActionsHiddenFlags { }
- public static final int HIDDEN_UNSUPPORTED_NAVIGATION = 1 << 0;
- public static final int HIDDEN_DISABLED_FEATURE = 1 << 1;
- public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 2;
- public static final int HIDDEN_NO_TASKS = 1 << 3;
- public static final int HIDDEN_GESTURE_RUNNING = 1 << 4;
- public static final int HIDDEN_NO_RECENTS = 1 << 5;
+ public static final int HIDDEN_NON_ZERO_ROTATION = 1 << 0;
+ public static final int HIDDEN_NO_TASKS = 1 << 1;
+ public static final int HIDDEN_NO_RECENTS = 1 << 2;
@IntDef(flag = true, value = {
DISABLED_SCROLLING,
@@ -116,7 +108,7 @@
findViewById(R.id.action_screenshot).setOnClickListener(this);
if (ENABLE_OVERVIEW_SHARE.get()) {
share.setVisibility(VISIBLE);
- findViewById(R.id.share_space).setVisibility(VISIBLE);
+ findViewById(R.id.oav_three_button_space).setVisibility(VISIBLE);
}
}
@@ -143,13 +135,6 @@
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- updateHiddenFlags(HIDDEN_DISABLED_FEATURE, !ENABLE_OVERVIEW_ACTIONS.get());
- updateHiddenFlags(HIDDEN_UNSUPPORTED_NAVIGATION, !removeShelfFromOverview(getContext()));
- }
-
- @Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
updateVerticalMargin(SysUINavigationMode.getMode(getContext()));
@@ -204,6 +189,14 @@
/** Updates vertical margins for different navigation mode or configuration changes. */
public void updateVerticalMargin(Mode mode) {
+ LayoutParams actionParams = (LayoutParams) findViewById(
+ R.id.action_buttons).getLayoutParams();
+ actionParams.setMargins(
+ actionParams.leftMargin, actionParams.topMargin, actionParams.rightMargin,
+ getBottomVerticalMargin(mode));
+ }
+
+ protected int getBottomVerticalMargin(Mode mode) {
int bottomMargin;
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
@@ -216,8 +209,6 @@
.getDimensionPixelSize(R.dimen.overview_actions_bottom_margin_gesture);
}
bottomMargin += mInsets.bottom;
- LayoutParams params = (LayoutParams) getLayoutParams();
- params.setMargins(
- params.leftMargin, params.topMargin, params.rightMargin, bottomMargin);
+ return bottomMargin;
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
similarity index 100%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsExtraViewContainer.java
rename to quickstep/src/com/android/quickstep/views/RecentsExtraViewContainer.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
similarity index 76%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
rename to quickstep/src/com/android/quickstep/views/RecentsView.java
index 23941fa..efe1f75 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
@@ -34,17 +35,14 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.launcher3.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.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
-import static com.android.quickstep.views.OverviewActionsView.HIDDEN_GESTURE_RUNNING;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
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;
@@ -53,6 +51,7 @@
import android.animation.LayoutTransition;
import android.animation.LayoutTransition.TransitionListener;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
@@ -73,6 +72,7 @@
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.FloatProperty;
+import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -89,6 +89,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.InvariantDeviceProfile;
@@ -99,26 +100,24 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.anim.PendingAnimation.EndState;
-import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.DynamicResource;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.OverScroller;
import com.android.launcher3.util.ResourceBasedOverride.Overrides;
import com.android.launcher3.util.Themes;
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.RecentsModel;
@@ -126,12 +125,12 @@
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
-import com.android.quickstep.TaskUtils;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
@@ -139,7 +138,6 @@
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.LauncherEventUtil;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -233,6 +231,7 @@
view.setScaleX(scale);
view.setScaleY(scale);
view.mLastComputedTaskPushOutDistance = null;
+ view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
view.updatePageOffsets();
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
}
@@ -243,13 +242,27 @@
}
};
- protected RecentsOrientedState mOrientationState;
+ public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
+ new FloatProperty<RecentsView>("recentsGrid") {
+ @Override
+ public void setValue(RecentsView view, float gridProgress) {
+ view.setGridProgress(gridProgress);
+ }
+
+ @Override
+ public Float get(RecentsView view) {
+ return view.mGridProgress;
+ }
+ };
+
+ protected final RecentsOrientedState mOrientationState;
protected final BaseActivityInterface mSizeStrategy;
protected RecentsAnimationController mRecentsAnimationController;
- protected RecentsAnimationTargets mRecentsAnimationTargets;
protected SurfaceTransactionApplier mSyncTransactionApplier;
protected int mTaskWidth;
protected int mTaskHeight;
+ protected final TransformParams mLiveTileParams = new TransformParams();
+ protected final TaskViewSimulator mLiveTileTaskViewSimulator;
protected final Rect mLastComputedTaskSize = new Rect();
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
protected Float mLastComputedTaskPushOutDistance = null;
@@ -267,6 +280,7 @@
private final float mFastFlingVelocity;
private final RecentsModel mModel;
private final int mTaskTopMargin;
+ private final int mRowSpacing;
private final ClearAllButton mClearAllButton;
private final Rect mClearAllButtonDeadZoneRect = new Rect();
private final Rect mTaskViewDeadZoneRect = new Rect();
@@ -285,9 +299,14 @@
protected boolean mDisallowScrollToClearAll;
private boolean mOverlayEnabled;
protected boolean mFreezeViewVisibility;
+ private boolean mOverviewGridEnabled;
+ private boolean mOverviewFullscreenEnabled;
private float mAdjacentPageOffset = 0;
private float mTaskViewsSecondaryTranslation = 0;
+ // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
+ private float mGridProgress = 0;
+ private boolean mShowAsGrid;
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
@@ -405,23 +424,23 @@
private boolean mShowEmptyMessage;
private OnEmptyMessageUpdatedListener mOnEmptyMessageUpdatedListener;
private Layout mEmptyTextLayout;
- private boolean mLiveTileOverlayAttached;
// Keeps track of the index where the first TaskView should be
private int mTaskViewStartIndex = 0;
private OverviewActionsView mActionsView;
- private BaseActivity.MultiWindowModeChangedListener mMultiWindowModeChangedListener =
- (inMultiWindowMode) -> {
- if (mOrientationState != null) {
+ private MultiWindowModeChangedListener mMultiWindowModeChangedListener =
+ new MultiWindowModeChangedListener() {
+ @Override
+ public void onMultiWindowModeChanged(boolean inMultiWindowMode) {
mOrientationState.setMultiWindowMode(inMultiWindowMode);
setLayoutRotation(mOrientationState.getTouchRotation(),
mOrientationState.getDisplayRotation());
updateChildTaskOrientations();
- }
- if (!inMultiWindowMode && mOverviewStateEnabled) {
- // TODO: Re-enable layout transitions for addition of the unpinned task
- reloadIfNeeded();
+ if (!inMultiWindowMode && mOverviewStateEnabled) {
+ // TODO: Re-enable layout transitions for addition of the unpinned task
+ reloadIfNeeded();
+ }
}
};
@@ -434,7 +453,8 @@
mActivity = BaseActivity.fromContext(context);
mOrientationState = new RecentsOrientedState(
context, mSizeStrategy, this::animateRecentsRotationInPlace);
- mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation());
+ final int rotation = mActivity.getDisplay().getRotation();
+ mOrientationState.setRecentsRotation(rotation);
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
@@ -451,6 +471,7 @@
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mTaskTopMargin = getResources()
.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ mRowSpacing = (int) getResources().getDimension(R.dimen.recents_row_spacing);
mSquaredTouchSlop = squaredTouchSlop(context);
mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -475,6 +496,16 @@
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
+
+ mLiveTileTaskViewSimulator = new TaskViewSimulator(getContext(), getSizeStrategy());
+ mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
+ mLiveTileTaskViewSimulator.setOrientationState(mOrientationState);
+ mLiveTileTaskViewSimulator.setDrawsBelowRecents(true);
+
+ mShowAsGrid =
+ mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get();
+ mActivity.addOnDeviceProfileChangeListener(newDp ->
+ mShowAsGrid = newDp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
}
public OverScroller getScroller() {
@@ -533,6 +564,9 @@
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
+ if (visibility != VISIBLE && LIVE_TILE.get()) {
+ finishRecentsAnimation(true /* toRecents */, null);
+ }
updateTaskStackListenerState();
}
@@ -559,6 +593,7 @@
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
+ mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIdp.addOnChangeListener(this);
mIPinnedStackAnimationListener.setActivity(mActivity);
@@ -577,6 +612,7 @@
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = null;
+ mLiveTileParams.setSyncTransactionApplier(null);
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
@@ -631,15 +667,31 @@
}
public boolean isTaskViewVisible(TaskView tv) {
- // For now, just check if it's the active task or an adjacent task
- return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
+ if (mShowAsGrid) {
+ int screenStart = mOrientationHandler.getPrimaryScroll(this);
+ int screenEnd = screenStart + mOrientationHandler.getMeasuredSize(this);
+ return isTaskViewWithinBounds(tv, screenStart, screenEnd);
+ } else {
+ // For now, just check if it's the active task or an adjacent task
+ return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
+ }
+ }
+
+ private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) {
+ int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
+ mOverviewFullscreenEnabled, mOverviewGridEnabled);
+ int taskSize = (int) (mOrientationHandler.getMeasuredSize(tv) * tv.getSizeAdjustment(
+ mOverviewFullscreenEnabled, mOverviewGridEnabled));
+ int taskEnd = taskStart + taskSize;
+ return (taskStart >= start && taskStart <= end) || (taskEnd >= start
+ && taskEnd <= end);
}
public TaskView getTaskView(int taskId) {
for (int i = 0; i < getTaskViewCount(); i++) {
- TaskView tv = getTaskViewAt(i);
- if (tv.getTask() != null && tv.getTask().key != null && tv.getTask().key.id == taskId) {
- return tv;
+ TaskView taskView = getTaskViewAt(i);
+ if (taskView.hasTaskId(taskId)) {
+ return taskView;
}
}
return null;
@@ -659,9 +711,6 @@
public void onDigitalWellbeingToastShown() {
if (!mDwbToastShown) {
mDwbToastShown = true;
- mActivity.getUserEventDispatcher().logActionTip(
- LauncherEventUtil.VISIBLE,
- LauncherLogProto.TipType.DWB_TOAST);
}
}
@@ -697,10 +746,21 @@
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
- TaskView taskView = getCurrentPageTaskView();
- if (taskView != null && taskView.offerTouchToChildren(ev)) {
- // Keep consuming events to pass to delegate
- return true;
+ if (mShowAsGrid) {
+ int taskCount = getTaskViewCount();
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ if (isTaskViewVisible(taskView) && taskView.offerTouchToChildren(ev)) {
+ // Keep consuming events to pass to delegate
+ return true;
+ }
+ }
+ } else {
+ TaskView taskView = getCurrentPageTaskView();
+ if (taskView != null && taskView.offerTouchToChildren(ev)) {
+ // Keep consuming events to pass to delegate
+ return true;
+ }
}
final int x = (int) ev.getX();
@@ -752,6 +812,11 @@
}
@Override
+ protected boolean snapToPageInFreeScroll() {
+ return !mShowAsGrid;
+ }
+
+ @Override
protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
// Enables swiping to the left or right only if the task overlay is not modal.
if (!isModal()) {
@@ -763,8 +828,14 @@
}
protected void applyLoadPlan(ArrayList<Task> tasks) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "applyLoadPlan: taskCount=" + tasks.size());
+ for (Task t : tasks) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "\t" + t);
+ }
+ }
if (mPendingAnimation != null) {
- mPendingAnimation.addEndListener((endState) -> applyLoadPlan(tasks));
+ mPendingAnimation.addEndListener(success -> applyLoadPlan(tasks));
return;
}
@@ -803,6 +874,7 @@
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task, mOrientationState);
}
+ updateTaskSize();
if (mNextPage == INVALID_PAGE) {
// Set the current page to the running task, but not if settling on new task.
@@ -823,12 +895,21 @@
resetTaskVisuals();
onTaskStackUpdated();
updateEnabledOverlays();
+
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "applyLoadPlan: taskViewCount="
+ + getTaskViewCount());
+ }
}
private boolean isModal() {
return mTaskModalness > 0;
}
+ public boolean isLoadingTasks() {
+ return mModel.isLoadingTasksInBackground();
+ }
+
private void removeTasksViewsAndClearAllButton() {
for (int i = getTaskViewCount() - 1; i >= 0; i--) {
removeView(getTaskViewAt(i));
@@ -839,6 +920,12 @@
}
public int getTaskViewCount() {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getTaskViewCount:"
+ + " numChildren=" + getChildCount()
+ + " start=" + mTaskViewStartIndex
+ + " clearAll=" + indexOfChild(mClearAllButton));
+ }
int taskViewCount = getChildCount() - mTaskViewStartIndex;
if (indexOfChild(mClearAllButton) != -1) {
taskViewCount--;
@@ -861,6 +948,15 @@
taskView.setModalness(mTaskModalness);
}
}
+ if (LIVE_TILE.get()) {
+ // Since we reuse the same mLiveTileTaskViewSimulator in the RecentsView, we need
+ // to reset the params after it settles in Overview from swipe up so that we don't
+ // render with obsolete param values.
+ mLiveTileTaskViewSimulator.taskPrimaryTranslation.value = 0;
+ mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = 0;
+ mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
+ mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
+ }
if (mRunningTaskTileHidden) {
setRunningTaskHidden(mRunningTaskTileHidden);
}
@@ -882,6 +978,7 @@
for (int i = 0; i < taskCount; i++) {
getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
}
+
// Fade out the actions view quickly (0.1 range)
mActionsView.getFullscreenAlpha().setValue(
mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
@@ -902,6 +999,7 @@
public void setInsets(Rect insets) {
mInsets.set(insets);
resetPaddingFromTaskSize();
+ mLiveTileTaskViewSimulator.setDp(mActivity.getDeviceProfile());
}
private void resetPaddingFromTaskSize() {
@@ -914,6 +1012,23 @@
setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
dp.widthPx - mInsets.right - mTempRect.right,
dp.heightPx - mInsets.bottom - mTempRect.bottom);
+ // Force TaskView to update size from thumbnail
+ updateTaskSize();
+ }
+
+ /**
+ * Updates TaskView scaling and translation required to support variable width.
+ */
+ private void updateTaskSize() {
+ float accumulatedTranslationX = 0;
+ final int taskCount = getTaskViewCount();
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ taskView.updateTaskSize();
+ taskView.setAccumulatedFullscreenTranslationX(accumulatedTranslationX);
+ accumulatedTranslationX += taskView.getFullscreenTranslationX();
+ }
+ updateGridProperties();
}
public void getTaskSize(Rect outRect) {
@@ -922,6 +1037,11 @@
mLastComputedTaskSize.set(outRect);
}
+ /** Gets the last computed task size */
+ public Rect getLastComputedTaskSize() {
+ return mLastComputedTaskSize;
+ }
+
/** Gets the task size for modal state. */
public void getModalTaskSize(Rect outRect) {
mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
@@ -944,6 +1064,12 @@
// Update the high res thumbnail loader state
mModel.getThumbnailCache().getHighResLoadingState().setFlingingFast(isFlingingFast);
+
+ mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
+ if (LIVE_TILE.get() && mEnableDrawingLiveTile
+ && mLiveTileParams.getTargetSet() != null) {
+ redrawLiveTile();
+ }
return scrolling;
}
@@ -961,12 +1087,37 @@
final int pageCount = getPageCount();
for (int i = 0; i < pageCount; i++) {
View page = getPageAt(i);
- mScrollState.updateInterpolation(
+ mScrollState.updateInterpolation(mActivity.getDeviceProfile(),
mOrientationHandler.getChildStartWithTranslation(page));
((PageCallbacks) page).onPageScroll(mScrollState);
}
}
+ @Override
+ protected int getDestinationPage(int scaledScroll) {
+ if (mGridProgress == 0) {
+ return super.getDestinationPage(scaledScroll);
+ }
+
+ final int childCount = getChildCount();
+ if (mPageScrolls == null || childCount != mPageScrolls.length) {
+ return -1;
+ }
+
+ // When in grid, return the page which scroll is closest to screenStart instead of page
+ // nearest to center of screen.
+ int minDistanceFromScreenStart = Integer.MAX_VALUE;
+ int minDistanceFromScreenStartIndex = -1;
+ for (int i = 0; i < childCount; ++i) {
+ int distanceFromScreenStart = Math.abs(mPageScrolls[i] - scaledScroll);
+ if (distanceFromScreenStart < minDistanceFromScreenStart) {
+ minDistanceFromScreenStart = distanceFromScreenStart;
+ minDistanceFromScreenStartIndex = i;
+ }
+ }
+ return minDistanceFromScreenStartIndex;
+ }
+
/**
* Iterates through all the tasks, and loads the associated task data for newly visible tasks,
* and unloads the associated task data for tasks that are no longer visible.
@@ -978,17 +1129,35 @@
return;
}
- int centerPageIndex = getPageNearestToCenterOfScreen();
- int numChildren = getChildCount();
- int lower = Math.max(0, centerPageIndex - 2);
- int upper = Math.min(centerPageIndex + 2, numChildren - 1);
+ int lower = 0;
+ int upper = 0;
+ int visibleStart = 0;
+ int visibleEnd = 0;
+ if (mShowAsGrid) {
+ int screenStart = mOrientationHandler.getPrimaryScroll(this);
+ int pageOrientedSize = mOrientationHandler.getMeasuredSize(this);
+ int halfScreenSize = pageOrientedSize / 2;
+ // Use +/- 50% screen width as visible area.
+ visibleStart = screenStart - halfScreenSize;
+ visibleEnd = screenStart + pageOrientedSize + halfScreenSize;
+ } else {
+ int centerPageIndex = getPageNearestToCenterOfScreen();
+ int numChildren = getChildCount();
+ lower = Math.max(0, centerPageIndex - 2);
+ upper = Math.min(centerPageIndex + 2, numChildren - 1);
+ }
// Update the task data for the in/visible children
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = getTaskViewAt(i);
Task task = taskView.getTask();
int index = indexOfChild(taskView);
- boolean visible = lower <= index && index <= upper;
+ boolean visible;
+ if (mShowAsGrid) {
+ visible = isTaskViewWithinBounds(taskView, visibleStart, visibleEnd);
+ } else {
+ visible = lower <= index && index <= upper;
+ }
if (visible) {
if (task == mTmpRunningTask) {
// Skip loading if this is the task that we are animating into
@@ -1051,7 +1220,7 @@
mTaskListChangeId = -1;
mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
+ mLiveTileParams.setTargetSet(null);
unloadVisibleTaskData();
setCurrentPage(0);
@@ -1063,6 +1232,10 @@
}
}
+ public int getRunningTaskId() {
+ return mRunningTaskId;
+ }
+
public @Nullable TaskView getRunningTaskView() {
return getTaskView(mRunningTaskId);
}
@@ -1107,7 +1280,6 @@
setEnableDrawingLiveTile(false);
setRunningTaskHidden(true);
setRunningTaskIconScaledDown(true);
- mActionsView.updateHiddenFlags(HIDDEN_GESTURE_RUNNING, true);
}
/**
@@ -1116,10 +1288,7 @@
*/
public void onSwipeUpAnimationSuccess() {
if (getRunningTaskView() != null) {
- float startProgress = ENABLE_QUICKSTEP_LIVE_TILE.get() && mLiveTileOverlayAttached
- ? LiveTileOverlay.INSTANCE.cancelIconAnimation()
- : 0f;
- animateUpRunningTaskIconScale(startProgress);
+ animateUpRunningTaskIconScale(0f);
}
setSwipeDownShouldLaunchApp(true);
}
@@ -1161,7 +1330,14 @@
}
/**
- * Called when a gesture from an app has finished.
+ * Called when a gesture from an app has finished, and an end target has been determined.
+ */
+ public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+
+ }
+
+ /**
+ * Called when a gesture from an app has finished, and the animation to the target has ended.
*/
public void onGestureAnimationEnd() {
if (mOrientationState.setGestureActive(false)) {
@@ -1171,18 +1347,22 @@
setOnScrollChangeListener(null);
setEnableFreeScroll(true);
setEnableDrawingLiveTile(true);
- if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (!LIVE_TILE.get()) {
setRunningTaskViewShowScreenshot(true);
}
setRunningTaskHidden(false);
animateUpRunningTaskIconScale();
- animateActionsViewIn();
+
+ // TODO: This should be tied to whether there is a focus app on overview.
+ if (!mShowAsGrid) {
+ animateActionsViewIn();
+ }
}
/**
- * Returns true if we should add a dummy taskView for the running task id
+ * Returns true if we should add a stub taskView for the running task id
*/
- protected boolean shouldAddDummyTaskView(RunningTaskInfo runningTaskInfo) {
+ protected boolean shouldAddStubTaskView(RunningTaskInfo runningTaskInfo) {
return runningTaskInfo != null && getTaskView(runningTaskInfo.taskId) == null;
}
@@ -1193,7 +1373,7 @@
* is called. Also scrolls the view to this task.
*/
public void showCurrentTask(RunningTaskInfo runningTaskInfo) {
- if (shouldAddDummyTaskView(runningTaskInfo)) {
+ if (shouldAddStubTaskView(runningTaskInfo)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView = mTaskViewPool.getView();
@@ -1218,6 +1398,8 @@
setCurrentPage(getRunningTaskIndex());
setRunningTaskViewShowScreenshot(false);
setRunningTaskHidden(runningTaskTileHidden);
+ // Update task size after setting current task.
+ updateTaskSize();
// Reload the task list
mTaskListChangeId = mModel.getTasks(this::applyLoadPlan);
@@ -1257,7 +1439,7 @@
}
private void setRunningTaskViewShowScreenshot(boolean showScreenshot) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (LIVE_TILE.get()) {
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
runningTaskView.setShowScreenshot(showScreenshot);
@@ -1266,19 +1448,26 @@
}
public void showNextTask() {
- TaskView runningTaskView = getRunningTaskView();
+ final TaskView runningTaskView = getRunningTaskView();
+ final TaskView targetTask;
+
if (runningTaskView == null) {
// Launch the first task
if (getTaskViewCount() > 0) {
- getTaskViewAt(0).launchTask(true);
+ targetTask = getTaskViewAt(0);
+ } else {
+ return;
}
} else {
- if (getNextTaskView() != null) {
- getNextTaskView().launchTask(true);
+ final TaskView nextTask = getNextTaskView();
+ if (nextTask != null) {
+ targetTask = nextTask;
} else {
- runningTaskView.launchTask(true);
+ targetTask = runningTaskView;
}
}
+ targetTask.setEndQuickswitchCuj(true);
+ targetTask.launchTask(true);
}
public void setRunningTaskIconScaledDown(boolean isScaledDown) {
@@ -1300,7 +1489,6 @@
}
private void animateActionsViewIn() {
- mActionsView.updateHiddenFlags(HIDDEN_GESTURE_RUNNING, false);
ObjectAnimator anim = ObjectAnimator.ofFloat(
mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 0, 1);
anim.setDuration(TaskView.SCALE_ICON_DURATION);
@@ -1320,6 +1508,166 @@
}
}
+ /**
+ * Updates TaskView and ClearAllButton scaling and translation required to turn into grid
+ * layout.
+ * This method only calculates the potential position and depends on {@link #setGridProgress} to
+ * apply the actual scaling and translation.
+ */
+ private void updateGridProperties() {
+ int taskCount = getTaskViewCount();
+ if (taskCount == 0) {
+ return;
+ }
+
+ final int boxLength = Math.max(mTaskWidth, mTaskHeight);
+
+ float availableHeight =
+ mTaskTopMargin + mTaskHeight + mSizeStrategy.getOverviewActionsHeight(mContext);
+ float rowHeight = (availableHeight - mRowSpacing) / 2;
+ float gridScale = rowHeight / (boxLength + mTaskTopMargin);
+
+ TaskView firstTask = getTaskViewAt(0);
+ float firstTaskWidthOffset;
+ if (mIsRtl) {
+ // Move the first task to the right edge.
+ firstTaskWidthOffset = mTaskWidth - firstTask.getLayoutParams().width * gridScale;
+ } else {
+ // Move the first task to the left edge.
+ firstTaskWidthOffset = -firstTask.getLayoutParams().width * (1 - gridScale);
+ }
+
+ int topRowWidth = 0;
+ int bottomRowWidth = 0;
+ float topAccumulatedTranslationX = 0;
+ float bottomAccumulatedTranslationX = 0;
+ IntSet topSet = new IntSet();
+ float[] gridTranslations = new float[taskCount];
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ taskView.setGridScale(gridScale);
+
+ float taskWidthDiff = mTaskWidth - taskView.getLayoutParams().width * gridScale;
+ float taskWidthOffset = mIsRtl ? taskWidthDiff : -taskWidthDiff;
+ // Visually we want to move all task by firstTaskWidthOffset, but calculate page scroll
+ // according to right edge (or left in nonRtl) of TaskView.
+ gridTranslations[i] = firstTaskWidthOffset - taskWidthOffset;
+ taskView.setGridOffsetTranslationX(taskWidthOffset);
+
+ // Off-set gap due to task scaling.
+ float widthDiff = taskView.getLayoutParams().width * (1 - gridScale);
+ float gridScaleTranslationX = mIsRtl ? widthDiff : -widthDiff;
+ gridTranslations[i] += gridScaleTranslationX;
+
+ // Visible offset caused by having scaling pivot on top-right.
+ taskView.setNonRtlVisibleOffset(mIsRtl ? 0 : widthDiff);
+
+ if (topRowWidth <= bottomRowWidth) {
+ gridTranslations[i] += topAccumulatedTranslationX;
+ topRowWidth += taskView.getLayoutParams().width * gridScale + mPageSpacing;
+ topSet.add(i);
+
+ taskView.setGridTranslationY(0);
+
+ // Move horizontally into empty space.
+ float widthOffset = 0;
+ for (int j = i - 1; !topSet.contains(j) && j >= 0; j--) {
+ widthOffset += getTaskViewAt(j).getLayoutParams().width * gridScale
+ + mPageSpacing;
+ }
+
+ float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
+ gridTranslations[i] += gridTranslationX;
+ topAccumulatedTranslationX += gridTranslationX + gridScaleTranslationX;
+ bottomAccumulatedTranslationX += gridScaleTranslationX;
+ } else {
+ gridTranslations[i] += bottomAccumulatedTranslationX;
+ bottomRowWidth += taskView.getLayoutParams().width * gridScale + mPageSpacing;
+
+ // Move into bottom row.
+ float heightOffset = (boxLength + mTaskTopMargin) * gridScale + mRowSpacing;
+ taskView.setGridTranslationY(heightOffset);
+
+ // Move horizontally into empty space.
+ float widthOffset = 0;
+ for (int j = i - 1; topSet.contains(j); j--) {
+ widthOffset += getTaskViewAt(j).getLayoutParams().width * gridScale
+ + mPageSpacing;
+ }
+
+ float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
+ gridTranslations[i] += gridTranslationX;
+ topAccumulatedTranslationX += gridScaleTranslationX;
+ bottomAccumulatedTranslationX += gridTranslationX + gridScaleTranslationX;
+ }
+ }
+
+ // Use the accumulated translation of the longer row.
+ float clearAllAccumulatedTranslation = mIsRtl ? Math.max(topAccumulatedTranslationX,
+ bottomAccumulatedTranslationX) : Math.min(topAccumulatedTranslationX,
+ bottomAccumulatedTranslationX);
+
+ // If the last task is on the shorter row, ClearAllButton will embed into the shorter row
+ // which is not what we want. Compensate the width difference of the 2 rows in that case.
+ float shorterRowCompensation = 0;
+ if (topRowWidth <= bottomRowWidth) {
+ if (topSet.contains(taskCount - 1)) {
+ shorterRowCompensation = bottomRowWidth - topRowWidth;
+ }
+ } else {
+ if (!topSet.contains(taskCount - 1)) {
+ shorterRowCompensation = topRowWidth - bottomRowWidth;
+ }
+ }
+ float clearAllShorterRowCompensation =
+ mIsRtl ? -shorterRowCompensation : shorterRowCompensation;
+
+ // If the total width is shorter than one task's width, move ClearAllButton further away
+ // accordingly.
+ float clearAllShortTotalCompensation = 0;
+ float longRowWidth = Math.max(topRowWidth, bottomRowWidth);
+ if (longRowWidth < mTaskWidth) {
+ float shortTotalCompensation = mTaskWidth - longRowWidth;
+ clearAllShortTotalCompensation =
+ mIsRtl ? -shortTotalCompensation : shortTotalCompensation;
+ }
+
+ float clearAllTotalTranslationX = firstTaskWidthOffset + clearAllAccumulatedTranslation
+ + clearAllShorterRowCompensation + clearAllShortTotalCompensation;
+
+ // We need to maintain first task's grid translation at 0, now shift translation of all
+ // the TaskViews to achieve that.
+ for (int i = 0; i < taskCount; i++) {
+ getTaskViewAt(i).setGridTranslationX(gridTranslations[i] - gridTranslations[0]);
+ }
+ mClearAllButton.setGridTranslationPrimary(clearAllTotalTranslationX - gridTranslations[0]);
+
+ setGridProgress(mGridProgress);
+ }
+
+ /**
+ * Moves TaskView and ClearAllButton between carousel and 2 row grid.
+ *
+ * @param gridProgress 0 = carousel; 1 = 2 row grid.
+ */
+ public void setGridProgress(float gridProgress) {
+ int taskCount = getTaskViewCount();
+ if (taskCount == 0) {
+ return;
+ }
+
+ if (!mShowAsGrid) {
+ gridProgress = 0;
+ }
+
+ mGridProgress = gridProgress;
+
+ for (int i = 0; i < taskCount; i++) {
+ getTaskViewAt(i).setGridProgress(gridProgress);
+ }
+ mClearAllButton.setGridProgress(gridProgress);
+ }
+
private void enableLayoutTransitions() {
if (mLayoutTransition == null) {
mLayoutTransition = new LayoutTransition();
@@ -1384,13 +1732,13 @@
/**
* Updates linearInterpolation for the provided child position
*/
- public void updateInterpolation(float childStart) {
+ public void updateInterpolation(DeviceProfile deviceProfile, float childStart) {
float pageCenter = childStart + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
// How far the page has to move from the center to be offscreen, taking into account
// the EDGE_SCALE_DOWN_FACTOR that will be applied at that position.
float distanceToReachEdge = halfScreenSize
- + halfPageSize * (1 - TaskView.EDGE_SCALE_DOWN_FACTOR);
+ + halfPageSize * (1 - TaskView.getEdgeScaleDownFactor(deviceProfile));
linearInterpolation = Math.min(1,
Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
}
@@ -1406,12 +1754,13 @@
}
}
- private void addDismissedTaskAnimations(View taskView, long duration, PendingAnimation anim) {
+ private void addDismissedTaskAnimations(TaskView taskView, long duration,
+ PendingAnimation anim) {
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
- FloatProperty<View> secondaryViewTranslate =
- mOrientationHandler.getSecondaryViewTranslate();
+ FloatProperty<TaskView> secondaryViewTranslate =
+ taskView.getDismissTaskTranslationProperty();
int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
@@ -1424,21 +1773,10 @@
verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
}
- private void removeTask(TaskView taskView, int index, EndState endState) {
- if (taskView.getTask() != null) {
- ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
- ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
- mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- endState.logAction, Direction.UP, index, compKey);
- mActivity.getStatsLogManager().logger().withItemInfo(taskView.getItemInfo())
- .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
- }
- }
-
public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
boolean shouldRemoveTask, long duration) {
if (mPendingAnimation != null) {
- mPendingAnimation.finish(false, Touch.SWIPE);
+ mPendingAnimation.createPlaybackController().dispatchOnCancel();
}
PendingAnimation anim = new PendingAnimation(duration);
@@ -1465,7 +1803,7 @@
if (animateTaskView) {
addDismissedTaskAnimations(taskView, duration, anim);
}
- } else {
+ } else if (!mShowAsGrid) { // Don't animate other tasks when dismissing in grid for now
// If we just take newScroll - oldScroll, everything to the right of dragged task
// translates to the left. We need to offset this in some cases:
// - In RTL, add page offset to all pages, since we want pages to move to the right
@@ -1488,7 +1826,7 @@
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
FloatProperty translationProperty = child instanceof TaskView
- ? ((TaskView) child).getPrimaryFillDismissGapTranslationProperty()
+ ? ((TaskView) child).getFillDismissGapTranslationProperty()
: mOrientationHandler.getPrimaryViewTranslate();
ResourceProvider rp = DynamicResource.provider(mActivity);
@@ -1507,28 +1845,43 @@
anim.addOnFrameCallback(this::updateCurveProperties);
}
+ if (LIVE_TILE.get() && getRunningTaskView() == taskView) {
+ anim.addOnFrameCallback(() -> {
+ mLiveTileTaskViewSimulator.taskSecondaryTranslation.value =
+ mOrientationHandler.getSecondaryValue(
+ taskView.getTranslationX(),
+ taskView.getTranslationY());
+ redrawLiveTile();
+ });
+ }
+
// Add a tiny bit of translation Z, so that it draws on top of other views
if (animateTaskView) {
taskView.setTranslationZ(0.1f);
}
mPendingAnimation = anim;
- mPendingAnimation.addEndListener(new Consumer<EndState>() {
+ mPendingAnimation.addEndListener(new Consumer<Boolean>() {
@Override
- public void accept(EndState endState) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
- taskView.isRunningTask() && endState.isSuccess) {
- finishRecentsAnimation(true /* toHome */, () -> onEnd(endState));
+ public void accept(Boolean success) {
+ if (LIVE_TILE.get() && taskView.isRunningTask() && success) {
+ finishRecentsAnimation(true /* toHome */, () -> onEnd(success));
} else {
- onEnd(endState);
+ onEnd(success);
}
}
@SuppressWarnings("WrongCall")
- private void onEnd(EndState endState) {
- if (endState.isSuccess) {
+ private void onEnd(boolean success) {
+ if (success) {
if (shouldRemoveTask) {
- removeTask(taskView, draggedIndex, endState);
+ if (taskView.getTask() != null) {
+ UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance()
+ .removeTask(taskView.getTask().key.id));
+ mActivity.getStatsLogManager().logger()
+ .withItemInfo(taskView.getItemInfo())
+ .log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
+ }
}
int pageToSnapTo = mCurrentPage;
@@ -1543,6 +1896,8 @@
startHome();
} else {
snapToPageImmediately(pageToSnapTo);
+ // Grid got messed up, reapply.
+ updateGridProperties();
}
// Update the layout synchronously so that the position of next view is
// immediately available.
@@ -1567,10 +1922,11 @@
}
mPendingAnimation = anim;
- mPendingAnimation.addEndListener((endState) -> {
- if (endState.isSuccess) {
+ mPendingAnimation.addEndListener(isSuccess -> {
+ if (isSuccess) {
// Remove all the task views now
- ActivityManagerWrapper.getInstance().removeAllRecentTasks();
+ UI_HELPER_EXECUTOR.execute(
+ ActivityManagerWrapper.getInstance()::removeAllRecentTasks);
removeTasksViewsAndClearAllButton();
startHome();
}
@@ -1595,7 +1951,6 @@
protected void runDismissAnimation(PendingAnimation pendingAnim) {
AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
controller.dispatchOnStart();
- controller.setEndAction(() -> pendingAnim.finish(true, Touch.SWIPE));
controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
controller.start();
}
@@ -1608,7 +1963,7 @@
@SuppressWarnings("unused")
private void dismissAllTasks(View view) {
runDismissAnimation(createAllTasksDismissAnimation(DISMISS_TASK_DURATION));
- mActivity.getUserEventDispatcher().logActionOnControl(TAP, CLEAR_ALL_BUTTON);
+ mActivity.getStatsLogManager().logger().log(LAUNCHER_TASK_CLEAR_ALL);
}
private void dismissCurrentTask() {
@@ -1687,7 +2042,7 @@
if (alpha > 0) {
setVisibility(VISIBLE);
} else if (!mFreezeViewVisibility) {
- setVisibility(GONE);
+ setVisibility(INVISIBLE);
}
}
@@ -1699,7 +2054,7 @@
if (mFreezeViewVisibility != freezeViewVisibility) {
mFreezeViewVisibility = freezeViewVisibility;
if (!mFreezeViewVisibility) {
- setVisibility(mContentAlpha > 0 ? VISIBLE : GONE);
+ setVisibility(mContentAlpha > 0 ? VISIBLE : INVISIBLE);
}
}
}
@@ -1718,9 +2073,11 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation())) {
+ final int rotation = mActivity.getDisplay().getRotation();
+ if (mOrientationState.setRecentsRotation(rotation)) {
updateOrientationHandler();
}
+
// If overview is in modal state when rotate, reset it to overview state without running
// animation.
if (mActivity.isInState(OVERVIEW_MODAL_TASK)) {
@@ -1924,10 +2281,6 @@
centerToOffscreenProgress = Utilities.mapRange(centerToOffscreenProgress,
distanceFromMidpoint / distanceToOffscreen, 1);
}
- // Find the task's scale based on its offscreen progress, then see how far it still needs to
- // move to be completely offscreen.
- Utilities.scaleRectFAboutCenter(taskPosition,
- TaskView.getCurveScaleForInterpolation(centerToOffscreenProgress));
distanceToOffscreen = desiredLeft - taskPosition.left;
// Finally, we need to account for RecentsView scale, because it moves tasks based on its
// pivot. To do this, we move the task position to where it would be offscreen at scale = 1
@@ -1946,8 +2299,9 @@
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView task = getTaskViewAt(i);
- mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
+ task.getTaskResistanceTranslationProperty().set(task, translation / getScaleY());
}
+ mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
}
/**
@@ -2046,22 +2400,35 @@
boolean launchingCenterTask = taskIndex == centerTaskIndex;
float toScale = getMaxScaleForFullScreen();
+ RecentsView recentsView = tv.getRecentsView();
if (launchingCenterTask) {
- RecentsView recentsView = tv.getRecentsView();
anim.play(ObjectAnimator.ofFloat(recentsView, RECENTS_SCALE_PROPERTY, toScale));
anim.play(ObjectAnimator.ofFloat(recentsView, FULLSCREEN_PROGRESS, 1));
} else {
// We are launching an adjacent task, so parallax the center and other adjacent task.
- float displacementX = tv.getWidth() * (toScale - tv.getCurveScale());
- anim.play(ObjectAnimator.ofFloat(getPageAt(centerTaskIndex), TRANSLATION_X,
- mIsRtl ? -displacementX : displacementX));
+ 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 (LIVE_TILE.get() && runningTaskIndex != -1
+ && runningTaskIndex != taskIndex) {
+ anim.play(ObjectAnimator.ofFloat(
+ recentsView.getLiveTileTaskViewSimulator().taskPrimaryTranslation,
+ AnimatedFloat.VALUE,
+ primaryTranslation));
+ }
int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < getPageCount()) {
- anim.play(new PropertyListBuilder()
- .translationX(mIsRtl ? -displacementX : displacementX)
- .scale(1)
- .build(getPageAt(otherAdjacentTaskIndex)));
+ PropertyValuesHolder[] properties = new PropertyValuesHolder[3];
+ properties[0] = PropertyValuesHolder.ofFloat(
+ mOrientationHandler.getPrimaryViewTranslate(), primaryTranslation);
+ properties[1] = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+ properties[2] = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
+
+ anim.play(ObjectAnimator.ofPropertyValuesHolder(getPageAt(otherAdjacentTaskIndex),
+ properties));
}
}
return anim;
@@ -2098,8 +2465,6 @@
? targetSysUiFlags
: 0);
- onTaskLaunchAnimationUpdate(animator.getAnimatedFraction(), tv);
-
// Passing the threshold from taskview to fullscreen app will vibrate
final boolean passed = animator.getAnimatedFraction() >=
SUCCESS_TRANSITION_PROGRESS;
@@ -2123,20 +2488,26 @@
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
- mPendingAnimation.addEndListener((endState) -> {
- if (endState.isSuccess) {
+ if (LIVE_TILE.get()) {
+ mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
+ mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
+ }
+ mPendingAnimation.addEndListener(isSuccess -> {
+ if (isSuccess) {
Consumer<Boolean> onLaunchResult = (result) -> {
onTaskLaunchAnimationEnd(result);
if (!result) {
tv.notifyTaskLaunchFailed(TAG);
}
};
- tv.launchTask(false, onLaunchResult, getHandler());
+ if (LIVE_TILE.get()) {
+ finishRecentsAnimation(false /* toRecents */, null);
+ onLaunchResult.accept(true /* success */);
+ } else {
+ tv.launchTask(false, onLaunchResult, getHandler());
+ }
Task task = tv.getTask();
if (task != null) {
- mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- endState.logAction, Direction.DOWN, indexOfChild(tv),
- TaskUtils.getLaunchComponentKeyForTask(task.key));
mActivity.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
}
@@ -2148,22 +2519,12 @@
return mPendingAnimation;
}
- protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
- }
-
- public abstract boolean shouldUseMultiWindowTaskSizeStrategy();
-
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
}
}
- /**
- * Called when task activity is launched
- */
- public void onTaskLaunched(Task task){ }
-
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
@@ -2224,22 +2585,28 @@
mEnableDrawingLiveTile = enableDrawingLiveTile;
}
- public void redrawLiveTile(boolean mightNeedToRefill) { }
+ public void redrawLiveTile() {
+ if (mLiveTileParams.getTargetSet() != null) {
+ mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+ }
+ }
+
+ public TaskViewSimulator getLiveTileTaskViewSimulator() {
+ return mLiveTileTaskViewSimulator;
+ }
+
+ public TransformParams getLiveTileParams() {
+ return mLiveTileParams;
+ }
// TODO: To be removed in a follow up CL
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) {
mRecentsAnimationController = recentsAnimationController;
- mRecentsAnimationTargets = recentsAnimationTargets;
- }
-
- public void setLiveTileOverlayAttached(boolean liveTileOverlayAttached) {
- mLiveTileOverlayAttached = liveTileOverlayAttached;
- }
-
- public void updateLiveTileIcon(Drawable icon) {
- if (mLiveTileOverlayAttached) {
- LiveTileOverlay.INSTANCE.setIcon(icon);
+ if (recentsAnimationTargets != null && recentsAnimationTargets.apps.length > 0) {
+ mLiveTileTaskViewSimulator.setPreview(
+ recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
+ mLiveTileParams.setTargetSet(recentsAnimationTargets);
}
}
@@ -2254,13 +2621,13 @@
mRecentsAnimationController.finish(toRecents, () -> {
if (onFinishComplete != null) {
onFinishComplete.run();
- // After we finish the recents animation, the current task id should be correctly
- // reset so that when the task is launched from Overview later, it goes through the
- // flow of starting a new task instead of finishing recents animation to app. A
- // typical example of this is (1) user swipes up from app to Overview (2) user
- // taps on QSB (3) user goes back to Overview and launch the most recent task.
- setCurrentTask(-1);
}
+ // After we finish the recents animation, the current task id should be correctly
+ // reset so that when the task is launched from Overview later, it goes through the
+ // flow of starting a new task instead of finishing recents animation to app. A
+ // typical example of this is (1) user swipes up from app to Overview (2) user
+ // taps on QSB (3) user goes back to Overview and launch the most recent task.
+ setCurrentTask(-1);
});
}
@@ -2291,6 +2658,54 @@
}
@Override
+ protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
+ ComputePageScrollsLogic scrollLogic) {
+ boolean pageScrollChanged = super.getPageScrolls(outPageScrolls, layoutChildren,
+ scrollLogic);
+
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ float scrollDiff = 0;
+ if (child instanceof TaskView) {
+ scrollDiff = ((TaskView) child).getScrollAdjustment(mOverviewFullscreenEnabled,
+ mOverviewGridEnabled);
+ } else if (child instanceof ClearAllButton) {
+ scrollDiff = ((ClearAllButton) child).getScrollAdjustment(mOverviewGridEnabled);
+ }
+
+ if (scrollDiff != 0) {
+ outPageScrolls[i] += scrollDiff;
+ pageScrollChanged = true;
+ }
+ }
+ return pageScrollChanged;
+ }
+
+ @Override
+ protected int getChildOffset(int index) {
+ int childOffset = super.getChildOffset(index);
+ View child = getChildAt(index);
+ if (child instanceof TaskView) {
+ childOffset += ((TaskView) child).getOffsetAdjustment(mOverviewFullscreenEnabled,
+ mOverviewGridEnabled);
+ } else if (child instanceof ClearAllButton) {
+ childOffset += ((ClearAllButton) child).getOffsetAdjustment(mOverviewGridEnabled);
+ }
+ return childOffset;
+ }
+
+ @Override
+ protected int getChildVisibleSize(int index) {
+ final TaskView taskView = getTaskViewAtByAbsoluteIndex(index);
+ if (taskView == null) {
+ return super.getChildVisibleSize(index);
+ }
+ return (int) (super.getChildVisibleSize(index) * taskView.getSizeAdjustment(
+ mOverviewFullscreenEnabled, mOverviewGridEnabled));
+ }
+
+ @Override
protected int computeMaxScroll() {
if (getTaskViewCount() > 0) {
if (mDisallowScrollToClearAll) {
@@ -2328,7 +2743,7 @@
}
/**
- * @return How many pixels the page is offset on the currently laid out dominant axis.
+ * Returns how many pixels the page is offset on the currently laid out dominant axis.
*/
public int getScrollOffset(int pageIndex) {
if (pageIndex == -1) {
@@ -2344,6 +2759,29 @@
return getScrollForPage(pageIndex) - scroll;
}
+ /**
+ * Returns how many pixels the task is offset on the currently laid out secondary axis
+ * according to {@link #mGridProgress}.
+ */
+ public float getGridTranslationSecondary(int pageIndex) {
+ TaskView taskView = getTaskViewAtByAbsoluteIndex(pageIndex);
+ if (taskView == null) {
+ return 0;
+ }
+
+ return mOrientationHandler.getSecondaryValue(taskView.getGridTranslationX(),
+ taskView.getGridTranslationY());
+ }
+
+ /**
+ * Returns the progress of forming a grid from carousel.
+ *
+ * @return A float from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
+ */
+ public float getGridProgress() {
+ return mGridProgress;
+ }
+
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
float degreesRotated;
if (navbarRotation == 0) {
@@ -2373,11 +2811,6 @@
};
}
- public TransformParams getLiveTileParams(
- boolean mightNeedToRefill) {
- return null;
- }
-
private void updateEnabledOverlays() {
int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
@@ -2393,7 +2826,36 @@
}
}
- /** If it's in the live tile mode, switch the running task into screenshot mode. */
+ public void setOverviewGridEnabled(boolean overviewGridEnabled) {
+ if (mOverviewGridEnabled != overviewGridEnabled) {
+ mOverviewGridEnabled = overviewGridEnabled;
+ // Request layout to ensure scroll position is recalculated with updated mGridProgress.
+ requestLayout();
+ }
+ }
+
+ public void setOverviewFullscreenEnabled(boolean overviewFullscreenEnabled) {
+ if (mOverviewFullscreenEnabled != overviewFullscreenEnabled) {
+ mOverviewFullscreenEnabled = overviewFullscreenEnabled;
+ // Request layout to ensure scroll position is recalculated with updated
+ // mFullscreenProgress.
+ requestLayout();
+ }
+ }
+
+ /**
+ * Switch the current running task view to static snapshot mode,
+ * capturing the snapshot at the same time.
+ */
+ public void switchToScreenshot(Runnable onFinishRunnable) {
+ switchToScreenshot(mRunningTaskId == -1 ? null
+ : mRecentsAnimationController.screenshotTask(mRunningTaskId), onFinishRunnable);
+ }
+
+ /**
+ * Switch the current running task view to static snapshot mode, using the
+ * provided thumbnail data as the snapshot.
+ */
public void switchToScreenshot(ThumbnailData thumbnailData, Runnable onFinishRunnable) {
TaskView taskView = getRunningTaskView();
if (taskView != null) {
@@ -2403,7 +2865,7 @@
} else {
taskView.getThumbnail().refresh();
}
- ViewUtils.postDraw(taskView, onFinishRunnable);
+ ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
} else {
onFinishRunnable.run();
}
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index 19e278b..db04fc0 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -15,10 +15,7 @@
*/
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.QUICK_SWITCH;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -32,25 +29,20 @@
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Path.Op;
-import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.animation.Interpolator;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
-import com.android.quickstep.util.LayoutUtils;
/**
* Scrim used for all-apps and shelf in Overview
@@ -77,15 +69,11 @@
private final float mRadius;
private final int mMaxScrimAlpha;
private final Paint mPaint;
- private final OnboardingPrefs mOnboardingPrefs;
// Mid point where the alpha changes
private int mMidAlpha;
private float mMidProgress;
- // The progress at which the drag handle starts moving up with the shelf.
- private float mDragHandleProgress;
-
private Interpolator mBeforeMidProgressColorInterpolator = ACCEL;
private Interpolator mAfterMidProgressColorInterpolator = ACCEL;
@@ -103,7 +91,6 @@
private boolean mRemainingScreenPathValid = false;
private Mode mSysUINavigationMode;
- private boolean mIsTwoZoneSwipeModel;
public ShelfScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -112,7 +99,6 @@
mEndAlpha = Color.alpha(mEndScrim);
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mOnboardingPrefs = mLauncher.getOnboardingPrefs();
// Just assume the easiest UI for now, until we have the proper layout information.
mDrawingFlatColor = true;
@@ -145,11 +131,9 @@
// Show the shelf more quickly before reaching overview progress.
mBeforeMidProgressColorInterpolator = ACCEL_2;
mAfterMidProgressColorInterpolator = ACCEL;
- mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get();
} else {
mBeforeMidProgressColorInterpolator = ACCEL;
mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
- mIsTwoZoneSwipeModel = false;
}
}
@@ -162,49 +146,26 @@
mRemainingScreenPathValid = false;
mShiftRange = mLauncher.getAllAppsController().getShiftRange();
- Context context = getContext();
- if ((OVERVIEW.getVisibleElements(mLauncher) & ALL_APPS_HEADER_EXTRA) == 0) {
- mDragHandleProgress = 1;
- if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
- && SysUINavigationMode.removeShelfFromOverview(context)) {
- // Fade in all apps background quickly to distinguish from swiping from nav bar.
- mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
- mMidProgress = OverviewState.getDefaultVerticalProgress(mLauncher);
- } else {
- mMidAlpha = 0;
- mMidProgress = 1;
- }
- } else {
- mMidAlpha = Themes.getAttrInteger(context, R.attr.allAppsInterimScrimAlpha);
- mMidProgress = OVERVIEW.getVerticalProgress(mLauncher);
- Rect hotseatPadding = dp.getHotseatLayoutPadding();
- int hotseatSize = dp.hotseatBarSizePx + dp.getInsets().bottom
- + hotseatPadding.bottom + hotseatPadding.top;
- float dragHandleTop =
- Math.min(hotseatSize, LayoutUtils.getDefaultSwipeHeight(context, dp));
- mDragHandleProgress = 1 - (dragHandleTop / mShiftRange);
- }
- mTopOffset = dp.getInsets().top - mDragHandleSize.y;
+ // Fade in all apps background quickly to distinguish from swiping from nav bar.
+ mMidAlpha = Themes.getAttrInteger(getContext(), R.attr.allAppsInterimScrimAlpha);
+ mMidProgress = 1 - (OverviewState.getDefaultSwipeHeight(mLauncher)
+ / mLauncher.getAllAppsController().getShiftRange());
+
+ mTopOffset = dp.getInsets().top;
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
}
updateColors();
updateSysUiColors();
- updateDragHandleAlpha();
invalidate();
}
@Override
public void updateColors() {
super.updateColors();
- mDragHandleOffset = 0;
if (mDrawingFlatColor) {
return;
}
- if (mProgress < mDragHandleProgress) {
- mDragHandleOffset = mShiftRange * (mDragHandleProgress - mProgress);
- }
-
if (mProgress >= SCRIM_CATCHUP_THRESHOLD) {
mShelfTop = mShiftRange * mProgress + mTopOffset;
} else {
@@ -215,13 +176,6 @@
if (mProgress >= 1) {
mRemainingScreenColor = 0;
mShelfColor = 0;
- LauncherState state = mLauncher.getStateManager().getState();
- if (mSysUINavigationMode == Mode.NO_BUTTON
- && (state == BACKGROUND_APP || state == QUICK_SWITCH)
- && mLauncher.getShelfPeekAnim().isPeeking()) {
- // Show the shelf background when peeking during swipe up.
- mShelfColor = setColorAlphaBound(mEndScrim, mMidAlpha);
- }
} else if (mProgress >= mMidProgress) {
mRemainingScreenColor = 0;
@@ -259,19 +213,7 @@
}
@Override
- protected boolean shouldDragHandleBeVisible() {
- boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
- && !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
- return needsAllAppsEdu || super.shouldDragHandleBeVisible();
- }
-
- @Override
protected void onDraw(Canvas canvas) {
- drawBackground(canvas);
- drawDragHandle(canvas);
- }
-
- private void drawBackground(Canvas canvas) {
if (mDrawingFlatColor) {
if (mCurrentFlatColor != 0) {
canvas.drawColor(mCurrentFlatColor);
@@ -311,9 +253,4 @@
mPaint.setColor(mShelfColor);
canvas.drawRoundRect(0, mShelfTop, width, height + mRadius, mRadius, mRadius, mPaint);
}
-
- @Override
- public float getVisualTop() {
- return mShelfTop;
- }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
similarity index 78%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
rename to quickstep/src/com/android/quickstep/views/TaskMenuView.java
index fb799a8..fe7ece2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -16,36 +16,37 @@
package com.android.quickstep.views;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.Outline;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.touch.PagedOrientationHandler;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskUtils;
-import com.android.quickstep.views.IconView.OnScaleUpdateListener;
+import com.android.quickstep.util.TaskCornerRadius;
/**
* Contains options for a recent task when long-pressing its icon.
@@ -54,42 +55,15 @@
private static final Rect sTempRect = new Rect();
- private final OnScaleUpdateListener mTaskViewIconScaleListener = new OnScaleUpdateListener() {
- @Override
- public void onScaleUpdate(float scale) {
- final Drawable drawable = mTaskIcon.getDrawable();
- if (drawable instanceof FastBitmapDrawable) {
- if (scale != ((FastBitmapDrawable) drawable).getScale()) {
- mMenuIconDrawable.setScale(scale);
- }
- }
- }
- };
-
- private final OnScaleUpdateListener mMenuIconScaleListener = new OnScaleUpdateListener() {
- @Override
- public void onScaleUpdate(float scale) {
- final Drawable taskViewDrawable = mTaskView.getIconView().getDrawable();
- if (taskViewDrawable instanceof FastBitmapDrawable) {
- final float currentScale = ((FastBitmapDrawable) taskViewDrawable).getScale();
- if (currentScale != scale) {
- ((FastBitmapDrawable) taskViewDrawable).setScale(scale);
- }
- }
- }
- };
-
private static final int REVEAL_OPEN_DURATION = 150;
private static final int REVEAL_CLOSE_DURATION = 100;
private final float mThumbnailTopMargin;
private BaseDraggingActivity mActivity;
private TextView mTaskName;
- private IconView mTaskIcon;
private AnimatorSet mOpenCloseAnimator;
private TaskView mTaskView;
private LinearLayout mOptionLayout;
- private FastBitmapDrawable mMenuIconDrawable;
public TaskMenuView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -100,13 +74,13 @@
mActivity = BaseDraggingActivity.fromContext(context);
mThumbnailTopMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin);
+ setClipToOutline(true);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTaskName = findViewById(R.id.task_name);
- mTaskIcon = findViewById(R.id.task_icon);
mOptionLayout = findViewById(R.id.menu_option_layout);
}
@@ -133,31 +107,33 @@
}
@Override
- public void logActionCommand(int command) {
- // TODO
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- // Remove all scale listeners when menu is removed
- mTaskView.getIconView().removeUpdateScaleListener(mTaskViewIconScaleListener);
- mTaskIcon.removeUpdateScaleListener(mMenuIconScaleListener);
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_TASK_MENU) != 0;
}
+ @Override
+ public ViewOutlineProvider getOutlineProvider() {
+ return new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(),
+ TaskCornerRadius.get(view.getContext()));
+ }
+ };
+ }
+
public void setPosition(float x, float y, PagedOrientationHandler pagedOrientationHandler) {
float adjustedY = y + mThumbnailTopMargin;
// 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);
+ if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ // In tablet, set pivotY to original position without mThumbnailTopMargin adjustment.
+ setPivotY(-mThumbnailTopMargin);
+ } else {
+ setPivotY(0);
+ }
setRotation(pagedOrientationHandler.getDegreesRotated());
setX(pagedOrientationHandler.getTaskMenuX(x, mTaskView.getThumbnail()));
setY(pagedOrientationHandler.getTaskMenuY(adjustedY, mTaskView.getThumbnail()));
@@ -207,23 +183,9 @@
}
private void addMenuOptions(TaskView taskView) {
- Drawable icon = taskView.getTask().icon.getConstantState().newDrawable();
- mTaskIcon.setDrawable(icon);
- mTaskIcon.setOnClickListener(v -> close(true));
mTaskName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
mTaskName.setOnClickListener(v -> close(true));
- // Set the icons to match scale by listening to each other's changes
- mMenuIconDrawable = icon instanceof FastBitmapDrawable ? (FastBitmapDrawable) icon : null;
- taskView.getIconView().addUpdateScaleListener(mTaskViewIconScaleListener);
- mTaskIcon.addUpdateScaleListener(mMenuIconScaleListener);
-
- // Move the icon and text up half an icon size to lay over the TaskView
- LinearLayout.LayoutParams params =
- (LinearLayout.LayoutParams) mTaskIcon.getLayoutParams();
- params.topMargin = (int) -mThumbnailTopMargin;
- mTaskIcon.setLayoutParams(params);
-
TaskOverlayFactory.getEnabledShortcuts(taskView).forEach(this::addMenuOption);
}
@@ -234,7 +196,16 @@
menuOptionView.findViewById(R.id.icon), menuOptionView.findViewById(R.id.text));
LayoutParams lp = (LayoutParams) menuOptionView.getLayoutParams();
mTaskView.getPagedOrientationHandler().setLayoutParamsForTaskMenuOptionItem(lp);
- menuOptionView.setOnClickListener(menuOption);
+ menuOptionView.setOnClickListener(view -> {
+ if (LIVE_TILE.get()) {
+ RecentsView recentsView = mTaskView.getRecentsView();
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+ () -> menuOption.onClick(view)));
+ } else {
+ menuOption.onClick(view);
+ }
+ });
mOptionLayout.addView(menuOptionView);
}
@@ -303,7 +274,7 @@
}
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
- float radius = Themes.getDialogCornerRadius(getContext());
+ float radius = TaskCornerRadius.get(mContext);
Rect fromRect = new Rect(0, 0, getWidth(), 0);
Rect toRect = new Rect(0, 0, getWidth(), getHeight());
return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
similarity index 80%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
rename to quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 607672a..4c21745 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,7 +16,10 @@
package com.android.quickstep.views;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
+
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.content.Context;
@@ -48,6 +51,7 @@
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.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
@@ -123,7 +127,7 @@
mDimmingPaintAfterClearing.setColor(Color.BLACK);
mActivity = BaseActivity.fromContext(context);
mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
- // Initialize with dummy value. It is overridden later by TaskView
+ // Initialize with placeholder value. It is overridden later by TaskView
mFullscreenParams = TEMP_PARAMS.get(context);
}
@@ -250,10 +254,10 @@
public int getSysUiStatusNavFlags() {
if (mThumbnailData != null) {
int flags = 0;
- flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0
+ flags |= (mThumbnailData.appearance & APPEARANCE_LIGHT_STATUS_BARS) != 0
? SystemUiController.FLAG_LIGHT_STATUS
: SystemUiController.FLAG_DARK_STATUS;
- flags |= (mThumbnailData.systemUiVisibility & SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0
+ flags |= (mThumbnailData.appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0
? SystemUiController.FLAG_LIGHT_NAV
: SystemUiController.FLAG_DARK_NAV;
return flags;
@@ -315,7 +319,7 @@
public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
float cornerRadius) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ if (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,
@@ -327,22 +331,14 @@
// Draw the background in all cases, except when the thumbnail data is opaque
final boolean drawBackgroundOnly = mTask == null || mTask.isLocked || mBitmapShader == null
|| mThumbnailData == null;
- if (drawBackgroundOnly || mPreviewPositionHelper.mClipBottom > 0
- || mThumbnailData.isTranslucent) {
+ if (drawBackgroundOnly || mThumbnailData.isTranslucent) {
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mBackgroundPaint);
if (drawBackgroundOnly) {
return;
}
}
- if (mPreviewPositionHelper.mClipBottom > 0) {
- canvas.save();
- canvas.clipRect(x, y, width, mPreviewPositionHelper.mClipBottom);
- canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
- canvas.restore();
- } else {
- canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
- }
+ canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
}
public TaskView getTaskView() {
@@ -380,7 +376,6 @@
}
private void updateThumbnailMatrix() {
- mPreviewPositionHelper.mClipBottom = -1;
mPreviewPositionHelper.mIsOrientationChanged = false;
if (mBitmapShader != null && mThumbnailData != null) {
mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
@@ -474,41 +469,99 @@
/**
* Updates the matrix based on the provided parameters
*/
- public void updateThumbnailMatrix(Rect thumbnailPosition, ThumbnailData thumbnailData,
+ public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation) {
boolean isRotated = false;
boolean isOrientationDifferent;
- mClipBottom = -1;
int thumbnailRotation = thumbnailData.rotation;
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
- Rect thumbnailInsets = getBoundedInsets(
- dp.getInsets(), thumbnailData.insets, deltaRotate);
+ RectF thumbnailClipHint = new RectF(thumbnailData.insets);
float scale = thumbnailData.scale;
- final float thumbnailWidth = thumbnailPosition.width()
- - (thumbnailInsets.left + thumbnailInsets.right) * scale;
- final float thumbnailHeight = thumbnailPosition.height()
- - (thumbnailInsets.top + thumbnailInsets.bottom) * scale;
-
final float thumbnailScale;
- // Landscape vs portrait change
+ // Landscape vs portrait change.
+ // Note: Disable rotation in grid layout.
boolean windowingModeSupportsRotation = !dp.isMultiWindowMode
- && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN;
+ && thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN
+ && !(dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get());
isOrientationDifferent = isOrientationChange(deltaRotate)
&& windowingModeSupportsRotation;
- if (canvasWidth == 0) {
+ 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;
- // Scale the screenshot to always fit the width of the card.
- thumbnailScale = isOrientationDifferent
- ? canvasWidth / thumbnailHeight
- : canvasWidth / thumbnailWidth;
+
+ float surfaceWidth = thumbnailBounds.width() / scale;
+ float surfaceHeight = thumbnailBounds.height() / scale;
+ float availableWidth = surfaceWidth
+ - (thumbnailClipHint.left + thumbnailClipHint.right);
+ float availableHeight = surfaceHeight
+ - (thumbnailClipHint.top + thumbnailClipHint.bottom);
+
+ final float targetW, targetH;
+ if (isOrientationDifferent) {
+ targetW = canvasHeight;
+ targetH = canvasWidth;
+ } else {
+ targetW = canvasWidth;
+ targetH = canvasHeight;
+ }
+ float canvasAspect = targetW / targetH;
+
+ // Update the clipHint such that
+ // > the final clipped position has same aspect ratio as requested by canvas
+ // > the clipped region is within the task insets if possible
+ // > the clipped region is not scaled up when drawing. If that is not possible
+ // while staying within the taskInsets, move outside the insets.
+ float croppedWidth = availableWidth;
+ if (croppedWidth < targetW) {
+ croppedWidth = Math.min(targetW, surfaceWidth);
+ }
+
+ float croppedHeight = croppedWidth / canvasAspect;
+ if (croppedHeight > availableHeight) {
+ croppedHeight = availableHeight;
+ if (croppedHeight < targetH) {
+ croppedHeight = Math.min(targetH, surfaceHeight);
+ }
+ croppedWidth = croppedHeight * canvasAspect;
+
+ // One last check in case the task aspect radio messed up something
+ if (croppedWidth > surfaceWidth) {
+ croppedWidth = surfaceWidth;
+ croppedHeight = croppedWidth / canvasAspect;
+ }
+ }
+
+ // Update the clip hints
+ float halfExtraW = (availableWidth - croppedWidth) / 2;
+ thumbnailClipHint.left += halfExtraW;
+ thumbnailClipHint.right += halfExtraW;
+ if (thumbnailClipHint.left < 0) {
+ thumbnailClipHint.right += thumbnailClipHint.left;
+ thumbnailClipHint.left = 0;
+ } else if (thumbnailClipHint.right < 0) {
+ thumbnailClipHint.left += thumbnailClipHint.right;
+ thumbnailClipHint.right = 0;
+ }
+
+ float halfExtraH = (availableHeight - croppedHeight) / 2;
+ thumbnailClipHint.top += halfExtraH;
+ thumbnailClipHint.bottom += halfExtraH;
+ 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();
@@ -518,24 +571,24 @@
mClippedInsets.offsetTo(splitScreenInsets.left * scale,
splitScreenInsets.top * scale);
} else {
- mClippedInsets.offsetTo(thumbnailInsets.left * scale,
- thumbnailInsets.top * scale);
+ mClippedInsets.offsetTo(thumbnailClipHint.left * scale,
+ thumbnailClipHint.top * scale);
}
mMatrix.setTranslate(
- -thumbnailInsets.left * scale,
- -thumbnailInsets.top * scale);
+ -thumbnailClipHint.left * scale,
+ -thumbnailClipHint.top * scale);
} else {
- setThumbnailRotation(deltaRotate, thumbnailInsets, scale, thumbnailPosition);
+ setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds);
}
final float widthWithInsets;
final float heightWithInsets;
if (isOrientationDifferent) {
- widthWithInsets = thumbnailPosition.height() * thumbnailScale;
- heightWithInsets = thumbnailPosition.width() * thumbnailScale;
+ widthWithInsets = thumbnailBounds.height() * thumbnailScale;
+ heightWithInsets = thumbnailBounds.width() * thumbnailScale;
} else {
- widthWithInsets = thumbnailPosition.width() * thumbnailScale;
- heightWithInsets = thumbnailPosition.height() * thumbnailScale;
+ widthWithInsets = thumbnailBounds.width() * thumbnailScale;
+ heightWithInsets = thumbnailBounds.height() * thumbnailScale;
}
mClippedInsets.left *= thumbnailScale;
mClippedInsets.top *= thumbnailScale;
@@ -551,25 +604,9 @@
}
mMatrix.postScale(thumbnailScale, thumbnailScale);
-
- float bitmapHeight = Math.max(0,
- (isOrientationDifferent ? thumbnailWidth : thumbnailHeight) * thumbnailScale);
- if (Math.round(bitmapHeight) < canvasHeight) {
- mClipBottom = bitmapHeight;
- }
mIsOrientationChanged = isOrientationDifferent;
}
- private Rect getBoundedInsets(Rect activityInsets, Rect insets, int deltaRotation) {
- if (deltaRotation != 0) {
- return insets;
- }
- return new Rect(Math.min(insets.left, activityInsets.left),
- Math.min(insets.top, activityInsets.top),
- Math.min(insets.right, activityInsets.right),
- Math.min(insets.bottom, activityInsets.bottom));
- }
-
private int getRotationDelta(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
@@ -585,12 +622,12 @@
return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
}
- private void setThumbnailRotation(int deltaRotate, Rect thumbnailInsets, float scale,
+ private void setThumbnailRotation(int deltaRotate, RectF thumbnailInsets, float scale,
Rect thumbnailPosition) {
- int newLeftInset = 0;
- int newTopInset = 0;
- int translateX = 0;
- int translateY = 0;
+ float newLeftInset = 0;
+ float newTopInset = 0;
+ float translateX = 0;
+ float translateY = 0;
mMatrix.setRotate(90 * deltaRotate);
switch (deltaRotate) { /* Counter-clockwise */
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
similarity index 66%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
rename to quickstep/src/com/android/quickstep/views/TaskView.java
index a8f2a1c..88545c6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -30,18 +30,20 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.comp;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-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.UI_HELPER_EXECUTOR;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
@@ -49,8 +51,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.GradientDrawable;
-import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
@@ -60,36 +60,36 @@
import android.view.Surface;
import android.view.TouchDelegate;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.Toast;
-import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.config.FeatureFlags;
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.touch.PagedOrientationHandler;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
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.TaskCornerRadius;
import com.android.quickstep.views.RecentsView.PageCallbacks;
@@ -111,20 +111,14 @@
private static final String TAG = TaskView.class.getSimpleName();
- /** A curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
- private static final TimeInterpolator CURVE_INTERPOLATOR
- = x -> (float) -Math.cos(x * Math.PI) / 2f + .5f;
-
/**
* The alpha of a black scrim on a page in the carousel as it leaves the screen.
* In the resting position of the carousel, the adjacent pages have about half this scrim.
*/
public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
- /**
- * How much to scale down pages near the edge of the screen.
- */
- public static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
+ private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
+ private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
@@ -153,29 +147,29 @@
}
};
- private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_X =
- new FloatProperty<TaskView>("fillDismissGapTranslationX") {
+ private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
+ new FloatProperty<TaskView>("dismissTranslationX") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setFillDismissGapTranslationX(v);
+ taskView.setDismissTranslationX(v);
}
@Override
public Float get(TaskView taskView) {
- return taskView.mFillDismissGapTranslationX;
+ return taskView.mDismissTranslationX;
}
};
- private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_Y =
- new FloatProperty<TaskView>("fillDismissGapTranslationY") {
+ private static final FloatProperty<TaskView> DISMISS_TRANSLATION_Y =
+ new FloatProperty<TaskView>("dismissTranslationY") {
@Override
public void setValue(TaskView taskView, float v) {
- taskView.setFillDismissGapTranslationY(v);
+ taskView.setDismissTranslationY(v);
}
@Override
public Float get(TaskView taskView) {
- return taskView.mFillDismissGapTranslationY;
+ return taskView.mDismissTranslationY;
}
};
@@ -205,6 +199,32 @@
}
};
+ private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_X =
+ new FloatProperty<TaskView>("taskResistanceTranslationX") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setTaskResistanceTranslationX(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mTaskResistanceTranslationX;
+ }
+ };
+
+ private static final FloatProperty<TaskView> TASK_RESISTANCE_TRANSLATION_Y =
+ new FloatProperty<TaskView>("taskResistanceTranslationY") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setTaskResistanceTranslationY(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mTaskResistanceTranslationY;
+ }
+ };
+
private final OnAttachStateChangeListener mTaskMenuStateListener =
new OnAttachStateChangeListener() {
@Override
@@ -227,17 +247,30 @@
private TaskMenuView mMenuView;
private IconView mIconView;
private final DigitalWellBeingToast mDigitalWellBeingToast;
- private float mCurveScale;
private float mFullscreenProgress;
+ private float mGridProgress;
+ private float mFullscreenScale = 1;
+ private float mGridScale = 1;
private final FullscreenDrawParams mCurrentFullscreenParams;
- private final BaseDraggingActivity mActivity;
+ private final StatefulActivity mActivity;
// Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
- // TODO: We should do this for secondary translation properties as well.
- private float mFillDismissGapTranslationX;
- private float mFillDismissGapTranslationY;
+ private float mDismissTranslationX;
+ private float mDismissTranslationY;
private float mTaskOffsetTranslationX;
private float mTaskOffsetTranslationY;
+ private float mTaskResistanceTranslationX;
+ private float mTaskResistanceTranslationY;
+ // The following translation variables should only be used in the same orientation as Launcher.
+ private float mFullscreenTranslationX;
+ private float mAccumulatedFullscreenTranslationX;
+ private float mBoxTranslationY;
+ // The following grid translations scales with mGridProgress.
+ private float mGridTranslationX;
+ private float mGridTranslationY;
+ // Offset translation does not affect scroll calculation.
+ private float mGridOffsetTranslationX;
+ private float mNonRtlVisibleOffset;
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
@@ -248,20 +281,18 @@
private boolean mShowScreenshot;
// The current background requests to load the task thumbnail and icon
- private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
- private TaskIconCache.IconLoadRequest mIconLoadRequest;
+ private CancellableTask mThumbnailLoadRequest;
+ private CancellableTask mIconLoadRequest;
- // Order in which the footers appear. Lower order appear below higher order.
- public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
- private final FooterWrapper[] mFooters = new FooterWrapper[2];
- private float mFooterVerticalOffset = 0;
- private float mFooterAlpha = 1;
- private int mStackHeight;
+ private boolean mEndQuickswitchCuj;
+
private View mContextualChipWrapper;
private View mContextualChip;
private final float[] mIconCenterCoords = new float[2];
private final float[] mChipCenterCoords = new float[2];
+ private boolean mIsClickableAsLiveTile = true;
+
public TaskView(Context context) {
this(context, null);
}
@@ -272,24 +303,40 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mActivity = BaseDraggingActivity.fromContext(context);
+ mActivity = StatefulActivity.fromContext(context);
setOnClickListener((view) -> {
if (getTask() == null) {
return;
}
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (isRunningTask()) {
- createLaunchAnimationForRunningTask().start();
- } else {
- launchTask(true /* animate */);
+ if (LIVE_TILE.get() && isRunningTask()) {
+ if (!mIsClickableAsLiveTile) {
+ return;
}
+
+ mIsClickableAsLiveTile = false;
+ RecentsView recentsView = getRecentsView();
+ RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
+ recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
+
+ AnimatorSet anim = new AnimatorSet();
+ TaskViewUtils.composeRecentsLaunchAnimator(
+ anim, this, targets.apps,
+ targets.wallpapers, true /* launcherClosing */,
+ mActivity.getStateManager(), recentsView,
+ recentsView.getDepthController());
+ anim.addListener(new AnimatorListenerAdapter() {
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
+ recentsView.finishRecentsAnimation(false, null);
+ mIsClickableAsLiveTile = true;
+ }
+ });
+ anim.start();
} else {
launchTask(true /* animate */);
}
-
- mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
- TaskUtils.getLaunchComponentKeyForTask(getTask().key));
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
.log(LAUNCHER_TASK_LAUNCH_TAP);
});
@@ -305,15 +352,16 @@
* Builds proto for logging
*/
public WorkspaceItemInfo getItemInfo() {
- ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key);
- WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
- dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
- dummyInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
- dummyInfo.user = componentKey.user;
- dummyInfo.intent = new Intent().setComponent(componentKey.componentName);
- dummyInfo.title = TaskUtils.getTitle(getContext(), getTask());
- dummyInfo.screenId = getRecentsView().indexOfChild(this);
- return dummyInfo;
+ final Task task = getTask();
+ ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
+ WorkspaceItemInfo stubInfo = new WorkspaceItemInfo();
+ stubInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
+ stubInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
+ stubInfo.user = componentKey.user;
+ stubInfo.intent = new Intent().setComponent(componentKey.componentName);
+ stubInfo.title = task.title;
+ stubInfo.screenId = getRecentsView().indexOfChild(this);
+ return stubInfo;
}
@Override
@@ -387,10 +435,9 @@
mContextualChip.setScaleX(comp(modalness));
mContextualChip.setScaleY(comp(modalness));
}
- if (mContextualChipWrapper != null) {
- mContextualChipWrapper.setAlpha(comp(modalness));
- }
- updateFooterVerticalOffset(mFooterVerticalOffset);
+ mDigitalWellBeingToast.updateBannerOffset(modalness,
+ mCurrentFullscreenParams.mCurrentDrawnInsets.top
+ + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
}
public TaskMenuView getMenuView() {
@@ -418,6 +465,10 @@
return mTask;
}
+ public boolean hasTaskId(int taskId) {
+ return mTask != null && mTask.key != null && mTask.key.id == taskId;
+ }
+
public TaskThumbnailView getThumbnail() {
return mSnapshotView;
}
@@ -427,14 +478,9 @@
}
public AnimatorPlaybackController createLaunchAnimationForRunningTask() {
- final PendingAnimation pendingAnimation = getRecentsView().createTaskLaunchAnimation(
- this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
- AnimatorPlaybackController currentAnimation = pendingAnimation.createPlaybackController();
- currentAnimation.setEndAction(() -> {
- pendingAnimation.finish(true, Touch.SWIPE);
- launchTask(false);
- });
- return currentAnimation;
+ return getRecentsView().createTaskLaunchAnimation(
+ this, RECENTS_LAUNCH_DURATION, TOUCH_RESPONSE_INTERPOLATOR)
+ .createPlaybackController();
}
public void launchTask(boolean animate) {
@@ -456,28 +502,6 @@
public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- RecentsView recentsView = getRecentsView();
- if (isRunningTask()) {
- recentsView.finishRecentsAnimation(false /* toRecents */,
- () -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
- } else {
- // This is a workaround against the WM issue that app open is not correctly animated
- // when recents animation is being cleaned up (b/143774568). When that's possible,
- // we should rely on the framework side to cancel the recents animation, and we will
- // clean up the screenshot on the launcher side while we launch the next task.
- recentsView.switchToScreenshot(null,
- () -> recentsView.finishRecentsAnimation(true /* toRecents */,
- () -> launchTaskInternal(animate, freezeTaskList, resultCallback,
- resultCallbackHandler)));
- }
- } else {
- launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
- }
- }
-
- private void launchTaskInternal(boolean animate, boolean freezeTaskList,
- Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
if (mTask != null) {
final ActivityOptions opts;
TestLogging.recordEvent(
@@ -500,17 +524,22 @@
if (freezeTaskList) {
ActivityOptionsCompat.setFreezeRecentTasksList(opts);
}
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
- opts, (success) -> {
- if (resultCallback != null && !success) {
- // If the call to start activity failed, then post the result
- // immediately, otherwise, wait for the animation start callback
- // from the activity options above
- resultCallbackHandler.post(() -> resultCallback.accept(false));
- }
- }, resultCallbackHandler);
+ UI_HELPER_EXECUTOR.execute(
+ () -> ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
+ mTask.key,
+ opts,
+ (success) -> {
+ if (resultCallback != null && !success) {
+ // If the call to start activity failed, then post the
+ // result
+ // immediately, otherwise, wait for the animation start
+ // callback
+ // from the activity options above
+ resultCallbackHandler.post(
+ () -> resultCallback.accept(false));
+ }
+ }, resultCallbackHandler));
}
- getRecentsView().onTaskLaunched(mTask);
}
}
@@ -530,9 +559,6 @@
mIconLoadRequest = iconCache.updateIconInBackground(mTask,
(task) -> {
setIcon(task.icon);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
- getRecentsView().updateLiveTileIcon(task.icon);
- }
mDigitalWellBeingToast.initialize(mTask);
});
} else {
@@ -555,15 +581,13 @@
}
}
- private boolean showTaskMenu(int action) {
+ private boolean showTaskMenu() {
if (!getRecentsView().isClearAllHidden()) {
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
} else {
mMenuView = TaskMenuView.showForTask(this);
mActivity.getStatsLogManager().logger().withItemInfo(getItemInfo())
.log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS);
- UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
- LauncherLogProto.ItemType.TASK_ICON);
if (mMenuView != null) {
mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
}
@@ -574,10 +598,10 @@
private void setIcon(Drawable icon) {
if (icon != null) {
mIconView.setDrawable(icon);
- mIconView.setOnClickListener(v -> showTaskMenu(Touch.TAP));
+ mIconView.setOnClickListener(v -> showTaskMenu());
mIconView.setOnLongClickListener(v -> {
requestDisallowInterceptTouchEvent(true);
- return showTaskMenu(Touch.LONGPRESS);
+ return showTaskMenu();
});
} else {
mIconView.setDrawable(null);
@@ -591,6 +615,9 @@
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
+ int taskIconMargin = (int) getResources().getDimension(R.dimen.task_icon_top_margin);
+ int taskIconHeight = (int) getResources().getDimension(R.dimen.task_thumbnail_icon_size);
+ int iconTopMargin = taskIconMargin - taskIconHeight + thumbnailPadding;
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
switch (orientationHandler.getRotation()) {
case ROTATION_90:
@@ -602,7 +629,8 @@
case ROTATION_180:
iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
iconParams.bottomMargin = -thumbnailPadding;
- iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
+ iconParams.leftMargin = iconParams.rightMargin = 0;
+ iconParams.topMargin = iconTopMargin;
break;
case ROTATION_270:
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
@@ -613,7 +641,8 @@
case Surface.ROTATION_0:
default:
iconParams.gravity = TOP | CENTER_HORIZONTAL;
- iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
+ iconParams.leftMargin = iconParams.rightMargin = 0;
+ iconParams.topMargin = iconTopMargin;
break;
}
mIconView.setLayoutParams(iconParams);
@@ -629,7 +658,7 @@
progress = 1 - progress;
}
mFocusTransitionProgress = progress;
- mSnapshotView.setDimAlphaMultipler(progress);
+ mSnapshotView.setDimAlphaMultipler(0);
float iconScalePercentage = (float) SCALE_ICON_DURATION / DIM_ANIM_DURATION;
float lowerClamp = invert ? 1f - iconScalePercentage : 0;
float upperClamp = invert ? 1 : iconScalePercentage;
@@ -642,7 +671,9 @@
mContextualChip.setScaleX(scale);
mContextualChip.setScaleY(scale);
}
- updateFooterVerticalOffset(1.0f - scale);
+ mDigitalWellBeingToast.updateBannerOffset(1f - scale,
+ mCurrentFullscreenParams.mCurrentDrawnInsets.top
+ + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
}
public void setIconScaleAnimStartProgress(float startProgress) {
@@ -677,11 +708,12 @@
}
protected void resetViewTransforms() {
- setCurveScale(1);
- mFillDismissGapTranslationX = mTaskOffsetTranslationX = 0f;
- mFillDismissGapTranslationY = mTaskOffsetTranslationY = 0f;
- setTranslationX(0f);
- setTranslationY(0f);
+ // fullscreenTranslation and accumulatedTranslation should not be reset, as
+ // resetViewTransforms is called during Quickswitch scrolling.
+ mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = 0f;
+ mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
+ applyTranslationX();
+ applyTranslationY();
setTranslationZ(0);
setAlpha(mStableAlpha);
setIconScaleAndDim(1);
@@ -694,6 +726,9 @@
@Override
public void onRecycle() {
+ mFullscreenTranslationX = mAccumulatedFullscreenTranslationX = mGridTranslationX =
+ mGridTranslationY =
+ mGridOffsetTranslationX = mBoxTranslationY = mNonRtlVisibleOffset = 0f;
resetViewTransforms();
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
@@ -709,19 +744,9 @@
return;
}
- float curveInterpolation =
- CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
- float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
- curveInterpolation);
- mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
- setCurveScale(curveScaleForCurveInterpolation);
-
- mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f);
- for (FooterWrapper footer : mFooters) {
- if (footer != null) {
- footer.mView.setAlpha(mFooterAlpha);
- }
- }
+ float dwbBannerAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation,
+ 0f, 1f);
+ mDigitalWellBeingToast.updateBannerAlpha(dwbBannerAlpha);
if (mMenuView != null) {
PagedOrientationHandler pagedOrientationHandler = getPagedOrientationHandler();
@@ -734,57 +759,6 @@
}
/**
- * Sets the footer at the specific index and returns the previously set footer.
- */
- public View setFooter(int index, View view) {
- View oldFooter = null;
-
- // If the footer are is already collapsed, do not animate entry
- boolean shouldAnimateEntry = mFooterVerticalOffset <= 0;
-
- if (mFooters[index] != null) {
- oldFooter = mFooters[index].mView;
- mFooters[index].release();
- removeView(oldFooter);
-
- // If we are replacing an existing footer, do not animate entry
- shouldAnimateEntry = false;
- }
- if (view != null) {
- int indexToAdd = getChildCount();
- for (int i = index - 1; i >= 0; i--) {
- if (mFooters[i] != null) {
- indexToAdd = indexOfChild(mFooters[i].mView);
- break;
- }
- }
-
- addView(view, indexToAdd);
- LayoutParams layoutParams = (LayoutParams) view.getLayoutParams();
- layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
- layoutParams.bottomMargin =
- ((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin;
- view.setAlpha(mFooterAlpha);
- mFooters[index] = new FooterWrapper(view);
- if (shouldAnimateEntry) {
- mFooters[index].animateEntry();
- }
- } else {
- mFooters[index] = null;
- }
-
- mStackHeight = 0;
- for (FooterWrapper footer : mFooters) {
- if (footer != null) {
- footer.setVerticalShift(mStackHeight);
- mStackHeight += footer.mExpectedHeight;
- }
- }
-
- return oldFooter;
- }
-
- /**
* Sets the contextual chip.
*
* @param view Wrapper view containing contextual chip.
@@ -800,30 +774,16 @@
layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
int expectedChipHeight = getExpectedViewHeight(view);
float chipOffset = getResources().getDimension(R.dimen.chip_hint_vertical_offset);
- layoutParams.bottomMargin = (int)
- (((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin
- - expectedChipHeight + chipOffset);
+ layoutParams.bottomMargin = -expectedChipHeight - (int) chipOffset;
mContextualChip = ((FrameLayout) mContextualChipWrapper).getChildAt(0);
mContextualChip.setScaleX(0f);
mContextualChip.setScaleY(0f);
- GradientDrawable scrimDrawable = (GradientDrawable) getResources().getDrawable(
- R.drawable.chip_scrim_gradient, mActivity.getTheme());
- float cornerRadius = getTaskCornerRadius();
- scrimDrawable.setCornerRadii(
- new float[]{0, 0, 0, 0, cornerRadius, cornerRadius, cornerRadius,
- cornerRadius});
- InsetDrawable scrimDrawableInset = new InsetDrawable(scrimDrawable, 0, 0, 0,
- (int) (expectedChipHeight - chipOffset));
- mContextualChipWrapper.setBackground(scrimDrawableInset);
- mContextualChipWrapper.setPadding(0, 0, 0, 0);
- mContextualChipWrapper.setAlpha(0f);
addView(view, getChildCount(), layoutParams);
if (mContextualChip != null) {
mContextualChip.animate().scaleX(1f).scaleY(1f).setDuration(50);
}
if (mContextualChipWrapper != null) {
mChipTouchDelegate = new TransformingTouchDelegate(mContextualChipWrapper);
- mContextualChipWrapper.animate().alpha(1f).setDuration(50);
}
}
}
@@ -851,58 +811,69 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
- setPivotX((right - left) * 0.5f);
- setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
+ if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ setPivotX(getLayoutDirection() == LAYOUT_DIRECTION_RTL ? (right - left) : 0);
+ setPivotY(mSnapshotView.getTop());
+ } else {
+ setPivotX((right - left) * 0.5f);
+ setPivotY(mSnapshotView.getTop() + mSnapshotView.getHeight() * 0.5f);
+ }
if (Utilities.ATLEAST_Q) {
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight());
setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
}
-
- mStackHeight = 0;
- for (FooterWrapper footer : mFooters) {
- if (footer != null) {
- mStackHeight += footer.mView.getHeight();
- }
- }
- updateFooterVerticalOffset(0);
}
- private void updateFooterVerticalOffset(float offset) {
- mFooterVerticalOffset = offset;
-
- for (FooterWrapper footer : mFooters) {
- if (footer != null) {
- footer.updateFooterOffset();
- }
+ /**
+ * How much to scale down pages near the edge of the screen.
+ */
+ public static float getEdgeScaleDownFactor(DeviceProfile deviceProfile) {
+ if (deviceProfile.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ return EDGE_SCALE_DOWN_FACTOR_GRID;
+ } else {
+ return EDGE_SCALE_DOWN_FACTOR_CAROUSEL;
}
}
- public static float getCurveScaleForInterpolation(float linearInterpolation) {
- float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
- return getCurveScaleForCurveInterpolation(curveInterpolation);
+ private void setFullscreenScale(float fullscreenScale) {
+ mFullscreenScale = fullscreenScale;
+ applyScale();
}
- private static float getCurveScaleForCurveInterpolation(float curveInterpolation) {
- return 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
+ public void setGridScale(float gridScale) {
+ mGridScale = gridScale;
+ applyScale();
}
- private void setCurveScale(float curveScale) {
- mCurveScale = curveScale;
- setScaleX(mCurveScale);
- setScaleY(mCurveScale);
+ /**
+ * Moves TaskView between carousel and 2 row grid.
+ *
+ * @param gridProgress 0 = carousel; 1 = 2 row grid.
+ */
+ public void setGridProgress(float gridProgress) {
+ mGridProgress = gridProgress;
+ applyTranslationX();
+ applyTranslationY();
+ applyScale();
}
- public float getCurveScale() {
- return mCurveScale;
+ private void applyScale() {
+ float scale = 1;
+ float fullScreenProgress = EXAGGERATED_EASE.getInterpolation(mFullscreenProgress);
+ scale *= Utilities.mapRange(fullScreenProgress, 1f, mFullscreenScale);
+ float gridProgress = ACCEL_DEACCEL.getInterpolation(mGridProgress);
+ scale *= Utilities.mapRange(gridProgress, 1f, mGridScale);
+ setScaleX(scale);
+ setScaleY(scale);
}
- private void setFillDismissGapTranslationX(float x) {
- mFillDismissGapTranslationX = x;
+ private void setDismissTranslationX(float x) {
+ mDismissTranslationX = x;
applyTranslationX();
}
- private void setFillDismissGapTranslationY(float y) {
- mFillDismissGapTranslationY = y;
+ private void setDismissTranslationY(float y) {
+ mDismissTranslationY = y;
applyTranslationY();
}
@@ -916,17 +887,117 @@
applyTranslationY();
}
+ private void setTaskResistanceTranslationX(float x) {
+ mTaskResistanceTranslationX = x;
+ applyTranslationX();
+ }
+
+ private void setTaskResistanceTranslationY(float y) {
+ mTaskResistanceTranslationY = y;
+ applyTranslationY();
+ }
+
+ private void setFullscreenTranslationX(float fullscreenTranslationX) {
+ mFullscreenTranslationX = fullscreenTranslationX;
+ applyTranslationX();
+ }
+
+ public float getFullscreenTranslationX() {
+ return mFullscreenTranslationX;
+ }
+
+ public void setAccumulatedFullscreenTranslationX(float accumulatedFullscreenTranslationX) {
+ mAccumulatedFullscreenTranslationX = accumulatedFullscreenTranslationX;
+ applyTranslationX();
+ }
+
+ public void setGridTranslationX(float gridTranslationX) {
+ mGridTranslationX = gridTranslationX;
+ applyTranslationX();
+ }
+
+ public float getGridTranslationX() {
+ return mGridTranslationX;
+ }
+
+ public void setGridTranslationY(float gridTranslationY) {
+ mGridTranslationY = gridTranslationY;
+ applyTranslationY();
+ }
+
+ public float getGridTranslationY() {
+ return mGridTranslationY;
+ }
+
+ public void setGridOffsetTranslationX(float gridOffsetTranslationX) {
+ mGridOffsetTranslationX = gridOffsetTranslationX;
+ applyTranslationX();
+ }
+
+ public void setNonRtlVisibleOffset(float nonRtlVisibleOffset) {
+ mNonRtlVisibleOffset = nonRtlVisibleOffset;
+ }
+
+ public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+ float scrollAdjustment = 0;
+ if (fullscreenEnabled) {
+ scrollAdjustment += mFullscreenTranslationX + mAccumulatedFullscreenTranslationX;
+ }
+ if (gridEnabled) {
+ scrollAdjustment += mGridTranslationX;
+ }
+ return scrollAdjustment;
+ }
+
+ public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+ float offsetAdjustment = getScrollAdjustment(fullscreenEnabled, gridEnabled);
+ if (gridEnabled) {
+ offsetAdjustment += mGridOffsetTranslationX + mNonRtlVisibleOffset;
+ }
+ return offsetAdjustment;
+ }
+
+ public float getSizeAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+ float sizeAdjustment = 1;
+ if (fullscreenEnabled) {
+ sizeAdjustment *= mFullscreenScale;
+ }
+ if (gridEnabled) {
+ sizeAdjustment *= mGridScale;
+ }
+ return sizeAdjustment;
+ }
+
+ private void setBoxTranslationY(float boxTranslationY) {
+ mBoxTranslationY = boxTranslationY;
+ applyTranslationY();
+ }
+
private void applyTranslationX() {
- setTranslationX(mFillDismissGapTranslationX + mTaskOffsetTranslationX);
+ setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
+ + getFullscreenTrans(mFullscreenTranslationX + mAccumulatedFullscreenTranslationX)
+ + getGridTrans(mGridTranslationX + mGridOffsetTranslationX));
}
private void applyTranslationY() {
- setTranslationY(mFillDismissGapTranslationY + mTaskOffsetTranslationY);
+ setTranslationY(
+ mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
+ + getGridTrans(mGridTranslationY) + mBoxTranslationY);
}
- public FloatProperty<TaskView> getPrimaryFillDismissGapTranslationProperty() {
+ private float getGridTrans(float endTranslation) {
+ float progress = ACCEL_DEACCEL.getInterpolation(mGridProgress);
+ return Utilities.mapRange(progress, 0, endTranslation);
+ }
+
+ public FloatProperty<TaskView> getFillDismissGapTranslationProperty() {
return getPagedOrientationHandler().getPrimaryValue(
- FILL_DISMISS_GAP_TRANSLATION_X, FILL_DISMISS_GAP_TRANSLATION_Y);
+ DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
+ }
+
+ public FloatProperty<TaskView> getDismissTaskTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
}
public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
@@ -934,12 +1005,25 @@
TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
}
+ public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
+ }
+
@Override
public boolean hasOverlappingRendering() {
// TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
return false;
}
+ public boolean isEndQuickswitchCuj() {
+ return mEndQuickswitchCuj;
+ }
+
+ public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
+ mEndQuickswitchCuj = endQuickswitchCuj;
+ }
+
private static final class TaskOutlineProvider extends ViewOutlineProvider {
private final int mMarginTop;
@@ -967,71 +1051,6 @@
}
}
- private class FooterWrapper extends ViewOutlineProvider {
-
- final View mView;
- final ViewOutlineProvider mOldOutlineProvider;
- final ViewOutlineProvider mDelegate;
-
- final int mExpectedHeight;
- final int mOldPaddingBottom;
-
- int mAnimationOffset = 0;
- int mEntryAnimationOffset = 0;
-
- public FooterWrapper(View view) {
- mView = view;
- mOldOutlineProvider = view.getOutlineProvider();
- mDelegate = mOldOutlineProvider == null
- ? ViewOutlineProvider.BACKGROUND : mOldOutlineProvider;
-
- mExpectedHeight = getExpectedViewHeight(view);
- mOldPaddingBottom = view.getPaddingBottom();
-
- if (mOldOutlineProvider != null) {
- view.setOutlineProvider(this);
- view.setClipToOutline(true);
- }
- }
-
- public void setVerticalShift(int shift) {
- mView.setPadding(mView.getPaddingLeft(), mView.getPaddingTop(),
- mView.getPaddingRight(), mOldPaddingBottom + shift);
- }
-
- @Override
- public void getOutline(View view, Outline outline) {
- mDelegate.getOutline(view, outline);
- outline.offset(0, -mAnimationOffset - mEntryAnimationOffset);
- }
-
- void updateFooterOffset() {
- float offset = Utilities.or(mFooterVerticalOffset, mModalness);
- mAnimationOffset = Math.round(mStackHeight * offset);
- mView.setTranslationY(mAnimationOffset + mEntryAnimationOffset
- + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom
- + mCurrentFullscreenParams.mCurrentDrawnInsets.top);
- mView.invalidateOutline();
- }
-
- void release() {
- mView.setOutlineProvider(mOldOutlineProvider);
- setVerticalShift(0);
- }
-
- void animateEntry() {
- ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
- animator.addUpdateListener(anim -> {
- float factor = 1 - anim.getAnimatedFraction();
- int totalShift = mExpectedHeight + mView.getPaddingBottom() - mOldPaddingBottom;
- mEntryAnimationOffset = Math.round(factor * totalShift);
- updateFooterOffset();
- });
- animator.setDuration(100);
- animator.start();
- }
- }
-
private int getExpectedViewHeight(View view) {
int expectedHeight;
int h = view.getLayoutParams().height;
@@ -1121,10 +1140,12 @@
public void setFullscreenProgress(float progress) {
progress = Utilities.boundToRange(progress, 0, 1);
mFullscreenProgress = progress;
- boolean isFullscreen = mFullscreenProgress > 0;
mIconView.setVisibility(progress < 1 ? VISIBLE : INVISIBLE);
- setClipChildren(!isFullscreen);
- setClipToPadding(!isFullscreen);
+ getThumbnail().getTaskOverlay().setFullscreenProgress(progress);
+
+ applyTranslationX();
+ applyTranslationY();
+ applyScale();
TaskThumbnailView thumbnail = getThumbnail();
updateCurrentFullscreenParams(thumbnail.getPreviewPositionHelper());
@@ -1151,6 +1172,77 @@
previewPositionHelper);
}
+ /**
+ * Updates TaskView scaling and translation required to support variable width if enabled, while
+ * ensuring TaskView fits into screen in fullscreen.
+ */
+ void updateTaskSize() {
+ ViewGroup.LayoutParams params = getLayoutParams();
+ if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+ final int thumbnailPadding = (int) getResources().getDimension(
+ R.dimen.task_thumbnail_top_margin);
+
+ Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
+ int taskWidth = lastComputedTaskSize.width();
+ int taskHeight = lastComputedTaskSize.height();
+
+ int expectedWidth;
+ int expectedHeight;
+ float thumbnailRatio = mTask != null ? mTask.getVisibleThumbnailRatio() : 0f;
+ if (isRunningTask() || thumbnailRatio == 0f) {
+ expectedWidth = taskWidth;
+ expectedHeight = taskHeight + thumbnailPadding;
+ } else {
+ int boxLength = Math.max(taskWidth, taskHeight);
+ if (thumbnailRatio > 1) {
+ expectedWidth = boxLength;
+ expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding;
+ } else {
+ expectedWidth = (int) (boxLength * thumbnailRatio);
+ expectedHeight = boxLength + thumbnailPadding;
+ }
+ }
+
+ float heightDiff = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
+ setBoxTranslationY(heightDiff);
+
+ float fullscreenScale = 1f;
+ if (expectedWidth > taskWidth) {
+ // In full screen, expectedWidth should not be larger than taskWidth.
+ fullscreenScale = taskWidth / (float) expectedWidth;
+ } else if (expectedHeight - thumbnailPadding > taskHeight) {
+ // In full screen, expectedHeight should not be larger than taskHeight.
+ fullscreenScale = taskHeight / (float) (expectedHeight - thumbnailPadding);
+ }
+ setFullscreenScale(fullscreenScale);
+
+ float widthDiff = params.width * (1 - mFullscreenScale);
+ setFullscreenTranslationX(
+ getLayoutDirection() == LAYOUT_DIRECTION_RTL ? -widthDiff : widthDiff);
+
+ if (params.width != expectedWidth || params.height != expectedHeight) {
+ params.width = expectedWidth;
+ params.height = expectedHeight;
+ setLayoutParams(params);
+ }
+ } else {
+ setBoxTranslationY(0);
+ setFullscreenTranslationX(0);
+ setFullscreenScale(1);
+ if (params.width != ViewGroup.LayoutParams.MATCH_PARENT) {
+ params.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ params.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ setLayoutParams(params);
+ }
+ }
+ }
+
+
+ private float getFullscreenTrans(float endTranslation) {
+ float progress = ACCEL_DEACCEL.getInterpolation(mFullscreenProgress);
+ return Utilities.mapRange(progress, 0, endTranslation);
+ }
+
public boolean isRunningTask() {
if (getRecentsView() == null) {
return false;
diff --git a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java b/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
deleted file mode 100644
index 5904fcd..0000000
--- a/quickstep/tests/src/com/android/quickstep/AppPredictionsUITests.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.quickstep;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-import android.app.prediction.AppPredictor;
-import android.app.prediction.AppTarget;
-import android.app.prediction.AppTargetId;
-import android.content.ComponentName;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.os.Process;
-import android.view.View;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.appprediction.PredictionRowView;
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-import com.android.launcher3.model.AppLaunchTracker;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class AppPredictionsUITests extends AbstractQuickStepTest {
-
- private LauncherActivityInfo mSampleApp1;
- private LauncherActivityInfo mSampleApp2;
- private LauncherActivityInfo mSampleApp3;
-
- private AppPredictor.Callback mCallback;
-
- @Before
- public void setUp() throws Exception {
- super.setUp();
-
- List<LauncherActivityInfo> activities = mTargetContext.getSystemService(LauncherApps.class)
- .getActivityList(null, Process.myUserHandle());
- mSampleApp1 = activities.get(0);
- mSampleApp2 = activities.get(1);
- mSampleApp3 = activities.get(2);
-
- // Disable app tracker
- AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
- PredictionUiStateManager.INSTANCE.initializeForTesting(null);
-
- mCallback = PredictionUiStateManager.INSTANCE.get(mTargetContext).appPredictorCallback(
- Client.HOME);
-
- mDevice.setOrientationNatural();
- }
-
- @After
- public void tearDown() throws Throwable {
- AppLaunchTracker.INSTANCE.initializeForTesting(null);
- PredictionUiStateManager.INSTANCE.initializeForTesting(null);
- mDevice.unfreezeRotation();
- }
-
- /**
- * Test that prediction UI is updated as soon as we get predictions from the system
- */
- @Test
- public void testPredictionExistsInAllApps() {
- mLauncher.pressHome().switchToAllApps();
-
- // Dispatch an update
- sendPredictionUpdate(mSampleApp1, mSampleApp2);
- // The first update should apply immediately.
- waitForLauncherCondition("Predictions were not updated in loading state",
- launcher -> getPredictedApp(launcher).size() == 2);
- }
-
- /**
- * Test that prediction update is deferred if it is already visible
- */
- @Test
- public void testPredictionsDeferredUntilHome() {
- mDevice.pressHome();
- sendPredictionUpdate(mSampleApp1, mSampleApp2);
- mLauncher.pressHome().switchToAllApps();
- waitForLauncherCondition("Predictions were not updated in loading state",
- launcher -> getPredictedApp(launcher).size() == 2);
-
- // Update predictions while all-apps is visible
- sendPredictionUpdate(mSampleApp1, mSampleApp2, mSampleApp3);
- assertEquals(2, getFromLauncher(this::getPredictedApp).size());
-
- // Go home and go back to all-apps
- mLauncher.pressHome().switchToAllApps();
- assertEquals(3, getFromLauncher(this::getPredictedApp).size());
- }
-
- @Test
- public void testPredictionsDisabled() {
- mDevice.pressHome();
- sendPredictionUpdate();
- mLauncher.pressHome().switchToAllApps();
-
- waitForLauncherCondition("Predictions were not updated in loading state",
- launcher -> launcher.getAppsView().getFloatingHeaderView()
- .findFixedRowByType(PredictionRowView.class).getVisibility() == View.GONE);
- assertFalse(PredictionUiStateManager.INSTANCE.get(mTargetContext)
- .getCurrentState().isEnabled);
- }
-
- public ArrayList<BubbleTextView> getPredictedApp(Launcher launcher) {
- PredictionRowView container = launcher.getAppsView().getFloatingHeaderView()
- .findFixedRowByType(PredictionRowView.class);
-
- ArrayList<BubbleTextView> predictedAppViews = new ArrayList<>();
- for (int i = 0; i < container.getChildCount(); i++) {
- View view = container.getChildAt(i);
- if (view instanceof BubbleTextView && view.getVisibility() == View.VISIBLE) {
- predictedAppViews.add((BubbleTextView) view);
- }
- }
- return predictedAppViews;
- }
-
- private void sendPredictionUpdate(LauncherActivityInfo... activities) {
- getOnUiThread(() -> {
- List<AppTarget> targets = new ArrayList<>(activities.length);
- for (LauncherActivityInfo info : activities) {
- ComponentName cn = info.getComponentName();
- AppTarget target = new AppTarget.Builder(
- new AppTargetId("app:" + cn), cn.getPackageName(), info.getUser())
- .setClassName(cn.getClassName())
- .build();
- targets.add(target);
- }
- mCallback.onTargetsAvailable(targets);
- return null;
- });
- }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
index ccfa3fc..2a7da7e 100644
--- a/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
+++ b/quickstep/tests/src/com/android/quickstep/DigitalWellBeingToastTest.java
@@ -3,6 +3,8 @@
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -11,12 +13,12 @@
import android.app.PendingIntent;
import android.app.usage.UsageStatsManager;
import android.content.Intent;
-import android.os.Build;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.Launcher;
+import com.android.launcher3.util.rule.TestStabilityRule;
import com.android.quickstep.views.DigitalWellBeingToast;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -34,9 +36,6 @@
@Test
public void testToast() throws Exception {
- // b/150303529
- if (Build.MODEL.contains("Cuttlefish")) return;
-
startAppFast(CALCULATOR_PACKAGE);
final UsageStatsManager usageStatsManager =
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index b9e0f62..713fd07 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
@@ -41,6 +42,7 @@
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.RemoteException;
+import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -54,8 +56,8 @@
import com.android.launcher3.tapl.OverviewTask;
import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.testcomponent.TestCommandReceiver;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.FailureRewriterRule;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.quickstep.views.RecentsView;
@@ -66,6 +68,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -100,8 +104,7 @@
}
mOrderSensitiveRules = RuleChain
- .outerRule(new FailureRewriterRule())
- .around(new NavigationModeSwitchRule(mLauncher))
+ .outerRule(new NavigationModeSwitchRule(mLauncher))
.around(new FailureWatcher(mDevice));
mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
@@ -112,11 +115,16 @@
@Override
public void evaluate() throws Throwable {
TestCommandReceiver.callCommand(TestCommandReceiver.ENABLE_TEST_LAUNCHER);
+ OverviewUpdateHandler updateHandler =
+ MAIN_EXECUTOR.submit(OverviewUpdateHandler::new).get();
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
getLauncherCommand(mOtherLauncherActivity));
+ updateHandler.mChangeCounter
+ .await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
try {
base.evaluate();
} finally {
+ MAIN_EXECUTOR.submit(updateHandler::destroy).get();
TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
getLauncherCommand(getLauncherInMyProcess()));
@@ -166,9 +174,15 @@
protected <T> T getFromRecents(Function<RecentsActivity, T> f) {
if (!TestHelpers.isInLauncherProcess()) return null;
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "getFromRecents");
+ }
Object[] result = new Object[1];
Wait.atMost("Failed to get from recents", () -> MAIN_EXECUTOR.submit(() -> {
RecentsActivity activity = RecentsActivity.ACTIVITY_TRACKER.getCreatedActivity();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "activity=" + activity);
+ }
if (activity == null) {
return false;
}
@@ -194,8 +208,13 @@
() -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
BaseOverview overview = mLauncher.getBackground().switchToOverview();
- executeOnRecents(recents ->
- assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3));
+ executeOnRecents(recents -> {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.GET_RECENTS_FAILED, "isLoading=" +
+ recents.<RecentsView>getOverviewPanel().isLoadingTasks());
+ }
+ assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3);
+ });
// Test flinging forward and backward.
overview.flingForward();
@@ -213,7 +232,7 @@
OverviewTask task = overview.getCurrentTask();
assertNotNull("overview.getCurrentTask() returned null (1)", task);
assertNotNull("OverviewTask.open returned null", task.open());
- assertTrue("Test activity didn't open from Overview", mDevice.wait(Until.hasObject(
+ assertTrue("Test activity didn't open from Overview", TestHelpers.wait(Until.hasObject(
By.pkg(getAppPackageName()).text("TestActivity2")),
DEFAULT_UI_TIMEOUT));
@@ -230,7 +249,7 @@
// Test dismissing all tasks.
pressHomeAndGoToOverview().dismissAllTasks();
- assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
+ assertTrue("Fallback Launcher not visible", TestHelpers.wait(Until.hasObject(By.pkg(
mOtherLauncherActivity.packageName)), WAIT_TIME_MS));
}
@@ -241,4 +260,30 @@
private int getTaskCount(RecentsActivity recents) {
return recents.<RecentsView>getOverviewPanel().getTaskViewCount();
}
+
+ private class OverviewUpdateHandler {
+
+ final RecentsAnimationDeviceState mRads;
+ final OverviewComponentObserver mObserver;
+ final CountDownLatch mChangeCounter;
+
+ OverviewUpdateHandler() {
+ Context ctx = getInstrumentation().getTargetContext();
+ mRads = new RecentsAnimationDeviceState(ctx);
+ mObserver = new OverviewComponentObserver(ctx, mRads);
+ mChangeCounter = new CountDownLatch(1);
+ if (mObserver.getHomeIntent().getComponent()
+ .getPackageName().equals(mOtherLauncherActivity.packageName)) {
+ // Home already same
+ mChangeCounter.countDown();
+ } else {
+ mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+ }
+ }
+
+ void destroy() {
+ mObserver.onDestroy();
+ mRads.destroy();
+ }
+ }
}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 40265c4..67840d1 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -168,7 +168,7 @@
Log.d(TAG, "setActiveOverlay: " + overlayPackage + "...");
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
- "cmd overlay enable-exclusive " + overlayPackage);
+ "cmd overlay enable-exclusive --category " + overlayPackage);
if (currentSysUiNavigationMode() != expectedMode) {
final CountDownLatch latch = new CountDownLatch(1);
@@ -188,7 +188,7 @@
SYS_UI_NAVIGATION_MODE.removeModeChangeListener(listener));
Wait.atMost(() -> "Navigation mode didn't change to " + expectedMode,
- () -> currentSysUiNavigationMode() == expectedMode, 60000 /* b/148422894 */,
+ () -> currentSysUiNavigationMode() == expectedMode, WAIT_TIME_MS,
launcher);
// b/139137636
// assertTrue(launcher, "Navigation mode didn't change to " + expectedMode,
@@ -200,9 +200,9 @@
() -> launcher.getNavigationModel() == expectedMode, WAIT_TIME_MS, launcher);
Wait.atMost(() -> "Switching nav mode: "
- + launcher.getNavigationModeMismatchError(),
- () -> launcher.getNavigationModeMismatchError() == null,
- 60000 /* b/148422894 */, launcher);
+ + launcher.getNavigationModeMismatchError(false),
+ () -> launcher.getNavigationModeMismatchError(false) == null,
+ WAIT_TIME_MS, launcher);
AbstractLauncherUiTest.checkDetectedLeaks(launcher);
return true;
}
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index ecd4e2b..0fe5432 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -31,14 +31,11 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.tapl.AllApps;
-import com.android.launcher3.tapl.AllAppsFromOverview;
import com.android.launcher3.tapl.Background;
import com.android.launcher3.tapl.LauncherInstrumentation.NavigationModel;
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewActions;
import com.android.launcher3.tapl.OverviewTask;
-import com.android.launcher3.tapl.TestHelpers;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
@@ -94,19 +91,6 @@
}
@Test
- public void testAllAppsFromOverview() throws Exception {
- if (!mLauncher.hasAllAppsInOverview()) {
- return;
- }
-
- // Test opening all apps from Overview.
- assertNotNull("switchToAllApps() returned null",
- mLauncher.getWorkspace().switchToOverview().switchToAllApps());
-
- TaplTestsLauncher3.runAllAppsTest(this, mLauncher.getAllAppsFromOverview());
- }
-
- @Test
@PortraitLandscape
public void testOverview() throws Exception {
startTestAppsWithCheck();
@@ -159,28 +143,6 @@
launcher -> assertEquals("Dismissing a task didn't remove 1 task from Overview",
numTasks - 1, getTaskCount(launcher)));
- if (mLauncher.hasAllAppsInOverview() && (!TestHelpers.isInLauncherProcess()
- || getFromLauncher(launcher -> !launcher.getDeviceProfile().isLandscape))) {
- // Test switching to all apps and back.
- final AllAppsFromOverview allApps = overview.switchToAllApps();
- assertNotNull("overview.switchToAllApps() returned null (1)", allApps);
- assertTrue("Launcher internal state is not All Apps (1)",
- isInState(() -> LauncherState.ALL_APPS));
-
- overview = allApps.switchBackToOverview();
- assertNotNull("allApps.switchBackToOverview() returned null", overview);
- assertTrue("Launcher internal state didn't switch to Overview",
- isInState(() -> LauncherState.OVERVIEW));
-
- // Test UIDevice.pressBack()
- overview.switchToAllApps();
- assertNotNull("overview.switchToAllApps() returned null (2)", allApps);
- assertTrue("Launcher internal state is not All Apps (2)",
- isInState(() -> LauncherState.ALL_APPS));
- mDevice.pressBack();
- mLauncher.getOverview();
- }
-
// Test UIDevice.pressHome, once we are in AllApps.
mDevice.pressHome();
waitForState("Launcher internal state didn't switch to Home", () -> LauncherState.NORMAL);
@@ -200,13 +162,17 @@
@NavigationModeSwitch
@PortraitLandscape
public void testOverviewActions() throws Exception {
- if (mLauncher.getNavigationModel() != NavigationModel.TWO_BUTTON) {
- startTestAppsWithCheck();
- OverviewActions actionsView =
- mLauncher.pressHome().switchToOverview().getOverviewActions();
- actionsView.clickAndDismissScreenshot();
- actionsView.clickAndDismissShare();
- }
+ // Experimenting for b/165029151:
+ final Overview overview = mLauncher.pressHome().switchToOverview();
+ if (overview.hasTasks()) overview.dismissAllTasks();
+ mLauncher.pressHome();
+ //
+
+ startTestAppsWithCheck();
+ OverviewActions actionsView =
+ mLauncher.pressHome().switchToOverview().getOverviewActions();
+ actionsView.clickAndDismissScreenshot();
+ actionsView.clickAndDismissShare();
}
private int getCurrentOverviewPage(Launcher launcher) {
@@ -218,20 +184,6 @@
}
@Test
- public void testAppIconLaunchFromAllAppsFromOverview() throws Exception {
- if (!mLauncher.hasAllAppsInOverview()) {
- return;
- }
-
- final AllApps allApps =
- mLauncher.getWorkspace().switchToOverview().switchToAllApps();
- assertTrue("Launcher internal state is not All Apps",
- isInState(() -> LauncherState.ALL_APPS));
-
- TaplTestsLauncher3.runIconLaunchFromAllAppsTest(this, allApps);
- }
-
- @Test
@NavigationModeSwitch
@PortraitLandscape
public void testSwitchToOverview() throws Exception {
@@ -304,6 +256,10 @@
assertTrue("The second app we should have quick switched to is not running",
isTestActivityRunning(2));
}
+ background = getAndAssertBackground();
+ background.quickSwitchToPreviousAppSwipeLeft();
+ assertTrue("The 2nd app we should have quick switched to is not running",
+ isTestActivityRunning(3));
getAndAssertBackground();
}
diff --git a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
index 115294a..4ca1f59 100644
--- a/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
+++ b/quickstep/tests/src/com/android/quickstep/ViewInflationDuringSwipeUp.java
@@ -49,7 +49,6 @@
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.Background;
@@ -58,6 +57,7 @@
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.ui.TaplTestsLauncher3;
import com.android.launcher3.ui.TestViewHelpers;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import org.junit.Before;
@@ -71,7 +71,7 @@
/**
* Test to verify view inflation does not happen during swipe up.
- * To verify view inflation, we setup a dummy ViewConfiguration and check if any call to that class
+ * To verify view inflation, we setup a stub ViewConfiguration and check if any call to that class
* does from a View.init method or not.
*
* Alternative approaches considered:
@@ -138,13 +138,13 @@
@Test
@NavigationModeSwitch(mode = ZERO_BUTTON)
public void testSwipeUpFromApp_widget_update() {
- String dummyText = "Some random dummy text";
+ String stubText = "Some random stub text";
executeSwipeUpTestWithWidget(
widgetId -> { },
widgetId -> AppWidgetManager.getInstance(getContext())
- .updateAppWidget(widgetId, createMainWidgetViews(dummyText)),
- dummyText);
+ .updateAppWidget(widgetId, createMainWidgetViews(stubText)),
+ stubText);
}
@Test
diff --git a/res/animator-v23/discovery_bounce.xml b/res/animator-v23/discovery_bounce.xml
deleted file mode 100644
index f554853..0000000
--- a/res/animator-v23/discovery_bounce.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:duration="2166"
- android:repeatCount="5">
- <propertyValuesHolder
- android:propertyName="progress"
- android:valueType="floatType">
- <keyframe
- android:fraction="0"
- android:value="1f" />
- <keyframe
- android:fraction="0.246"
- android:value="1f" />
- <keyframe
- android:fraction=".423"
- android:interpolator="@interpolator/disco_bounce"
- android:value="0.9738f" />
- <keyframe
- android:fraction="0.754"
- android:interpolator="@interpolator/disco_bounce"
- android:value="1f" />
- <keyframe
- android:fraction="1"
- android:value="1f" />
- </propertyValuesHolder>
-</objectAnimator>
diff --git a/res/animator/discovery_bounce.xml b/res/animator/discovery_bounce.xml
index f02ebdb..f554853 100644
--- a/res/animator/discovery_bounce.xml
+++ b/res/animator/discovery_bounce.xml
@@ -16,20 +16,28 @@
** limitations under the License.
*/
-->
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:ordering="sequentially">
- <objectAnimator
- android:duration="166"
- android:interpolator="@interpolator/disco_bounce"
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="2166"
+ android:repeatCount="5">
+ <propertyValuesHolder
android:propertyName="progress"
- android:startOffset="750"
- android:valueFrom="1f"
- android:valueTo="0.9438f"
- android:valueType="floatType" />
- <objectAnimator
- android:duration="500"
- android:interpolator="@interpolator/disco_bounce"
- android:propertyName="progress"
- android:valueTo="1f"
- android:valueType="floatType" />
-</set>
+ android:valueType="floatType">
+ <keyframe
+ android:fraction="0"
+ android:value="1f" />
+ <keyframe
+ android:fraction="0.246"
+ android:value="1f" />
+ <keyframe
+ android:fraction=".423"
+ android:interpolator="@interpolator/disco_bounce"
+ android:value="0.9738f" />
+ <keyframe
+ android:fraction="0.754"
+ android:interpolator="@interpolator/disco_bounce"
+ android:value="1f" />
+ <keyframe
+ android:fraction="1"
+ android:value="1f" />
+ </propertyValuesHolder>
+</objectAnimator>
diff --git a/res/color-v24/all_apps_bg_hand_fill.xml b/res/color/all_apps_bg_hand_fill.xml
similarity index 100%
rename from res/color-v24/all_apps_bg_hand_fill.xml
rename to res/color/all_apps_bg_hand_fill.xml
diff --git a/res/color-v24/all_apps_bg_hand_fill_dark.xml b/res/color/all_apps_bg_hand_fill_dark.xml
similarity index 100%
rename from res/color-v24/all_apps_bg_hand_fill_dark.xml
rename to res/color/all_apps_bg_hand_fill_dark.xml
diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png
deleted file mode 100644
index a710932..0000000
--- a/res/drawable-hdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 7cb5214..0000000
--- a/res/drawable-hdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png
deleted file mode 100644
index 252482f..0000000
--- a/res/drawable-mdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png
deleted file mode 100644
index a2010e2..0000000
--- a/res/drawable-mdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-v26/ic_deepshortcut_placeholder.xml b/res/drawable-v26/ic_deepshortcut_placeholder.xml
deleted file mode 100644
index 3fa8506..0000000
--- a/res/drawable-v26/ic_deepshortcut_placeholder.xml
+++ /dev/null
@@ -1,20 +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.
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background android:drawable="?attr/popupColorSecondary"/>
- <foreground android:drawable="?attr/popupColorSecondary"/>
-</adaptive-icon>
diff --git a/res/drawable-v26/ic_launcher_home.xml b/res/drawable-v26/ic_launcher_home.xml
deleted file mode 100644
index 7038775..0000000
--- a/res/drawable-v26/ic_launcher_home.xml
+++ /dev/null
@@ -1,21 +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.
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
- <background android:drawable="@color/icon_background" />
- <foreground>
- <bitmap android:src="@mipmap/ic_launcher_home_foreground"/>
- </foreground>
-</adaptive-icon>
diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png
deleted file mode 100644
index 563c75d..0000000
--- a/res/drawable-xhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 2b1ac05..0000000
--- a/res/drawable-xhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png
deleted file mode 100644
index ea527f4..0000000
--- a/res/drawable-xxhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 5412168..0000000
--- a/res/drawable-xxhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png
deleted file mode 100644
index 4644e9a..0000000
--- a/res/drawable-xxxhdpi/widget_resize_frame.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
deleted file mode 100644
index 63cea84..0000000
--- a/res/drawable-xxxhdpi/widget_resize_shadow.9.png
+++ /dev/null
Binary files differ
diff --git a/go/res/values/dimens.xml b/res/drawable/bg_widgets_searchbox.xml
similarity index 71%
copy from go/res/values/dimens.xml
copy to res/drawable/bg_widgets_searchbox.xml
index f1b1053..81dd2aa 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/bg_widgets_searchbox.xml
@@ -1,5 +1,5 @@
<?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.
@@ -13,8 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="#FFFFF7" />
+ <corners android:radius="24dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/drag_handle_indicator_no_shadow.xml b/res/drawable/drag_handle_indicator_no_shadow.xml
deleted file mode 100644
index 341e60c..0000000
--- a/res/drawable/drag_handle_indicator_no_shadow.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/vertical_drag_handle_width"
- android:height="@dimen/vertical_drag_handle_height"
- android:viewportWidth="18.0"
- android:viewportHeight="6.0"
- android:tint="?attr/workspaceTextColor" >
-
- <path
- android:pathData="M17,6c-0.15,0-0.3-0.03-0.45-0.11L9,2.12L1.45,5.89c-0.5,0.25-1.09,
- 0.05-1.34-0.45S0.06,4.35,0.55,4.11l8-4c0.28-0.14,0.61-0.14,0.89,0l8,4c0.49,0.25,0.69,
- 0.85,0.45,1.34C17.72,5.8,17.37,6,17,6z"
- android:fillColor="@android:color/white" />
-</vector>
diff --git a/res/drawable/ic_allapps_search.xml b/res/drawable/ic_allapps_search.xml
index 2aeb947..c0e20f1 100644
--- a/res/drawable/ic_allapps_search.xml
+++ b/res/drawable/ic_allapps_search.xml
@@ -19,6 +19,6 @@
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
- android:fillColor="?android:attr/colorAccent"
+ android:fillColor="?android:attr/textColorTertiary"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector>
diff --git a/res/drawable-v24/ic_block_shadow.xml b/res/drawable/ic_block_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_block_shadow.xml
rename to res/drawable/ic_block_shadow.xml
diff --git a/res/drawable/ic_deepshortcut_placeholder.xml b/res/drawable/ic_deepshortcut_placeholder.xml
index 85a9694..3fa8506 100644
--- a/res/drawable/ic_deepshortcut_placeholder.xml
+++ b/res/drawable/ic_deepshortcut_placeholder.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+ 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.
@@ -13,10 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
- <solid android:color="?attr/popupColorSecondary" />
- <size
- android:height="32dp"
- android:width="32dp" />
-</shape>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="?attr/popupColorSecondary"/>
+ <foreground android:drawable="?attr/popupColorSecondary"/>
+</adaptive-icon>
diff --git a/res/drawable/ic_expand_less.xml b/res/drawable/ic_expand_less.xml
new file mode 100644
index 0000000..8360cee
--- /dev/null
+++ b/res/drawable/ic_expand_less.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorHint">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18.59,16.41L20,15l-8,-8 -8,8 1.41,1.41L12,9.83"/>
+</vector>
diff --git a/res/drawable/ic_expand_more.xml b/res/drawable/ic_expand_more.xml
new file mode 100644
index 0000000..49e24f6
--- /dev/null
+++ b/res/drawable/ic_expand_more.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorHint">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/>
+</vector>
diff --git a/res/drawable/ic_launcher_home.xml b/res/drawable/ic_launcher_home.xml
index a6f2519..7038775 100644
--- a/res/drawable/ic_launcher_home.xml
+++ b/res/drawable/ic_launcher_home.xml
@@ -13,6 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<bitmap
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@mipmap/ic_launcher_home" />
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@color/icon_background" />
+ <foreground>
+ <bitmap android:src="@mipmap/ic_launcher_home_foreground"/>
+ </foreground>
+</adaptive-icon>
diff --git a/res/drawable-v24/ic_remove_shadow.xml b/res/drawable/ic_remove_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_remove_shadow.xml
rename to res/drawable/ic_remove_shadow.xml
diff --git a/res/drawable-v24/ic_setup_shadow.xml b/res/drawable/ic_setup_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_setup_shadow.xml
rename to res/drawable/ic_setup_shadow.xml
diff --git a/res/drawable-v24/ic_uninstall_shadow.xml b/res/drawable/ic_uninstall_shadow.xml
similarity index 100%
rename from res/drawable-v24/ic_uninstall_shadow.xml
rename to res/drawable/ic_uninstall_shadow.xml
diff --git a/res/drawable/ic_widget_height_decrease.xml b/res/drawable/ic_widget_height_decrease.xml
new file mode 100644
index 0000000..df704ba
--- /dev/null
+++ b/res/drawable/ic_widget_height_decrease.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorPrimary">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M8,19h3v3h2v-3h3l-4,-4 -4,4zM16,4h-3L13,1h-2v3L8,4l4,4 4,-4zM4,9v2h16L20,9L4,9zM4,12h16v2H4z"/>
+</vector>
diff --git a/res/drawable/ic_widget_height_increase.xml b/res/drawable/ic_widget_height_increase.xml
new file mode 100644
index 0000000..c263a4b
--- /dev/null
+++ b/res/drawable/ic_widget_height_increase.xml
@@ -0,0 +1,25 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorPrimary">
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M4,20h16v2L4,22zM4,2h16v2L4,4zM13,9h3l-4,-4 -4,4h3v6L8,15l4,4 4,-4h-3z"/>
+</vector>
diff --git a/res/drawable/ic_widget_width_decrease.xml b/res/drawable/ic_widget_width_decrease.xml
new file mode 100644
index 0000000..2a2fad7
--- /dev/null
+++ b/res/drawable/ic_widget_width_decrease.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<rotate
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_widget_height_decrease"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fromDegrees="90"
+ android:toDegrees="90" />
diff --git a/res/drawable/ic_widget_width_increase.xml b/res/drawable/ic_widget_width_increase.xml
new file mode 100644
index 0000000..89b9f40
--- /dev/null
+++ b/res/drawable/ic_widget_width_increase.xml
@@ -0,0 +1,22 @@
+<!--
+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.
+-->
+<rotate
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/ic_widget_height_increase"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fromDegrees="90"
+ android:toDegrees="90" />
diff --git a/go/res/values/dimens.xml b/res/drawable/widget_resize_frame.xml
similarity index 61%
copy from go/res/values/dimens.xml
copy to res/drawable/widget_resize_frame.xml
index f1b1053..d157f5d 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/widget_resize_frame.xml
@@ -1,5 +1,6 @@
<?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.
@@ -13,8 +14,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="@android:color/transparent" />
+ <corners android:radius="@android:dimen/system_app_widget_background_radius" />
+ <stroke android:width="2dp" android:color="?android:attr/colorAccent" />
+</shape>
\ No newline at end of file
diff --git a/go/res/values/dimens.xml b/res/drawable/widgets_tray_expand_button.xml
similarity index 64%
copy from go/res/values/dimens.xml
copy to res/drawable/widgets_tray_expand_button.xml
index f1b1053..8316e0f 100644
--- a/go/res/values/dimens.xml
+++ b/res/drawable/widgets_tray_expand_button.xml
@@ -1,5 +1,5 @@
<?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.
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<resources>
- <!-- Dynamic Grid -->
- <dimen name="dynamic_grid_hotseat_size">60dp</dimen>
-</resources>
\ No newline at end of file
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_checked="true"
+ android:drawable="@drawable/ic_expand_less" />
+ <item android:state_checked="false"
+ android:drawable="@drawable/ic_expand_more" />
+</selector>
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 830255b..b1a1efe 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -46,7 +46,7 @@
android:background="?android:attr/colorPrimaryDark"
android:theme="?attr/widgetsTheme">
- <com.android.launcher3.dragndrop.LivePreviewWidgetCell
+ <com.android.launcher3.widget.WidgetCell
android:id="@+id/widget_cell"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -59,7 +59,7 @@
<include layout="@layout/widget_cell_content" />
- </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
+ </com.android.launcher3.widget.WidgetCell>
</FrameLayout>
</LinearLayout>
</ScrollView>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 67ec664..8ed16c7 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<?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.
@@ -16,8 +15,7 @@
<!-- The top and bottom paddings are defined in this container, but since we want
the list view to span the full width (for touch interception purposes), we
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"
+<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"
@@ -42,35 +40,7 @@
<include layout="@layout/floating_header_content" />
- <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
- android:id="@+id/tabs"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_header_tab_height"
- android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
- android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
- 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: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:textColor="@color/all_apps_tab_text"
- android:textSize="14sp" />
- </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+ <include layout="@layout/personal_work_tabs" />
</com.android.launcher3.allapps.FloatingHeaderView>
<include
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 79fb612..069954c 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<?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.
@@ -13,16 +12,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.BubbleTextView
- xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
- style="@style/BaseIcon"
+ style="@style/BaseIcon.AllApps"
android:id="@+id/icon"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
launcher:iconDisplay="all_apps"
- launcher:centerVertically="true"
- android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
- android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
+ launcher:centerVertically="true" />
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index 2accd2d..c684881 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -26,7 +26,6 @@
android:clipChildren="true"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
- android:paddingTop="@dimen/all_apps_header_top_padding"
launcher:pageIndicator="@+id/tabs" >
<include layout="@layout/all_apps_rv_layout" />
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 12561b6..dfce946 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -18,15 +18,20 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:background="@drawable/widget_resize_shadow"
- android:foreground="@drawable/widget_resize_frame"
- android:foregroundTint="?attr/workspaceTextColor"
android:padding="0dp">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
+ <!-- Frame -->
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:layout_margin="@dimen/resize_frame_margin"
+ android:src="@drawable/widget_resize_frame" />
+
<!-- Left -->
<ImageView
android:layout_width="wrap_content"
@@ -34,7 +39,7 @@
android:layout_gravity="left|center_vertical"
android:layout_marginLeft="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
<!-- Top -->
<ImageView
@@ -43,7 +48,7 @@
android:layout_gravity="top|center_horizontal"
android:layout_marginTop="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
<!-- Right -->
<ImageView
@@ -52,7 +57,7 @@
android:layout_gravity="right|center_vertical"
android:layout_marginRight="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
<!-- Bottom -->
<ImageView
@@ -61,7 +66,7 @@
android:layout_gravity="bottom|center_horizontal"
android:layout_marginBottom="@dimen/widget_handle_margin"
android:src="@drawable/ic_widget_resize_handle"
- android:tint="?attr/workspaceTextColor" />
+ android:tint="?android:attr/colorAccent" />
</FrameLayout>
</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml
index 32a5419..1cdee08 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -21,4 +21,5 @@
android:textColor="?attr/folderTextColor"
android:includeFontPadding="false"
android:hapticFeedbackEnabled="false"
- launcher:iconDisplay="folder" />
+ launcher:iconDisplay="folder"
+ launcher:centerVertically="true" />
diff --git a/res/layout/home_settings.xml b/res/layout/home_settings.xml
new file mode 100644
index 0000000..0f2461a
--- /dev/null
+++ b/res/layout/home_settings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <EditText
+ android:id="@+id/filter_box"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/developer_options_filter_margins"
+ android:hint="@string/developer_options_filter_hint"
+ android:visibility="gone"
+ />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@android:id/list_container"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/keyboard_drag_and_drop.xml b/res/layout/keyboard_drag_and_drop.xml
new file mode 100644
index 0000000..e9463c4
--- /dev/null
+++ b/res/layout/keyboard_drag_and_drop.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.launcher3.keyboard.KeyboardDragAndDropView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:focusable="true"
+ android:orientation="vertical"
+ android:elevation="6dp">
+
+ <TextView
+ android:id="@+id/label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="?attr/folderFillColor"
+ android:padding="8dp"
+ android:textColor="?attr/folderTextColor"
+ />
+
+</com.android.launcher3.keyboard.KeyboardDragAndDropView>
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index a137908..8451b77 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -28,6 +28,12 @@
android:clipToPadding="false"
android:importantForAccessibility="no">
+ <com.android.launcher3.views.AccessibilityActionsView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/home_screen"
+ />
+
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
@@ -43,11 +49,6 @@
android:id="@+id/hotseat"
layout="@layout/hotseat" />
- <include
- android:id="@+id/overview_panel"
- layout="@layout/overview_panel" />
-
-
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
<com.android.launcher3.pageindicators.WorkspacePageIndicator
@@ -71,6 +72,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
+ <include
+ android:id="@+id/overview_panel"
+ layout="@layout/overview_panel" />
+
</com.android.launcher3.dragndrop.DragLayer>
</com.android.launcher3.LauncherRootView>
diff --git a/res/layout/launcher_preview_layout.xml b/res/layout/launcher_preview_layout.xml
index 3fd02e3..1691680 100644
--- a/res/layout/launcher_preview_layout.xml
+++ b/res/layout/launcher_preview_layout.xml
@@ -17,7 +17,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:focusable="false">
<com.android.launcher3.CellLayout
android:id="@+id/workspace"
@@ -28,23 +29,8 @@
launcher:containerType="workspace"
launcher:pageIndicator="@+id/page_indicator"/>
- <com.android.launcher3.Hotseat
+ <include
android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- android:theme="@style/HomeScreenElementTheme"
- launcher:containerType="hotseat" />
-
- <com.android.launcher3.InsettableFrameLayout
- android:id="@+id/apps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <include
- android:id="@id/search_container_all_apps"
- layout="@layout/search_container_all_apps"/>
-
- </com.android.launcher3.InsettableFrameLayout>
+ layout="@layout/hotseat" />
</com.android.launcher3.InsettableFrameLayout>
\ No newline at end of file
diff --git a/res/layout/personal_work_tabs.xml b/res/layout/personal_work_tabs.xml
new file mode 100644
index 0000000..5fb5bcb
--- /dev/null
+++ b/res/layout/personal_work_tabs.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+-->
+
+<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/all_apps_header_tab_height"
+ android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
+ android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
+ android:orientation="horizontal"
+ android:elevation="2dp"
+ 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: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:textColor="@color/all_apps_tab_text"
+ android:textSize="14sp" />
+</com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
\ No newline at end of file
diff --git a/res/layout/secondary_launcher.xml b/res/layout/secondary_launcher.xml
index fdf4446..e3c60ec 100644
--- a/res/layout/secondary_launcher.xml
+++ b/res/layout/secondary_launcher.xml
@@ -67,7 +67,7 @@
android:paddingTop="@dimen/all_apps_header_top_padding"
android:orientation="vertical" >
- <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
+ <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_tab_height"
@@ -97,7 +97,7 @@
android:textAllCaps="true"
android:textColor="@color/all_apps_tab_text"
android:textSize="14sp" />
- </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+ </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
</com.android.launcher3.allapps.FloatingHeaderView>
<com.android.launcher3.allapps.search.AppsSearchContainerLayout
diff --git a/res/layout/switch_preference_with_settings.xml b/res/layout/switch_preference_with_settings.xml
index d319561..cd51833 100644
--- a/res/layout/switch_preference_with_settings.xml
+++ b/res/layout/switch_preference_with_settings.xml
@@ -26,7 +26,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_setting"
- android:tint="@android:color/black"
+ android:forceDarkAllowed="true"
android:padding="12dp"
android:background="?android:attr/selectableItemBackgroundBorderless" />
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 881df1b..c620e2a 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -30,7 +30,7 @@
android:paddingStart="@dimen/deep_shortcuts_text_padding_start"
android:paddingEnd="@dimen/popup_padding_end"
android:textSize="14sp"
- android:maxLines="2"
+ android:maxLines="1"
android:textColor="?android:attr/textColorPrimary"
launcher:iconDisplay="shortcut_popup"
launcher:layoutHorizontal="true"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 923352e..d38a77a 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -27,15 +27,12 @@
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="16dp"
launcher:pageIndicator="@+id/folder_page_indicator" />
<LinearLayout
android:id="@+id/folder_footer"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="48dp"
android:clipChildren="false"
android:orientation="horizontal"
android:paddingLeft="12dp"
@@ -53,13 +50,10 @@
android:gravity="center_horizontal"
android:hint="@string/folder_hint_text"
android:imeOptions="flagNoExtractUi"
- android:paddingBottom="@dimen/folder_label_padding_bottom"
- android:paddingTop="@dimen/folder_label_padding_top"
android:singleLine="true"
android:textColor="?attr/folderTextColor"
android:textColorHighlight="?android:attr/colorControlHighlight"
- android:textColorHint="?attr/folderHintColor"
- android:textSize="@dimen/folder_label_text_size" />
+ android:textColorHint="?attr/folderHintColor"/>
<com.android.launcher3.pageindicators.PageIndicatorDots
android:id="@+id/folder_page_indicator"
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 148a99b..55dd1de 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -15,12 +15,13 @@
-->
<com.android.launcher3.widget.WidgetCell
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/widget_cell_horizontal_padding"
+ android:paddingVertical="@dimen/widget_cell_vertical_padding"
android:layout_weight="1"
android:orientation="vertical"
android:focusable="true"
- android:background="?android:attr/colorPrimaryDark"
android:gravity="center_horizontal">
<include layout="@layout/widget_cell_content" />
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 64f2362..65a49ab 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -17,47 +17,47 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/widget_preview_label_vertical_padding"
- android:paddingBottom="@dimen/widget_preview_label_vertical_padding"
- android:paddingLeft="@dimen/widget_preview_label_horizontal_padding"
- android:paddingRight="@dimen/widget_preview_label_horizontal_padding"
- android:orientation="horizontal">
-
- <!-- The name of the widget. -->
- <TextView
- android:id="@+id/widget_name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:ellipsize="end"
- android:fadingEdge="horizontal"
- android:gravity="start"
- android:singleLine="true"
- android:maxLines="1"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp" />
-
- <!-- The original dimensions of the widget (can't be the same text as above due to different
- style. -->
- <TextView
- android:id="@+id/widget_dims"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="5dp"
- android:layout_marginLeft="5dp"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp"
- android:alpha="0.8" />
- </LinearLayout>
-
<!-- The image of the widget. This view does not support padding. Any placement adjustment
should be done using margins. -->
<com.android.launcher3.widget.WidgetImageView
android:id="@+id/widget_preview"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="0dp"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:layout_marginVertical="8dp" />
+
+ <!-- The name of the widget. -->
+ <TextView
+ android:id="@+id/widget_name"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal"
+ android:gravity="center_horizontal"
+ android:singleLine="true"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp" />
+
+ <!-- The original dimensions of the widget (can't be the same text as above due to different
+ style. -->
+ <TextView
+ android:id="@+id/widget_dims"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ android:alpha="0.8" />
+
+ <TextView
+ android:id="@+id/widget_description"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textSize="12sp"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:fadingEdge="horizontal" />
+
</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index 3fdfc96..c1b2cbf 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -44,11 +44,16 @@
android:textSize="14sp"
android:text="@string/long_press_widget_to_add"/>
- <include layout="@layout/widgets_scroll_container"
- android:id="@+id/widgets"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="45dp"
- android:layout_marginBottom="40dp"/>
+ <ScrollView
+ android:id="@+id/widgets_table_scroll_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="45dp"
+ android:layout_marginBottom="40dp">
+ <include layout="@layout/widgets_table_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal" />
+ </ScrollView>
</com.android.launcher3.widget.WidgetsBottomSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index f507a88..6c18d7a 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.widget.WidgetsFullSheet
+<com.android.launcher3.widget.picker.WidgetsFullSheet
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
@@ -27,11 +28,13 @@
android:background="?android:attr/colorPrimary"
android:elevation="4dp">
- <com.android.launcher3.widget.WidgetsRecyclerView
- android:id="@+id/widgets_list_view"
+ <TextView
+ android:id="@+id/no_widgets_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:clipToPadding="false" />
+ android:gravity="center"
+ android:visibility="gone"
+ tools:text="No widgets available" />
<!-- Fast scroller popup -->
<TextView
@@ -49,4 +52,4 @@
android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
</com.android.launcher3.views.TopRoundedCornerView>
-</com.android.launcher3.widget.WidgetsFullSheet>
\ No newline at end of file
+</com.android.launcher3.widget.picker.WidgetsFullSheet>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
new file mode 100644
index 0000000..8125db8
--- /dev/null
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
+ <include layout="@layout/personal_work_tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dp" />
+
+ <com.android.launcher3.workprofile.PersonalWorkPagedView
+ android:id="@+id/widgets_view_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:descendantFocusability="afterDescendants"
+ 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: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:clipToPadding="false" />
+
+ </com.android.launcher3.workprofile.PersonalWorkPagedView>
+
+</merge>
\ No newline at end of file
diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/res/layout/widgets_full_sheet_recyclerview.xml
similarity index 64%
rename from res/drawable-v24/drag_handle_indicator_shadow.xml
rename to res/layout/widgets_full_sheet_recyclerview.xml
index 774bc38..fbe559c 100644
--- a/res/drawable-v24/drag_handle_indicator_shadow.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.graphics.ShadowDrawable
+<com.android.launcher3.widget.picker.WidgetsRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/drag_handle_indicator_no_shadow"
- android:elevation="@dimen/vertical_drag_handle_elevation" />
+ android:id="@+id/primary_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false" />
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
new file mode 100644
index 0000000..9a6f922
--- /dev/null
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_and_recommendations_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical">
+ <View
+ android:id="@+id/collapse_handle"
+ android:layout_width="48dp"
+ android:layout_height="2dp"
+ android:layout_gravity="center_horizontal"
+ android:background="@color/popup_color_primary_dark"/>
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:textSize="24sp"
+ android:layout_marginTop="16dp"
+ android:text="@string/widget_button_text"/>
+ <!-- Disable the search bar because it has not been implemented. -->
+ <EditText
+ android:id="@+id/widgets_search_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:layout_marginTop="16dp"
+ android:background="@drawable/bg_widgets_searchbox"
+ android:drawablePadding="8dp"
+ android:drawableStart="@drawable/ic_allapps_search"
+ android:hint="@string/widgets_full_sheet_search_bar_hint"
+ android:padding="12dp" />
+</LinearLayout>
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
new file mode 100644
index 0000000..041e007
--- /dev/null
+++ b/res/layout/widgets_list_row_header.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<com.android.launcher3.widget.picker.WidgetsListHeader xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/widgets_list_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingVertical="20dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:id="@+id/app_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="16dp"
+ android:importantForAccessibility="no"
+ tools:src="@drawable/ic_corp"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants">
+
+ <TextView
+ android:id="@+id/app_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ tools:text="App name" />
+
+ <TextView
+ android:id="@+id/app_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:text="m widgets, n shortcuts" />
+
+ </LinearLayout>
+
+ <!-- This checkbox is not clickable. The outermost LinearLayout is responsible to handle all
+ click event and update the checkbox state. -->
+ <CheckBox
+ android:id="@+id/toggle"
+ android:layout_marginHorizontal="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_alignParentEnd="true"
+ android:clickable="false"
+ android:button="@drawable/widgets_tray_expand_button"/>
+
+</com.android.launcher3.widget.picker.WidgetsListHeader>
\ No newline at end of file
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index eec57a5..5942ba6 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -45,5 +45,5 @@
launcher:iconSizeOverride="@dimen/widget_section_icon_size"
launcher:layoutHorizontal="true" />
- <include layout="@layout/widgets_scroll_container" />
+ <include layout="@layout/widgets_table_container" />
</LinearLayout>
diff --git a/res/layout/widgets_scroll_container.xml b/res/layout/widgets_scroll_container.xml
deleted file mode 100644
index fc509d1..0000000
--- a/res/layout/widgets_scroll_container.xml
+++ /dev/null
@@ -1,33 +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.
--->
-
-<HorizontalScrollView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/widgets_scroll_container"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?android:attr/colorPrimaryDark"
- android:scrollbars="none">
- <LinearLayout
- android:id="@+id/widgets_cell_list"
- style="@style/TextTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingStart="0dp"
- android:paddingEnd="0dp"
- android:orientation="horizontal"
- android:showDividers="none"/>
-</HorizontalScrollView>
\ No newline at end of file
diff --git a/res/drawable-v24/drag_handle_indicator_shadow.xml b/res/layout/widgets_table_container.xml
similarity index 64%
copy from res/drawable-v24/drag_handle_indicator_shadow.xml
copy to res/layout/widgets_table_container.xml
index 774bc38..c4dfe7e 100644
--- a/res/drawable-v24/drag_handle_indicator_shadow.xml
+++ b/res/layout/widgets_table_container.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 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,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.graphics.ShadowDrawable
+<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/drag_handle_indicator_no_shadow"
- android:elevation="@dimen/vertical_drag_handle_elevation" />
+ android:id="@+id/widgets_table"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="8dp"
+ android:background="?android:attr/colorPrimaryDark" />
diff --git a/res/mipmap-hdpi/ic_launcher_home.png b/res/mipmap-hdpi/ic_launcher_home.png
deleted file mode 100644
index d068d92..0000000
--- a/res/mipmap-hdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_home.png b/res/mipmap-mdpi/ic_launcher_home.png
deleted file mode 100644
index 16c8ec2..0000000
--- a/res/mipmap-mdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_home.png b/res/mipmap-xhdpi/ic_launcher_home.png
deleted file mode 100644
index 8b2671b..0000000
--- a/res/mipmap-xhdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_home.png b/res/mipmap-xxhdpi/ic_launcher_home.png
deleted file mode 100644
index 43d8b7d..0000000
--- a/res/mipmap-xxhdpi/ic_launcher_home.png
+++ /dev/null
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index c39a9dc..ca59afa 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Werk"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Program is nie geïnstalleer nie."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Program is nie beskikbaar nie"</string>
@@ -52,8 +53,6 @@
<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="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>
<string name="permlab_read_settings" msgid="1941457408239617576">"lees Tuis-instellings en -kortpaaie"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Kon nie legstuk laai nie"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Stel op"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Wysig naam"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Naamlose vouer"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Het <xliff:g id="APP_NAME">%1$s</xliff:g> gedeaktiveer"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, het <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> kennisgewings</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om nuwe naam te stoor"</string>
<string name="folder_closed" msgid="4100806530910930934">"Vouer is gesluit"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Vouer hernoem na <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Style en muurpapiere"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Skakel programkennisgewings vir <xliff:g id="NAME">%1$s</xliff:g> aan om kennisgewingkolle te sien"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Verander instellings"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Wys kennisgewingkolle"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Voeg programikone by tuisskerm"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Voeg ikoon by tuisskerm"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Vir nuwe programme"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Kortpaaie"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Kortpaaie en kennisgewings"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Maak toe"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Maak toe"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Kennisgewing is toegemaak"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoonlik"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Persoonlike programme is apart en van werkprogramme versteek."</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Jou IT-admin kan jou werkprogramme en -data sien"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Volgende"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Het dit"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Werkprofiel is onderbreek"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Werkprogramme kan nie vir jou kennisgewings stuur, jou battery gebruik of toegang tot jou ligging kry nie"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Werkprofiel is onderbreek. Werkprogramme kan nie vir jou kennisgewings stuur, jou battery gebruik of toegang tot jou ligging kry nie"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Onderbreek werkprogramme en kennisgewings"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Kry werkprogramme hier"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Elke werkprogram het \'n kenteken en word deur jou organisasie veilig gehou. Skuif programme na jou tuisskerm toe vir makliker toegang."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Bestuur deur jou organisasie"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Kennisgewings en programme is af"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Maak toe"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Toe"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Misluk: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 90a54c0..0396df6 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ፍርግም የመጫን ችግር"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ማዋቀሪያ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"ስም ያርትዑ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ስም-አልባ አቃፊ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ተሰናክሏል"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያ አለው</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ዳግም የተሰጠውን ስም ለማስቀመጥ መታ ያድርጉ"</string>
<string name="folder_closed" msgid="4100806530910930934">"አቃፊ ተዘግቷል"</string>
<string name="folder_renamed" msgid="1794088362165669656">"አቃፊ <xliff:g id="NAME">%1$s</xliff:g> ተብሎ ዳግም ተሰይሟል"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ንጥሎች"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>፣ <xliff:g id="SIZE">%2$d</xliff:g> ወይም ተጨማሪ ንጥሎች"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ቅጦች እና ልጣፎች"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"የመተግበሪያ አዶዎችን ወደ መነሻ ገጹ ያክሉ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"አዶ ወደ የመነሻ ማያ ገጽ አክል"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"የሥራ መገለጫ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"የግል ውሂብ የተለየ እና ከሥራ መተግበሪያዎች የተደበቀ ነው"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"የስራ መተግበሪያዎች እና ውሂብ የተለዩ እና ከሥራ መተግበሪያዎች የተደበቁ ናቸው"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ቀጣይ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ገባኝ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"የሥራ መገለጫ ባለበት ቆሟል"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"የስራ መተግበሪያዎች ማሳወቂያዎችን ወደ እርስዎ መላክ፣ ባትሪዎን መጠቀም ወይም አካባቢዎን መድረስ አይችሉም"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"የሥራ መገለጫ ባለበት ቆሟል። የሥራ መተግበሪያዎች ማሳወቂያዎችን ወደ እርስዎ መላክ፣ ባትሪዎን መጠቀም ወይም አካባቢዎን መድረስ አይችሉም"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"የስራ መተግበሪያዎችን እና ማሳወቂያዎችን ባሉበት ያቁሙ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"የስራ መተግበሪያዎችን እዚህ ያግኙ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"እያንዳንዱ የሥራ መተግበሪያ ባጅ አለው፣ እና በድርጅትዎ ደህንነቱ ተጠብቋል። ለቀለለ መዳረሻ መተግበሪያዎችን ወደ የእርስዎ መነሻ ማያ ገጽ ይውሰዷቸው።"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"በእርስዎ ድርጅት የሚተዳደር"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ማሳወቂያዎች እና መተግበሪያዎች ጠፍተዋል"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ዝጋ"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ዝግ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"አልተሳካም፦ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index a9e5cb4..a80ecb0 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"قراءة إعدادات واختصارات الشاشة الرئيسية"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"حدثت مشكلة أثناء تحميل الأداة"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"الإعداد"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"تعديل الاسم"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"مجلد بدون اسم"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"تم إيقاف <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="zero">يتضمن تطبيق <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار.</item>
@@ -82,8 +81,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"انقر لحفظ الاسم الجديد"</string>
<string name="folder_closed" msgid="4100806530910930934">"تم إغلاق المجلد"</string>
<string name="folder_renamed" msgid="1794088362165669656">"تمت إعادة تسمية المجلد إلى <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> عنصر أو أكثر"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"الأنماط والخلفيات"</string>
@@ -94,11 +92,11 @@
<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="msg_missing_notification_access" msgid="281113995110910548">"لعرض نقاط الإشعارات، يجب تفعيل إشعارات التطبيق في <xliff:g id="NAME">%1$s</xliff:g>"</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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"إضافة رموز التطبيقات إلى الشاشة الرئيسية"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"إضافة رمز إلى الشاشة الرئيسية"</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>
@@ -135,18 +133,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"الملف الشخصي للعمل"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"إن البيانات الشخصية منفصلة عن تطبيقات العمل ومخفية عنها"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"إن بيانات وتطبيقات العمل مرئية لمشرف تكنولوجيا المعلومات في مؤسستك"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"التالي"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"حسنًا"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"تم إيقاف الملف الشخصي للعمل مؤقتًا"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"لا يمكن لتطبيقات العمل إرسال إشعارات إليك أو استخدام بطاريتك أو الوصول إلى موقعك الجغرافي."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"تم إيقاف الملف الشخصي للعمل مؤقتًا. لا يمكن لتطبيقات العمل إرسال إشعارات إليك أو استخدام بطاريتك أو الوصول إلى موقعك الجغرافي."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"إيقاف تطبيقات العمل وإشعاراتها مؤقتًا"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"البحث عن تطبيقات العمل هنا"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"يحتوي كل تطبيق للعمل على شارة ويظل تحت حماية مؤسستك. يمكنك نقل التطبيقات إلى شاشتك الرئيسية لتسهيل الوصول إليها."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ملف شخصي للعمل تديره مؤسستك"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"الإشعارات والتطبيقات متوقفة."</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"إغلاق"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"تمّ الإغلاق"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"تعذَّر <xliff:g id="WHAT">%1$s</xliff:g>."</string>
</resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 8e9aafc..2984603 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"গৃহ ছেটিং আৰু শ্বৰ্টকাটবোৰ পঢ়িব পাৰে"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ৱিজেট ল\'ড কৰাত সমস্য়া"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ছেটআপ কৰক"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"এইটো এটা ছিষ্টেম এপ আৰু ইয়াক আনইনষ্টল কৰিব নোৱৰি"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"নাম সম্পাদনা কৰক"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফ\'ল্ডাৰ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম কৰা হ’ল"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ৰ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টা জাননী আছে</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"সলনি কৰা নাম ছেভ কৰিবলৈ টিপক"</string>
<string name="folder_closed" msgid="4100806530910930934">"ফ\'ল্ডাৰ বন্ধ কৰা হ’ল"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ফ\'ল্ডাৰৰ নাম সলনি কৰি <xliff:g id="NAME">%1$s</xliff:g> কৰা হৈছে"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা বস্তু"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> টা অথবা তাতকৈ অধিক বস্তু"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ৱিজেটসমূহ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ৱালপেপাৰসমূহ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ষ্টাইল & ৱালপেপাৰ"</string>
@@ -94,11 +92,11 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"গৃহ স্ক্ৰীনত এপ্ চিহ্নসমূহ যোগ দিয়ক"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"গৃহ স্ক্ৰীণত আইকনটো যোগ কৰক"</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_search" msgid="891119232568284442">"Search"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনল’ড কৰি থকা হৈছে, <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পূৰ্ণ হ’ল"</string>
@@ -131,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ব্যক্তিগত ডেটাখিনি পৃথক হয় আৰু সেইখিনি কর্মস্থানৰ এপ্সমূহৰ পৰা লুকুওৱা আছে"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"আপোনাৰ আইটি প্ৰশাসকে কর্মস্থানৰ এপ্সমূহ আৰু ডেটা দেখা পায়"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"পৰৱৰ্তী"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুজি পালোঁ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"কৰ্মস্থানৰ প্ৰ\'ফাইলটো পজ কৰা আছে"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"কৰ্মস্থানৰ এপ্সমূহে আপোনালৈ জাননীসমূহ পঠিয়াব, আপোনাৰ বেটাৰী ব্যৱহাৰ কৰিব অথবা আপোনাৰ অৱস্থান এক্সেছ কৰিব নোৱাৰে"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"কৰ্মস্থানৰ প্ৰ\'ফাইলটো পজ কৰা আছে। কৰ্মস্থানৰ এপ্সমূহে আপোনালৈ জাননীসমূহ পঠিয়াব, আপোনাৰ বেটাৰী ব্যৱহাৰ কৰিব অথবা আপোনাৰ অৱস্থান এক্সেছ কৰিব নোৱাৰে"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"কর্মস্থানৰ এপ্সমূহ আৰু জাননীসমূহ পজ কৰক"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ইয়াত কৰ্মস্থানৰ এপ্ বিচাৰি পাওক"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"কৰ্মস্থানৰ প্ৰতিটো এপৰে একোটা প্ৰতীক আছে আৰু তাক আপোনাৰ প্ৰতিষ্ঠানে সুৰক্ষিত কৰি ৰাখে। ব্যৱহাৰ কৰাত সুবিধা হ\'বলৈ এপসমূহ আপোনাৰ গৃহ স্ক্ৰীণলৈ স্থানান্তৰ কৰক।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"আপোনাৰ প্ৰতিষ্ঠানৰ দ্বাৰা পৰিচালিত"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"জাননী আৰু এপসমূহ অফ হৈ আছে"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"বন্ধ কৰক"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"বন্ধ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"বিফল: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index aed975a..7c1ce84 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -50,10 +50,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Əsas səhifə"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Silin"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq infosu"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq məlumatı"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Quraşdırın"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Tətbiq təklif olunmasın"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Proqnozlaşdırılan tətbiqi bərkidin"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Əsas Səhifə ayarlarını və qısayolları oxuyun"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Vidcet yükləmə problemi"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Quraşdırma"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Adı redaktə edin"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Qovluq"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiv edildi"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildiriş var</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
<string name="folder_closed" msgid="4100806530910930934">"Qovluq bağlıdır"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Qovluq adı <xliff:g id="NAME">%1$s</xliff:g> ilə dəyişdirildi"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> element"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> və ya daha çox element"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Üslub və divar kağızları"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Bildiriş Nöqtələrini göstərmək üçün <xliff:g id="NAME">%1$s</xliff:g> bildirişlərini aktiv edin"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Ayarları dəyişin"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Bildiriş nöqtələrini göstərin"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Tətbiq ikonalarını Ana ekrana əlavə edin"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Əsas ekrana ikona əlavə edin"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni tətbiqlər üçün"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Qısa yollar"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Qısayol və bildirişlər"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Rədd edin"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Bağlayın"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Bildiriş rədd edildi"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Şəxsi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Şəxsi data ayrı olur və iş tətbiqlərindən gizlədilir"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"İş tətbiqləri və datasını İT admininiz görə bilir"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Növbəti"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"İş profilinə fasilə verilib"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"İş tətbiqləri sizə bildirişlər göndərə, batareyanızdan istifadə edə və ya məkanınıza daxil ola bilməz"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"İş profilinə fasilə verilib. İş tətbiqləri sizə bildirişlər göndərə, batareyanızdan istifadə edə və ya məkanınıza daxil ola bilməz"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"İş tətbiqlərinə və bildirişlərə fasilə verin"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Burada iş tətbiqləri axtarın"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Hər bir iş tətbiqində təşkilat tərəfindən qorunduğunu göstərən narıncı nişan var. Tətbiqləri daha asan giriş üçün Əsas Səhifə Ekranına köçürün."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Təşkilatınız tərəfindən idarə olunur"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Bildiriş və tətbiqlər deaktivdir"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Bağlayın"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Bağlıdır"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Alınmadı: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index e812368..883003c 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Infor. o aplikaciji"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Inform. o aplikaciji"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitanje podešavanja i prečica na početnom ekranu"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju vidžeta"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Podešavanje"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može da se deinstalira."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Izmenite naziv"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovani direktorijum"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenje</item>
@@ -79,8 +78,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste sačuvali preimenovanje"</string>
<string name="folder_closed" msgid="4100806530910930934">"Direktorijum je zatvoren"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Direktorijum je preimenovan u <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadine"</string>
@@ -95,7 +93,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Da biste prikazali tačke za obaveštenja, uključite obaveštenja za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Promenite podešavanja"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Prikazuj tačke za obaveštenja"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Dodaj ikone aplikacija na početni ekran"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni ekran"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
@@ -132,18 +130,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Prečice"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Prečice i obaveštenja"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obaveštenje je odbačeno"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Poslovni profil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Lični podaci su odvojeni i sakriveni od aplikacija za posao"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"IT administrator vidi poslovne aplikacije i podatke"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalje"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Važi"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Poslovni profil je pauziran"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu da vam šalju obaveštenja, koriste bateriju niti pristupaju lokaciji"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obaveštenja"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil za Work"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovde"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Svaka poslovna aplikacija ima značku i štiti je vaša organizacija. Premestite aplikacije na početni ekran da biste im lakše pristupali."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Ovim upravlja organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Obaveštenja i aplikacije su isključeni"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 0b2b6f1..b4cf913 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Галоўная"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"счытваць налады і ярлыкі на Галоўнай старонцы"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Праблема загрузкі віджэта"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Наладжванне"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Гэта сістэмная праграма, яе нельга выдаліць."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Змяніць назву"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назвы"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> адключана"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, мае <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнне</item>
@@ -80,8 +79,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Краніце, каб захаваць новую назву"</string>
<string name="folder_closed" msgid="4100806530910930934">"Папка закрыта"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Папка перайменавана ў <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, элементы: <xliff:g id="SIZE">%2$d</xliff:g> ці больш"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стылі і шпалеры"</string>
@@ -96,7 +94,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Дадаць значкі праграм на Галоўны экран"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Дадаць значок на Галоўны экран"</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>
@@ -133,18 +131,15 @@
<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="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_personal_apps" msgid="4155536355149317441">"Асабістыя даныя схаваны і паказваюцца адасоблена ад працоўных праграм"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Ваш IТ-адміністратар мае доступ да працоўных праграм і іх даных"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Далей"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Зразумела"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Працоўны профіль прыпынены"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Працоўныя праграмы не могуць адпраўляць вам апавяшчэнні, выкарыстоўваць акумулятар або атрымліваць доступ да вашага месцазнаходжання"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Працоўны профіль прыпынены. Працоўныя праграмы не могуць адпраўляць вам апавяшчэнні, выкарыстоўваць акумулятар або атрымліваць доступ да вашага месцазнаходжання"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Прыпыніць працоўныя праграмы і апавяшчэнні"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Знайдзіце працоўныя праграмы тут"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Кожная працоўная праграма мае значок і знаходзіцца пад аховай вашай арганізацыі. Для больш простага доступу перамясціце праграмы на Галоўны экран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Пад кіраваннем вашай арганізацыі"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Апавяшчэнні і праграмы выключаны"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрыць"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрытыя"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не ўдалося: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 512a7fc..408f205 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Начало"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"четене на настройките и преките пътища в Начало"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Проблем при зареждане на приспособлението"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Настройване"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Това е системно приложение и не може да се деинсталира."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Редактиране на името"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Папка без име"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Деактивирахте <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известия</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Докоснете, за да запазите новото име"</string>
<string name="folder_closed" msgid="4100806530910930934">"Папката бе затворена"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана на „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> елемента"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“ – <xliff:g id="SIZE">%2$d</xliff:g> или повече елементи"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилове и тапети"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Добавяне на икони на прил. на нач. екран"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавяне на икона към началния екран"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Служебен потребителски профил"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Личните данни се съхраняват отделно и са скрити от служебните приложения"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Служебните приложения и данни са видими за системния ви администратор"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Напред"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Разбрах"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Служебният потребителски профил е поставен на пауза"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Служебните приложения не могат да ви изпращат известия, да използват батерията или да осъществяват достъп до местоположението ви"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Служебният потребителски профил е поставен на пауза. Служебните приложения не могат да ви изпращат известия, да използват батерията или да осъществяват достъп до местоположението ви"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Поставете на пауза служебните приложения и известия"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Тук можете да намерите служебните приложения"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Всяко служебно приложение има значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Управлява се от организацията ви"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Известията и приложенията са изключени"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затваряне"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Неуспешно: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index b2d3070..09608b5 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"হোম সেটিংস এবং শর্টকাটগুলি পড়ে"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"উইজেট লোড হতে সমস্যা হয়েছে"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"সেটআপ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"নাম এডিট করুন"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফোল্ডার"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম করা হয়েছে"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"পুনঃনামকরণ সংরক্ষণ করতে আলতো চাপ দিন"</string>
<string name="folder_closed" msgid="4100806530910930934">"ফোল্ডার বন্ধ করা হয়েছে"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি আইটেম"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g>টি বা তার বেশি আইটেম"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"উইজেট"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"স্টাইল এবং ওয়ালপেপার"</string>
@@ -94,8 +91,8 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"হোম স্ক্রিনে অ্যাপ আইকন যোগ করুন"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপের জন্য"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"হোম স্ক্রিনে আইকন যোগ করুন"</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_search" msgid="891119232568284442">"সার্চ"</string>
@@ -131,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"অফিসের অ্যাপের থেকে ব্যক্তিগত ডেটা আলাদা করে লুকিয়ে রাখা হয়"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"আপনার আইটি অ্যাডমিন অফিস অ্যাপ এবং ডেটা দেখতে পাবেন"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"পরের"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"বুঝেছি"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"অফিস প্রোফাইল বন্ধ করা আছে"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"অফিসের অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"অফিসের প্রোফাইল পজ করা আছে। অফিসের অ্যাপ আপনাকে বিজ্ঞপ্তি পাঠাতে, ব্যাটারি ব্যবহার করতে বা লোকেশন অ্যাক্সেস করতে পারবে না"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"অফিস অ্যাপ এবং বিজ্ঞপ্তি বন্ধ করুন"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"এখানে কাজের অ্যাপ্সগুলি খুঁজুন"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"প্রতিটি কাজের অ্যাপে একটি করে ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"আপনার প্রতিষ্ঠানের দ্বারা পরিচালিত"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"বন্ধ করুন"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"বন্ধ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"কাজটি করা যায়নি: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 6ce19d8..65ad91e 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -44,16 +44,14 @@
<string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dodirnite dvaput i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
<string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Omiljeno"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacija"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Spisak aplikacija"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista ličnih aplikacija"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista poslovnih aplikacija"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Inform. o aplikaciji"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacije o aplikaciji"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Zakači predviđanje"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitaj postavke na početnom ekranu i prečice"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju dodatka"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može se deinstalirati."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Uređivanje naziva"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovani folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenje</item>
@@ -79,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da sačuvate promjenu naziva"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder je zatvoren"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Ime foldera je promijenjeno u <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, br. stavki: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadinske slike"</string>
@@ -95,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz tačaka za obavještenja, uključite obavještenja za aplikacije za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Promijeni postavke"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Prikaži tačke za obavještenja"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Dodaj ikone aplikacija na početni ekran"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni ekran"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
@@ -132,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Prečice"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Prečice i obavještenja"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zatvaranje"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obavještenje je odbačeno"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Lični podaci su odvojeni i sakriveni od poslovnih aplikacija"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Vaš IT administrator može vidjeti poslovne aplikacije i podatke"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Sljedeće"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumijem"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Radni profil je pauziran"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Radni profil je pauziran. Poslovne aplikacije vam ne mogu slati obavještenja, koristiti bateriju ili pristupiti vašoj lokaciji"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obavještenja"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovdje"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Svaka poslovna aplikacija ima značku i osigurava je vaša organizacija. Premjestite aplikacije na Početni ekran, radi lakšeg pristupa."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Upravlja vaša organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikacije i aplikacije su isključene"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index efef421..4ef9ec3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/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="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"Treball"</string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Feina"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'aplicació no s\'ha instal·lat."</string>
<string name="activity_not_available" msgid="7456344436509528827">"L\'aplicació no està disponible."</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'aplicació que has baixat està desactivada al mode segur."</string>
@@ -46,14 +46,12 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No hi ha més espai a la safata Preferits."</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Llista d\'aplicacions"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Llista d\'aplicacions personals"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"Llista d\'aplicacions de treball"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Llista d\'aplicacions per a la feina"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inici"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Suprimeix"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informació de l\'aplicació"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instal·la"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"No suggereixis l\'aplicació"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fixa la predicció"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"llegeix la configuració i les dreceres de la pantalla d\'inici"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"S\'ha produït un problema en carregar el widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuració"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edita el nom"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sense nom"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"S\'ha desactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> té <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacions</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca per desar el nom nou"</string>
<string name="folder_closed" msgid="4100806530910930934">"Carpeta tancada"</string>
<string name="folder_renamed" msgid="1794088362165669656">"S\'ha canviat el nom de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elements"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o més elements"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estils i fons de pantalla"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Per veure els punts de notificació, activa les notificacions de l\'aplicació <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Canvia la configuració"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra els punts de notificació"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Afegeix icones a la pantalla d\'inici"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Afegeix icona a la pantalla d\'inici"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per a les aplicacions noves"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Dreceres"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Dreceres i notificacions"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignora"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Tanca"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"S\'ha ignorat la notificació"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Treball"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de treball"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Les dades personals s\'oculten i se separen de les aplicacions de treball"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"L\'administrador de TI pot veure les dades i les aplicacions de treball"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Següent"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entesos"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de treball està en pausa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Les aplicacions de treball no poden enviar-te notificacions, consumir bateria ni accedir a la teva ubicació"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de treball està en pausa. Les aplicacions de treball no poden enviar-te notificacions, consumir bateria ni accedir a la teva ubicació"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Posa en pausa les notificacions i les aplicacions de treball"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Feina"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil professional"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Cerca aplicacions per a la feina aquí"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Totes les aplicacions per a la feina tenen una insígnia que indica que estan protegides per la teva organització. Mou les aplicacions a la pantalla d\'inici per poder-hi accedir més fàcilment."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gestionat per la teva organització"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Les notificacions i les aplicacions estan desactivades"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tanca"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"S\'ha tancat"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 91caf6f..692b57d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Práce"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikace není nainstalována."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikace není k dispozici."</string>
@@ -52,8 +53,6 @@
<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="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>
<string name="permlab_read_settings" msgid="1941457408239617576">"čtení nastavení a odkazů plochy"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problém s načtením widgetu"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Nastavení"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Upravit název"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Složka bez názvu"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je zakázána"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="few">Aplikace <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> oznámení</item>
@@ -80,8 +79,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Klepnutím změnu názvu uložíte"</string>
<string name="folder_closed" msgid="4100806530910930934">"Složka je uzavřena"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Složka přejmenována na <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Složka: <xliff:g id="NAME">%1$s</xliff:g>, počet položek: <xliff:g id="SIZE">%2$d</xliff:g> nebo více"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Složka: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styly a tapety"</string>
@@ -96,7 +94,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Chcete-li zobrazovat puntíky s oznámením, zapněte oznámení z aplikace <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Změnit nastavení"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Zobrazovat puntíky s oznámením"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Přidat na plochu ikony aplikací"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Přidat ikonu na plochu"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pro nové aplikace"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
@@ -133,18 +131,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Zkratky"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Zkratky a oznámení"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Zavřít"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zavřít"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Oznámení bylo zavřeno"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobní"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobní údaje jsou oddělené a jsou před pracovními aplikacemi skryty"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"K datům pracovních aplikací má přístup váš administrátor IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Další"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Rozumím"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovní profil je pozastaven"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Pracovní aplikace vám nemohou zasílat oznámení, používat vaši baterii ani získat přístup k vaší poloze"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Pracovní profil je pozastaven. Pracovní aplikace vám nemohou zasílat oznámení, používat vaši baterii ani získat přístup k vaší poloze"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pozastavit pracovní aplikace a oznámení"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Zde naleznete pracovní aplikace"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Každá pracovní aplikace má odznak a je zabezpečena vaší organizací. Aplikace si můžete pro jednoduchost přesunout na plochu."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Spravováno vaší organizací"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Oznámení a aplikace jsou vypnuty"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zavřít"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zavřeno"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Selhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 8a8db67..dc17516 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Arbejde"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installeret."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgængelig"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Appinfo"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Foreslå ikke en app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fastgør forslaget"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"læs indstillinger og genveje for startskærmen"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Der er problemer med indlæsning af widgetten"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurer"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Rediger navn"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Unavngiven mappe"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> er deaktiveret"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikation</item>
@@ -78,12 +77,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryk for at gemme omdøbningen"</string>
<string name="folder_closed" msgid="4100806530910930934">"Mappen er lukket"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Mappen er omdøbt til <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
- <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Layout og baggrunde"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærm"</string>
+ <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stil og baggrunde"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Startskærmindstillinger"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se notifikationscirkler, skal du aktivere appnotifikationer for <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Skift indstillinger"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Vis notifikationscirkler"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Føj appikoner til startskærmen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Føj ikon til startskærmen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Genveje"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Genveje og notifikationer"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Afvis"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Luk"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notifikationen blev afvist"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlige"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personlige data er adskilt og skjult fra arbejdsapps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Arbejdsapps og -data er synlige for din it-administrator"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Næste"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Arbejdsprofilen er sat på pause"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Arbejdsapps kan ikke sende dig notifikationer, bruge dit batteri eller få adgang til din placering"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Arbejdsprofilen er sat på pause. Arbejdsapps kan ikke sende dig notifikationer, bruge dit batteri eller få adgang til din placering"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Sæt arbejdsapps og notifikationer på pause"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find arbejdsapps her"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Alle arbejdsapps har et badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administreret af din organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikationer og apps er slået fra"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Luk"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lukket"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislykket: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index fdd9214..a345bab 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Arbeit"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App ist nicht installiert."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App nicht verfügbar"</string>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startseite"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Entfernen"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstallieren"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"App-Info"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"App-Details"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installieren"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"App nicht vorschlagen"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Vorgeschlagene App anpinnen"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem beim Laden des Widgets"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Einrichten"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Name bearbeiten"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Unbenannter Ordner"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiviert"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, hat <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> Benachrichtigungen</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Neuen Namen zum Speichern antippen"</string>
<string name="folder_closed" msgid="4100806530910930934">"Ordner geschlossen"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Ordner umbenannt in <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> Elemente"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> oder mehr Elemente"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Designs und Hintergründe"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Um dir Benachrichtigungspunkte anzeigen zu lassen, aktiviere die Benachrichtigungen für die App \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="title_change_settings" msgid="1376365968844349552">"Einstellungen ändern"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"App-Benachrichtigungspunkte anzeigen"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"App-Symbole auf Startbildschirm setzen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Symbol zum Startbildschirm hinzufügen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Bei neuen Apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Verknüpfungen"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Verknüpfungen und Benachrichtigungen"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Schließen"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Schließen"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Benachrichtigung geschlossen"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personenbezogene Daten sind für geschäftlichen Apps nicht sichtbar oder zugänglich"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Geschäftliche Apps und Daten können von deinem IT-Administrator eingesehen werden"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Weiter"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Arbeitsprofil pausiert"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht nutzen und nicht auf deinen Standort zugreifen"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Das Arbeitsprofil ist pausiert. Geschäftliche Apps können dir keine Benachrichtigungen senden, deinen Akku nicht beanspruchen und nicht auf deinen Standort zugreifen."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Geschäftliche Apps und Benachrichtigungen pausieren"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hier findest du Apps für die Arbeit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Jede App für die Arbeit ist mit einem Logo gekennzeichnet. Deine Organisation kümmert sich um den entsprechenden Schutz. Damit du leichter auf Apps zugreifen kannst, verschiebe sie auf deinen Startbildschirm."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Wird von deiner Organisation verwaltet"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Benachrichtigungen und Apps sind deaktiviert"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Schließen"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Geschlossen"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Fehler: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 5d72b77..d80e905 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -50,10 +50,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Αρχική οθόνη"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Παρουσιάστηκε πρόβλημα στη φόρτωση του γραφικού στοιχείου"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Ρύθμιση"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Επεξεργασία ονόματος"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Φάκελος χωρίς όνομα"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> είναι απενεργοποιημένη"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other">Η εφαρμογή <xliff:g id="APP_NAME_2">%1$s</xliff:g>, έχει <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ειδοποιήσεις</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Πατήστε για να αποθηκεύσετε τη νέα ονομασία"</string>
<string name="folder_closed" msgid="4100806530910930934">"Ο φάκελος έκλεισε"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Ο φάκελος μετονομάστηκε σε <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> στοιχεία"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ή περισσότερα στοιχεία"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Στιλ και ταπετσαρίες"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Προσθ. εικονιδίων εφαρμ. σε αρχική οθόνη"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Προσθήκη εικονιδίου στην Αρχική οθόνη"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Προφίλ εργασίας"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Τα προσωπικά δεδομένα βρίσκονται σε ξεχωριστή θέση και δεν είναι ορατά από τις εφαρμογές εργασίας"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Οι εφαρμογές εργασίας και τα δεδομένα τους είναι ορατά στον διαχειριστή IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Επόμενο"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Το κατάλαβα"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Το προφίλ εργασίας έχει τεθεί σε παύση"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Οι εφαρμογές εργασίας δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Το προφίλ εργασίας έχει τεθεί σε παύση. Οι εφαρμογές εργασιών δεν μπορούν να σας στέλνουν ειδοποιήσεις, να χρησιμοποιούν την μπαταρία ή να έχουν πρόσβαση στην τοποθεσία σας."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Παύση εφαρμογών εργασίας και ειδοποιήσεων"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Βρείτε όλες τις εφαρμογές εργασίας εδώ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Κάθε εφαρμογή εργασίας φέρει ένα σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Διαχειριζόμενο από τον οργανισμό σας"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Κλείσιμο"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Κλειστή"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Αποτυχία: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 3c54e5a..7adc218 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -32,7 +32,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised 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="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch & hold to place manually"</string>
<string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<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="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="1941457408239617576">"read Home settings and shortcuts"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</string>
@@ -94,7 +91,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="auto_add_shortcuts_label" msgid="3698776050751790653">"Add app icons to the home screen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon 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>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</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>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<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_personal_apps" msgid="4155536355149317441">"Personal data is separate and hidden from work apps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Work apps and data are visible to your IT admin"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
deleted file mode 100644
index 3c54e5a..0000000
--- a/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,148 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- 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">"Work"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
- <string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
- <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
- <string name="custom_actions" msgid="3747508247759093328">"Customised actions"</string>
- <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
- <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised 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="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
- <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</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="label_application" msgid="8531721983832654978">"App"</string>
- <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
- <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
- <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
- <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
- <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
- <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
- <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="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="1941457408239617576">"read Home settings and shortcuts"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
- <string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"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="6081085226050792095">"Problem loading widget"</string>
- <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
- <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
- <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
- </plurals>
- <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
- <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
- <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
- <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
- <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</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="7728578836261442095">"Allow Home screen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
- <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
- <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="auto_add_shortcuts_label" msgid="3698776050751790653">"Add app icons to the home screen"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For new apps"</string>
- <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
- <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
- <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
- <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="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
- <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
- <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
- <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
- <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_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>
- <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>
- <string name="added_to_folder" msgid="4793259502305558003">"Item added to folder"</string>
- <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="1603837886334246317">"Move to Home screen"</string>
- <string name="action_resize" msgid="1802976324781771067">"Re-size"</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="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>
- <string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
- <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_personal_apps" msgid="4155536355149317441">"Personal data is separate and hidden from work apps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Work apps and data are visible to your IT admin"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
- <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 3c54e5a..7adc218 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -32,7 +32,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised 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="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch & hold to place manually"</string>
<string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<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="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="1941457408239617576">"read Home settings and shortcuts"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</string>
@@ -94,7 +91,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="auto_add_shortcuts_label" msgid="3698776050751790653">"Add app icons to the home screen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon 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>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</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>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<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_personal_apps" msgid="4155536355149317441">"Personal data is separate and hidden from work apps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Work apps and data are visible to your IT admin"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 3c54e5a..7adc218 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -32,7 +32,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised 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="add_item_request_drag_hint" msgid="5899764264480397019">"Touch and hold to place manually"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Touch & hold to place manually"</string>
<string name="place_automatically" msgid="8064208734425456485">"Add automatically"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Search apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Loading apps…"</string>
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
<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="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="1941457408239617576">"read Home settings and shortcuts"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</string>
@@ -94,7 +91,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="auto_add_shortcuts_label" msgid="3698776050751790653">"Add app icons to the home screen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Add icon 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>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</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>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<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_personal_apps" msgid="4155536355149317441">"Personal data is separate and hidden from work apps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Work apps and data are visible to your IT admin"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps can\'t send you notifications, use your battery or access your location"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Each work app has a badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Close"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Closed"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
deleted file mode 100644
index 2e85204..0000000
--- a/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,148 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- 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">"Work"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
- <string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
- <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
- <string name="home_screen" msgid="806512411299847073">"Home screen"</string>
- <string name="custom_actions" msgid="3747508247759093328">"Custom actions"</string>
- <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
- <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up 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="add_item_request_drag_hint" msgid="5899764264480397019">"Touch & hold to place manually"</string>
- <string name="place_automatically" msgid="8064208734425456485">"Add automatically"</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="label_application" msgid="8531721983832654978">"App"</string>
- <string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
- <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</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_button_personal_label" msgid="1315764287305224468">"Personal apps list"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"Work apps list"</string>
- <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
- <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
- <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
- <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="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="1941457408239617576">"read Home settings and shortcuts"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
- <string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"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="6081085226050792095">"Problem loading widget"</string>
- <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edit Name"</string>
- <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
- <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
- <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, has <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notification</item>
- </plurals>
- <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
- <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
- <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
- <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
- <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> or more items"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
- <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles & wallpapers"</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="7728578836261442095">"Allow Home screen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="notification_dots_title" msgid="9062440428204120317">"Notification dots"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"On"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Off"</string>
- <string name="title_missing_notification_access" msgid="7503287056163941064">"Notification access needed"</string>
- <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="auto_add_shortcuts_label" msgid="3698776050751790653">"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>
- <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
- <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
- <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="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgets"</string>
- <string name="widgets_list" msgid="796804551140113767">"Widgets list"</string>
- <string name="widgets_list_closed" msgid="6141506579418771922">"Widgets list closed"</string>
- <string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
- <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
- <string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
- <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_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>
- <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>
- <string name="added_to_folder" msgid="4793259502305558003">"Item added to folder"</string>
- <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="1603837886334246317">"Move to Home screen"</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 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>
- <string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
- <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
- <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_personal_apps" msgid="4155536355149317441">"Personal data is separate & hidden from work apps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Work apps & data are visible to your IT admin"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Next"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Got it"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Work profile is paused"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Work apps can\'t send you notifications, use your battery, or access your location"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Work profile is paused. Work apps cant send you notifications, use your battery, or access your location"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pause work apps and notifications"</string>
- <string name="remote_action_failed" msgid="1383965239183576790">"Failed: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
-</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index e21c279..d10c84b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabajo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"No se instaló la aplicación."</string>
<string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible."</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Información de app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"leer configuración y accesos directos de la pantalla principal"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problema al cargar el widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta es una aplicación del sistema y no se puede desinstalar."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Editar nombre"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Se inhabilitó <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
@@ -78,12 +77,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Presiona para guardar el cambio de nombre"</string>
<string name="folder_closed" msgid="4100806530910930934">"Carpeta cerrada"</string>
<string name="folder_renamed" msgid="1794088362165669656">"El nombre de la carpeta se cambió a <xliff:g id="NAME">%1$s</xliff:g>."</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o más elementos"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos y fondos de pantalla"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Configuración de pantalla principal"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configuración de página principal"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir la rotación de la pantalla principal"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar los puntos de notificación, activa las notificaciones de la app para <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Cambiar la configuración"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificación"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Agrega íconos de apps a pantalla principal"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Agregar ícono a la pantalla principal"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para nuevas apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Accesos directos"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Accesos directos y notificaciones"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Descartar"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Se descartó la notificación"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personales"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"De trabajo"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Laborales"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Los datos personales están separados y ocultos de las apps de trabajo"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"El administrador de TI puede ver las apps de trabajo y los datos"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Siguiente"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Entendido"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de trabajo está en pausa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Las apps de trabajo no pueden enviarte notificaciones, usar la batería ni acceder a tu ubicación"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de trabajo está en pausa. Las apps de trabajo no pueden enviarte notificaciones, usar la batería ni acceder a tu ubicación"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pon en pausa las apps de trabajo y las notificaciones"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Apps de trabajo"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Cada app de trabajo tiene una insignia y está protegida por tu organización. Transfiere las apps a la pantalla principal para acceder a ellas con mayor facilidad."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administrado por tu organización"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las apps están desactivadas"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Cerrar"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Cerrado"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index d73247b..09b1239 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabajo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"La aplicación no está instalada."</string>
<string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Información de la aplicación"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"No sugerir aplicación"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fijar predicción"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"leer información de accesos directos y de ajustes de la pantalla de inicio"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problema al cargar el widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación es del sistema y no se puede desinstalar."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Editar nombre"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Se ha inhabilitado <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificaciones</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca para guardar el nuevo nombre"</string>
<string name="folder_closed" msgid="4100806530910930934">"Carpeta cerrada"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Se ha cambiado el nombre de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> elementos)"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="SIZE">%2$d</xliff:g> o más elementos)"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos y fondos de pantalla"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar puntos de notificación, activa las notificaciones de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Cambiar ajustes"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificación"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Añadir aplicaciones a la pantalla de inicio"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Añadir icono a la pantalla de inicio"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para aplicaciones nuevas"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Quitar"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Accesos directos"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Accesos directos y notificaciones"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Cerrar"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Cerrar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificación ignorada"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Los datos personales están separados y ocultos de las aplicaciones de trabajo"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Tu administrador de TI puede ver tus aplicaciones y datos de trabajo"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Siguiente"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Listo"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"El perfil de trabajo está en pausa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Las aplicaciones de trabajo no pueden enviarte notificaciones, gastar batería ni acceder a tu ubicación"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"El perfil de trabajo está en pausa. Las aplicaciones de trabajo no pueden enviarte notificaciones, consumir tu batería ni acceder a tu ubicación"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pausa apps y notificaciones de trabajo"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Aplicaciones de trabajo"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Cada aplicación de trabajo tiene una insignia y está protegida por tu organización. Mueve las aplicaciones a la pantalla de inicio para acceder a ellas más fácilmente."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administrada por tu organización"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las aplicaciones están desactivadas"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Cerrar"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Cerrada"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Se ha producido un error: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index e97fc6f..1e470e1 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Töö"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Rakendus pole installitud."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Rakendus ei ole saadaval"</string>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Avakuva"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduste teave"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installimine"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ära soovita rakendust"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Kinnita ennustus"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"avakuva seadete ja otseteede lugemine"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Probleem vidina laadimisel"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Seadistamine"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Muuda nime"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Nimetu kaust"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on keelatud"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> märguannet</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
<string name="folder_closed" msgid="4100806530910930934">"Kaust on suletud"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> üksust"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> või rohkem üksust"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiilid ja taustapildid"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Märguandetäppide kuvamiseks lülitage sisse rakenduse <xliff:g id="NAME">%1$s</xliff:g> märguanded"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Seadete muutmine"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Kuva märguandetäpid"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Lisa rakenduste ikoonid avakuvale"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisa ikoon avakuvasse"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uute rakenduste puhul"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Otseteed"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Otseteed ja märguanded"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Loobu"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Sule"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Märguandest loobuti"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Isiklik"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Isiklikke andmeid hoitakse töörakendustest eraldi"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Teie IT-administraator näeb töörakendusi ja -andmeid"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Järgmine"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selge"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Tööprofiil on peatatud"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Tööprofiil on peatatud. Töörakendused ei saa teile märguandeid saata, akut kasutada ega teie asukohale juurde pääseda"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Peatage töörakendused ja märguanded"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Töörakendused leiate siit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Igal töörakendusel on märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Haldab teie organisatsioon"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Märguanded ja rakendused on välja lülitatud"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sule"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Suletud"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nurjus: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 18cc766..10aebe7 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalatu"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ez iradoki aplikazioa"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Ainguratu iragarpena"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzeko baimena ematen die aplikazioei."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Arazo bat izan da widgeta kargatzean"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurazioa"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Editatu izena"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Izenik gabeko karpeta"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desgaituta dago"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> aplikazioak <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> jakinarazpen ditu</item>
@@ -78,15 +76,14 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Izen berria gordetzeko, sakatu hau"</string>
<string name="folder_closed" msgid="4100806530910930934">"Karpeta itxi da"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu)"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> karpeta (<xliff:g id="SIZE">%2$d</xliff:g> elementu edo gehiago)"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estiloak eta horma-paperak"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Eman hasierako pantaila biratzeko baimena"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzean"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzen denean"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Jakinarazpen-biribiltxoak"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Aktibatuta"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Desaktibatuta"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Jakinarazpen-biribiltxoak ikusteko, aktibatu <xliff:g id="NAME">%1$s</xliff:g> aplikazioaren jakinarazpenak"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Aldatu ezarpenak"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Erakutsi jakinarazpen-biribiltxoak"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Gehitu aplikazioen ikonoak hasierako pantailan"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrien kasuan"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Lasterbideak"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Lasterbideak eta jakinarazpenak"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Baztertu"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Itxi"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Baztertu egin da jakinarazpena"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pertsonalak"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Datu pertsonalak bananduta daude eta ez daude laneko aplikazioen artean ikusgai"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"IKT saileko administratzaileak laneko aplikazioak eta datuak ikus ditzake"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Hurrengoa"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ados"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Laneko profila pausatuta dago"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Laneko profila pausatuta dago. Laneko aplikazioek ezin dute jakinarazpenik bidali, bateria erabili edo kokapena atzitu."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pausatu laneko aplikazioak eta jakinarazpenak"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hemen dituzu laneko aplikazioak"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Laneko aplikazio bakoitzak bereizgarri bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Erakundeak kudeatzen du"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Jakinarazpenak eta aplikazioak desaktibatuta daude"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Itxi"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Itxita"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Huts egin du: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 1597043..926cdb9 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"خواندن تنظیمات و میانبرهای صفحه اصلی"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"مشکل در بارگیری ابزارک"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"تنظیم"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"این برنامه سیستمی است و حذف نصب نمیشود."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"ویرایش نام"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"پوشه بینام"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیرفعال شد"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
@@ -78,14 +77,13 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"برای ذخیره تغییر نام، ضربه بزنید"</string>
<string name="folder_closed" msgid="4100806530910930934">"پوشه بسته شد"</string>
<string name="folder_renamed" msgid="1794088362165669656">"نام پوشه به <xliff:g id="NAME">%1$s</xliff:g> تغییر کرد"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> مورد یا بیشتر"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ابزارکها"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواریها"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"سبک و کاغذدیواری"</string>
<string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"قابلچرخش بودن صفحه اصلی"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"امکان دادن به چرخش صفحه اصلی"</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>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"افزودن نماد برنامهها به صفحه اصلی"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"افزودن نماد به صفحه اصلی"</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,18 +129,15 @@
<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="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_personal_apps" msgid="4155536355149317441">"دادههای شخصی از برنامههای کاری جدا و از دسترس آنها پنهان هستند"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"برنامههای کاری و دادهها برای سرپرست فناوری اطلاعات نمایان هستند"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"بعدی"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"متوجهام"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"نمایه کاری موقتاً متوقف شده است"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"برنامههای کاری نمیتوانند اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"نمایه کاری موقتاً متوقف شده است. برنامههای کاری نمیتوانند به شما اعلان ارسال کنند، از باتری استفاده کنند، یا به مکانتان دسترسی داشته باشند"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"توقف موقت برنامههای کاری و اعلانها"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"اینجا برنامههای کاری را پیدا کنید"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"همه برنامههای کاری نشانی دارند و توسط سازمانتان ایمن نگه داشته میشوند. برنامههای کاری را برای دسترسی آسانتر به صفحه اصلی انتقال دهید."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"توسط سازمانتان مدیریت میشود"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"اعلانها و برنامهها خاموش هستند"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"بستن"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"بستهشده"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ناموفق بود: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9fea032..f87441f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Työ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Sovellusta ei ole asennettu."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Sovellus ei ole käytettävissä"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Sovelluksen tiedot"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Asenna"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Älä ehdota sovellusta"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Kiinnitä sovellus"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lue aloitusruudun asetuksia ja pikakuvakkeita"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Ongelma ladattaessa widgetiä"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Asetus"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tämä on järjestelmäsovellus, eikä sitä voi poistaa."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Muokkaa nimeä"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Nimetön kansio"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> poistettiin käytöstä"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ilmoitusta</item>
@@ -78,23 +77,22 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tallenna uusi nimi koskettamalla."</string>
<string name="folder_closed" msgid="4100806530910930934">"Kansio on suljettu"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Kansion nimeksi vaihdettiin <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>, ainakin <xliff:g id="SIZE">%2$d</xliff:g> kohdetta"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Tyylit ja taustakuvat"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Aloitusnäyttö"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Kotiasetukset"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Salli aloitusnäytön kiertäminen"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kun puhelinta kierretään"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Pistemerkit"</string>
<string name="notification_dots_desc_on" msgid="1679848116452218908">"Käytössä"</string>
- <string name="notification_dots_desc_off" msgid="1760796511504341095">"Ei päällä"</string>
+ <string name="notification_dots_desc_off" msgid="1760796511504341095">"Ei käytössä"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Ilmoituksien käyttöoikeus tarvitaan"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"<xliff:g id="NAME">%1$s</xliff:g> tarvitsee ilmoitusten käyttöoikeuden, jotta pistemerkkejä voidaan näyttää."</string>
<string name="title_change_settings" msgid="1376365968844349552">"Muuta asetuksia"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Näytä ilmoituksista kertovat pistemerkit"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Lisää sovelluskuvakkeet aloitusnäytölle"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisää kuvake aloitusruutuun"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uusille sovelluksille"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Pikakuvakkeet"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Pikakuvakkeet ja ilmoitukset"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Hylkää"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Sulje"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Ilmoitus hylätty"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Henkilökohtaiset"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Työsovellukset"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Työprofiili"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Henkilökohtainen data pidetään erillään, piilotettuna työsovelluksilta"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Työsovellukset ja ‑data näkyvät IT-järjestelmänvalvojalle"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Seuraava"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Selvä"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Työprofiilin käyttö on keskeytetty"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Työsovellukset eivät voi lähettää sinulle ilmoituksia eivätkä käyttää akkuasi tai sijaintiasi"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Työprofiilin käyttö on keskeytetty. Työsovellukset eivät voi lähettää sinulle ilmoituksia eivätkä käyttää akkuasi tai paikantaa sijaintiasi"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Keskeytä työsovellukset ja ‑ilmoitukset"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Etsi työsovelluksia tästä"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Kaikki työsovellukset on merkitty, ja organisaatiosi vastaa niiden suojaamisesta. Voit siirtää työsovelluksia aloitusnäytölle käytön helpottamiseksi."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Organisaatiosi hallinnoima"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Ilmoitukset ja sovellukset ovat poissa käytöstä"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sulje"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Suljettu"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Epäonnistui: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index aaffbd1..5ac514d 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Lanceur3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Travail"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails sur l\'appli"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'application"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'application"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problème lors du chargement du widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Modifier le nom"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est désactivée"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Touchez pour enregistrer le nouveau nom"</string>
<string name="folder_closed" msgid="4100806530910930934">"Dossier fermé"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> élément(s)"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les points de notification, activez les notifications d\'application pour <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Afficher les points de notification"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Ajouter icônes d\'applis à l\'écran d\'accueil"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ajouter l\'icône à l\'écran d\'accueil"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pour les nouvelles applications"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Raccourcis"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Raccourcis et notifications"</string>
<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">"Personnel"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Travail"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Les données personnelles sont distinctes et masquées des applications professionnelles"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Les applications et les données professionnelles sont visibles pour votre administrateur informatique"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Suivant"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Le profil professionnel est interrompu"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser la pile ni accéder à votre position"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Le profil professionnel est interrompu. Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser la pile ni accéder à votre position"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Interrompre les applications et les notifications professionnelles"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Trouvez ici des applications professionnelles"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Chaque application professionnelle comporte un badge, ce qui signifie qu\'elle est sécurisée par votre organisation. Vous pouvez déplacer vos applications vers l\'écran d\'accueil afin d\'y accéder plus facilement."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fermer"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fermé"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 2628cdb..65db47e 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/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="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"Travail"</string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Android Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Infos sur l\'appli"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne pas suggérer d\'application"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Épingler la prédiction"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de l\'écran d\'accueil"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problème lors du chargement du widget."</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Modifier le nom"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> est désactivé."</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> comporte <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification</item>
@@ -78,12 +76,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Appuyez pour enregistrer le nouveau nom du dossier."</string>
<string name="folder_closed" msgid="4100806530910930934">"Dossier fermé"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> éléments ou plus"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Dossier \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'écran d\'accueil"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Pour afficher les pastilles de notification, activez les notifications de l\'application <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Modifier les paramètres"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Afficher les pastilles de notification"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Ajouter icônes d\'applis à l\'écran d\'acc."</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ajouter l\'icône à l\'écran d\'accueil"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Pour les nouvelles applications"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Raccourcis"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Raccourcis et notifications"</string>
<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="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Les applications professionnelles n\'ont pas accès aux données personnelles"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Votre administrateur informatique a accès à vos applications et données professionnelles"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Suivant"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil professionnel en pause"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre position"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil professionnel en pause. Les applications professionnelles ne peuvent pas vous envoyer de notifications, utiliser votre batterie ni accéder à votre localisation"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Mettre en pause vos applications et notifications professionnelles"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Retrouvez ici vos applications professionnelles"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Les applications professionnelles sont accompagnées d\'un badge et sont sécurisées par votre organisation. Vous pouvez les déplacer vers votre écran d\'accueil pour y accéder plus facilement."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications et applications désactivées"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fermer"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fermé"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Échec : <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 9fdf22b..ca5ba3c 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suxerir aplicación"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fixar predición"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Produciuse un problema ao cargar o widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edita o nome"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Cartafol sen nome"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Desactivouse <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other">A aplicación <xliff:g id="APP_NAME_2">%1$s</xliff:g> ten <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacións</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca fóra para cambiar o nome do cartafol"</string>
<string name="folder_closed" msgid="4100806530910930934">"Pechouse o cartafol"</string>
<string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementos ou máis"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos/fondos de pantalla"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Para que se mostren os puntos de notificacións, activa as notificacións da aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Cambiar configuración"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar puntos de notificacións"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Engadir iconas de aplicacións á pantalla de inicio"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Engadir icona á pantalla de inicio"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicacións"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Atallos"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Atallos e notificacións"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Pechar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Ignorouse a notificación"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de traballo"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Os datos persoais sepáranse e ocúltanse das aplicacións do traballo"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"O teu administrador de TI pode ver as aplicacións e os datos do traballo"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Seguinte"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"De acordo"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"O perfil de traballo está en pausa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"As aplicacións do traballo non poden enviarche notificacións, utilizar a batería nin acceder á túa localización"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"O perfil de traballo está en pausa. As aplicacións do traballo non poden enviarche notificacións, utilizar a batería nin acceder á túa localización"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pon en pausa as aplicacións e as notificacións do traballo"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Buscar aplicacións do traballo aquí"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"As aplicacións do traballo teñen unha insignia e están protexidas pola túa organización. Traslada as aplicacións á pantalla de inicio para acceder a elas de forma máis fácil."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Perfil xestionado pola túa organización"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"As notificacións e as aplicacións están desactivadas"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Pechar"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Pechada"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Erro: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 01ecf73..3228dea 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/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="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"ઑફિસ"</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>
@@ -50,10 +50,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"હોમ"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"વિજેટ લોડ કરવામાં સમસ્યા"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"સેટઅપ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ ઍપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"નામમાં ફેરફાર કરો"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"અનામી ફોલ્ડર"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> અક્ષમ કરી"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
@@ -78,12 +76,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"નામ બદલવાનું સાચવવા માટે ટૅપ કરો"</string>
<string name="folder_closed" msgid="4100806530910930934">"ફોલ્ડર બંધ કર્યું"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ફોલ્ડરનું નામ બદલીને <xliff:g id="NAME">%1$s</xliff:g> કર્યું"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> આઇટમ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> કે વધુ આઇટમ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"શૈલીઓ અને વૉલપેપર"</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="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ઍપના આઇકન હોમ સ્ક્રીન પર ઉમેરો"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"હોમ સ્ક્રીન પર આઇકન ઉમેરો"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"ઑફિસની પ્રોફાઇલ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"વ્યક્તિગત ડેટા ઑફિસ માટેની ઍપથી અલગ અને છુપાવીને રાખેલો છે"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ઑફિસ માટેની ઍપ અને ડેટા તમારા IT વ્યવસ્થાપકને દેખાય છે"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"આગળ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"સમજાઈ ગયું"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ઑફિસની પ્રોફાઇલ થોભાવી છે"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"કાર્યાલયની પ્રોફાઇલ થોભાવી છે. ઑફિસ માટેની ઍપ તમને નોટિફિકેશન મોકલી શકતી નથી, તમારી બૅટરી વાપરી શકતી નથી કે તમારું સ્થાન ઍક્સેસ કરી શકતી નથી"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ઑફિસ માટેની ઍપ અને નોટિફિકેશન થોભાવો"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"કાર્યાલયની પ્રોફાઇલ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"કાર્ય ઍપને અહીંથી મેળવો"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"દરેક કાર્ય ઍપ પાસે એક બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"તમારી સંસ્થા દ્વારા મેનેજ કરેલ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"નોટિફિકેશન અને ઍપ બંધ છે"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"બંધ કરો"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"બંધ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"નિષ્ફળ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 0cec9a3..8fa02c0 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/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="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"दफ़्तर"</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>
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"होम पेज की सेटिंग और शॉर्टकट पढ़ें"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"विजेट लोड करने में समस्या"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"नाम में बदलाव करें"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फ़ोल्डर"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम है"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना है</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"नाम बदलना सहेजने के लिए टैप करें"</string>
<string name="folder_closed" msgid="4100806530910930934">"फ़ोल्डर बंद किया गया"</string>
<string name="folder_renamed" msgid="1794088362165669656">"फ़ोल्डर का नाम बदलकर <xliff:g id="NAME">%1$s</xliff:g> किया गया"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आइटम"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> या इससे ज़्यादा आइटम"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"स्टाइल और वॉलपेपर"</string>
@@ -94,11 +91,11 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"होम स्क्रीन पर ऐप्लिकेशन के आइकॉन जोड़ें"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीन में आइकॉन जोड़ें"</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_search" msgid="891119232568284442">"खोजें"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड हो रहा है, <xliff:g id="PROGRESS">%2$s</xliff:g> पूरी हुई"</string>
@@ -131,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"वर्क प्रोफ़ाइल"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"निजी डेटा को अलग रखा जाता है. साथ ही, ऑफ़िस के काम से जुड़े ऐप्लिकेशन से छिपा कर रखा जाता है"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन और डेटा, आपके आईटी एडमिन को दिखते हैं"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"आगे बढ़ें"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ठीक है"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"वर्क प्रोफ़ाइल रोक दी गई है"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन आपको सूचनाएं नहीं भेज सकते. साथ ही, आपकी बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस नहीं कर सकते"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"वर्क प्रोफ़ाइल रोक दी गई है. ऑफ़िस के काम से जुड़े ऐप्लिकेशन आपको सूचनाएं नहीं भेज सकते. साथ ही, आपके डिवाइस की बैटरी का इस्तेमाल या आपकी जगह की जानकारी को ऐक्सेस नहीं कर सकते"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ऑफ़िस के काम से जुड़े ऐप्लिकेशन और सूचनाएं रोकें"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"काम से जुड़े सभी ऐप्लिकेशन यहां पाएं"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"काम से जुड़े हर ऐप्लिकेशन पर एक बैज (निशान) होता है और इन ऐप्लिकेशन की सुरक्षा आपका संगठन करता है. आसानी से इस्तेमाल करने के लिए ऐप्लिकेशन को अपनी होम स्क्रीन पर ले जाएं."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"आपका संगठन प्रबंधित कर रहा है"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"सूचनाएं और ऐप्लिकेशन बंद हैं"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बंद करें"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बंद कर दिया गया"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"पूरा नहीं हुआ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index dd13ef0..b738bc0 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikaciji"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instaliraj"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlaži aplikaciju"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Prikvači predviđenu apl."</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čitanje postavki početnog zaslona i prečaca"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju widgeta"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je aplikacija sustava i ne može se ukloniti."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Uređivanje naziva"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućena"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijest</item>
@@ -79,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste spremili promijenjeni naziv"</string>
<string name="folder_closed" msgid="4100806530910930934">"Mapa je zatvorena"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana u <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> stavke"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ili više stavki"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadine"</string>
@@ -95,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz točaka obavijesti uključite obavijesti aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Promjena postavki"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Prikaži točke obavijesti"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Dodajte ikone aplikacija na početni zaslon"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonu na početni zaslon"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
@@ -132,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Prečaci"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Prečaci i obavijesti"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zatvori"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obavijest je odbačena"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobno"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobni podaci odvojeni su i skriveni od poslovnih aplikacija"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Podaci poslovnih aplikacija vidljivi su vašem IT administratoru"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalje"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Shvaćam"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Poslovni profil je pauziran"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Poslovne aplikacije ne mogu vam slati obavijesti, trošiti bateriju ili pristupiti vašoj lokaciji"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Poslovni profil je pauziran. Poslovne aplikacije ne mogu vam slati obavijesti, trošiti bateriju ili pristupiti vašoj lokaciji"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pauzirajte poslovne aplikacije i obavijesti"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ovdje možete pronaći radne aplikacije"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Svaka radna aplikacija ima značku i štiti je vaša organizacija. Premjestite aplikacije na početni zaslon radi lakšeg pristupa."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Pod upravljanjem vaše organizacije"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Obavijesti i aplikacije isključeni su"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zatvori"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zatvoreno"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nije uspjelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 4f03908..3327590 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Munka"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Az alkalmazás nincs telepítve."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Az alkalmazás nem érhető el"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Alkalmazásinformáció"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Telepítés"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne javasoljon alkalmazást"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Várható kitűzése"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Főoldal beállításainak és parancsikonjainak beolvasása"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Probléma történt a modul betöltésekor"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Beállítás"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ez egy rendszeralkalmazás, és nem lehet eltávolítani."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Név módosítása"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Névtelen mappa"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> letiltva"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other">A(z) <xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> értesítéssel rendelkezik</item>
@@ -78,12 +77,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Koppintson ide az átnevezés mentéséhez"</string>
<string name="folder_closed" msgid="4100806530910930934">"Mappa lezárva"</string>
<string name="folder_renamed" msgid="1794088362165669656">"A mappa új neve: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elem"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vagy több elem"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stílusok és háttérképek"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Kezdőképernyő beállításai"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Kezdőoldal beállításai"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"A kezdőképernyő elforgatásának engedélyezése"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"A telefon elforgatásakor"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Az értesítési pöttyök megjelenítéséhez kapcsolja be a(z) <xliff:g id="NAME">%1$s</xliff:g> alkalmazás értesítéseit"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Beállítások módosítása"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Értesítési pöttyök megjelenítése"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Appikonok hozzáadása a kezdőképernyőhöz"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ikon hozzáadása a kezdőképernyőhöz"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Új alkalmazásoknál"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Törlés"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Gyorsparancsok"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Parancsikonok és értesítések"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Elvetés"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Bezárás"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Értesítés elvetve"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Személyes"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"A személyes adatok el vannak különítve a munkahelyi alkalmazásoktól, amelyek nem is látják őket"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"A munkahelyi alkalmazásokat és adatokat látja a rendszergazda"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Tovább"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Értem"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"A munkaprofil használata szünetel"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"A munkaprofil használata szünetel. A munkahelyi alkalmazások nem küldhetnek értesítéseket, nem használhatják az akkumulátort, és nem férhetnek hozzá az Ön tartózkodási helyéhez"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Munkahelyi alkalmazások és értesítések szüneteltetése"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Itt kereshet munkahelyi alkalmazásokat"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"A munkahelyi alkalmazásoknál jelvény található, és biztonságukról az Ön szervezete gondoskodik. A könnyebb hozzáférés érdekében helyezze át az alkalmazásokat a kezdőképernyőre."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Az Ön szervezete kezeli"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Az értesítések és az alkalmazások ki vannak kapcsolva"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Bezárás"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Bezárva"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Sikertelen: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index c098bb3..93404e6 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -48,12 +48,10 @@
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Անձնական հավելվածների ցանկ"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"Աշխատանքային հավելվածների ցանկ"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Հիմնական"</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="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="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="1941457408239617576">"կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Վիջեթի բեռնման խնդիր կա"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Կարգավորում"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Փոխել անունը"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Անանուն պանակ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն անջատված է"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Հպեք՝ նոր անվանումը պահելու համար"</string>
<string name="folder_closed" msgid="4100806530910930934">"Պանակը փակ է"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> տարր"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> կամ ավելի տարրեր"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Ոճեր և պաստառներ"</string>
@@ -94,10 +91,10 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Ավելացնել պատկերակները հիմնական էկրանին"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ավելացնել պատկերակը Հիմնական էկրանին"</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>
@@ -131,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Անձնական տվյալները թաքցված են և առանձնացված աշխատանքային հավելվածներից"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Աշխատանքային հավելվածներն ու դրանց տվյալները տեսանելի են ձեր ադմինիստրատորին"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Առաջ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Եղավ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Աշխատանքային պրոֆիլը դադարեցված է"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել, օգտագործել ձեր մարտկոցը և ձեր տեղադրության մասին տվյալներ ստանալ։"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Աշխատանքային պրոֆիլը դադարեցված է։ Աշխատանքային հավելվածները չեն կարող ձեզ ծանուցումներ ուղարկել և օգտագործել ձեր մարտկոցն ու տեղադրության տվյալները։"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Դադարեցնել աշխատանքային հավելվածներն ու ծանուցումները"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Գտեք աշխատանքային հավելվածներ այստեղ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Աշխատանքային հավելվածները նշված են հատուկ նշանով: Նման հավելվածների անվտանգությունը ապահովում է ձեր կազմակերպությունը։ Հարմարության համար աշխատանքային հավելվածները կարող եք տեղափոխել հիմնական էկրան։"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Կառավարվում է ձեր կազմակերպության կողմից"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Ծանուցումներն ու հավելվածներն անջատված են"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Փակել"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Փակվեց"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Չհաջողվեց կատարել գործողությունը (<xliff:g id="WHAT">%1$s</xliff:g>)"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 242e808..92c7acc 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstal"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info aplikasi"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instal"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan sarankan aplikasi"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Pin Prediksi"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"membaca setelan dan pintasan layar Utama"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuat widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Siapkan"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Sunting Nama"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dinonaktifkan"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, memiliki <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifikasi</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketuk untuk menyimpan ganti nama"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder ditutup"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folder diganti namanya menjadi <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item atau lebih"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya & wallpaper"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Guna menampilkan Titik Notifikasi, aktifkan notifikasi aplikasi untuk <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Ubah setelan"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Tampilkan titik notifikasi"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Tambahkan ikon aplikasi ke Layar utama"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon ke Layar utama"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk aplikasi baru"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Pintasan"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Pintasan dan notifikasi"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Tutup"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Tutup"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notifikasi ditutup"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pribadi"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Kantor"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Data pribadi terpisah & tersembunyi dari aplikasi kerja"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Aplikasi & data kerja terlihat oleh admin IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Berikutnya"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Oke"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil kerja dijeda"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplikasi kerja tidak dapat mengirimi Anda notifikasi, menggunakan baterai, atau mengakses lokasi Anda"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil kerja dijeda. Aplikasi kerja tidak dapat mengirimi Anda notifikasi, menggunakan baterai, atau mengakses lokasi Anda"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Jeda notifikasi dan aplikasi kerja"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Temukan aplikasi kerja di sini"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Setiap aplikasi kerja memiliki badge dan dibuat tetap aman oleh organisasi. Pindahkan aplikasi ke Layar utama untuk memudahkan akses."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Dikelola oleh organisasi"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikasi dan aplikasi nonaktif"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tutup"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Ditutup"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 8e66dea..d66ca32 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Vinna"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Forritið er ekki uppsett."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Forritið er ekki í boði"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Setja upp"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ekki fá tillögu að forriti"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Festa tillögu"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Vandamál við að hlaða græju"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Uppsetning"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Breyta nafni"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Ónefnd mappa"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningu</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ýttu til að vista breytt heiti"</string>
<string name="folder_closed" msgid="4100806530910930934">"Möppu lokað"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atriði"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eða fleiri atriði"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stílar og veggfóður"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Til að sýna tilkynningarpunkta skaltu kveikja á forritstilkynningum fyrir <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Breyta stillingum"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Sýna tilkynningapunkta"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Bæta forritatáknum við heimaskjáinn"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bæta tákni á heimaskjáinn"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Fyrir ný forrit"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Taka niður"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Flýtileiðir"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Flýtileiðir og tilkynningar"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Hunsa"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Loka"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Tilkynningu lokað"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Persónulegt"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Persónuupplýsingar eru aðskildar og faldar í vinnuforritum"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Gögn vinnuforrita eru sýnileg kerfisstjóranum þínum"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Áfram"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ég skil"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Hlé gert á vinnusniði"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Vinnuforrit geta ekki sent þér tilkynningar, notað rafhlöðuorku eða fengið aðgang að staðsetningu þinni"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Hlé gert á vinnusniði. Vinnuforrit geta ekki sent þér tilkynningar, notað rafhlöðuorku eða fengið aðgang að staðsetningu þinni"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Gera hlé á vinnuforritum og tilkynningum"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hér finnurðu vinnuforrit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Öll vinnuforrit eru með merki og fyrirtækið þitt tryggir öryggi þeirra. Færðu forrit yfir á heimaskjáinn til að fá auðveldari aðgang að þeim."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Stjórnað af fyrirtækinu þínu"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Slökkt er á tilkynningum og forritum"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Loka"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lokað"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mistókst: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 4ffc861..8c4e3c5 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Lavoro"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App non installata."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App non disponibile"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informazioni app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installa"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Non suggerire app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Blocca previsione"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"aggiunta di scorciatoie"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lettura di impostazioni e scorciatoie in Home"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Errore durante il caricamento del widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configurazione"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Modifica nome"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Cartella senza nome"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"App <xliff:g id="APP_NAME">%1$s</xliff:g> disattivata"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ha <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifiche</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocca per salvare il nuovo nome"</string>
<string name="folder_closed" msgid="4100806530910930934">"Cartella chiusa"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Nome della cartella sostituito con <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementi"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o più elementi"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stili e sfondi"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Per mostrare gli indicatori di notifica, attiva le notifiche per l\'app <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Modifica impostazioni"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostra indicatori di notifica"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Aggiungi icone app a schermata Home"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Aggiungi icone alla schermata Home"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per le nuove app"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Scorciatoie"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Scorciatoie e notifiche"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignora"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Esci"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notifica ignorata"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personali"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"I dati personali sono separati e non sono visibili nelle app di lavoro"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"I dati e le app di lavoro sono visibili all\'amministratore IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Avanti"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Profilo di lavoro in pausa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Le app di lavoro non possono inviarti notifiche, usare la tua batteria o accedere alla tua posizione"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profilo di lavoro in pausa. Le app di lavoro non possono inviarti notifiche, usare la tua batteria o accedere alla tua posizione"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Metti in pausa le app di lavoro e le relative notifiche"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Qui puoi trovare le tue app di lavoro"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Ogni app di lavoro è contrassegnata da un badge e viene tenuta al sicuro dalla tua organizzazione. Sposta le app nella schermata Home per accedervi più facilmente."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gestito dalla tua organizzazione"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Le notifiche e le app non sono attive"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Chiudi"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Chiusa"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Operazione non riuscita: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a2068aa..c25e879 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"קרא הגדרות וקיצורי דרך של דף הבית"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"בעיה בטעינת ווידג\'ט"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"הגדרה"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"עריכת השם"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"תיקיה ללא שם"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> מושבתת"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="two">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> התראות</item>
@@ -80,9 +79,8 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"הקש כדי לשמור שינוי שם"</string>
<string name="folder_closed" msgid="4100806530910930934">"התיקיה נסגרה"</string>
<string name="folder_renamed" msgid="1794088362165669656">"שם התיקיה שונה ל-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, מספר הפריטים: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"תיקייה: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> פריטים או יותר"</string>
- <string name="widget_button_text" msgid="2880537293434387943">"ווידג\'טים"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"תיקיה: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"רכיבי ווידג\'ט"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"סגנונות וטפטים"</string>
<string name="settings_button_text" msgid="8873672322605444408">"הגדרות דף הבית"</string>
@@ -96,7 +94,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"הוספת סמלי אפליקציות למסך הבית"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"הוספת סמל במסך דף הבית"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"לאפליקציות חדשות"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
@@ -132,19 +130,16 @@
<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="accessibility_close" msgid="2277148124685870734">"סגירה"</string>
+ <string name="action_dismiss_notification" msgid="5909461085055959187">"סגור"</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="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"מידע אישי מאוחסן בנפרד ומוסתר מאפליקציות לעבודה"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"אפליקציות לעבודה ונתוני העבודה שלך גלויים למנהל ה-IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"הבא"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"הבנתי"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"פרופיל העבודה מושהה"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"אפליקציות לעבודה לא יכולות לשלוח לך התראות, להשתמש בסוללה או לגשת למיקום שלך"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"פרופיל העבודה מושהה. אפליקציות לעבודה לא יכולות לשלוח לך התראות, להשתמש בסוללה או לגשת למיקום שלך"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"השהיה של התראות ואפליקציות לעבודה"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ניתן למצוא כאן את אפליקציות העבודה"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"לכל אפליקציית עבודה יש תג ואבטחתה מטופלת בידי הארגון. אפשר להעביר אפליקציות אל מסך דף הבית כדי להקל את הגישה אליהן."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"מנוהל בידי הארגון"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"התראות ואפליקציות כבויות"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"סגירה"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"סגור"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"הפעולה נכשלה: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4e0181c..354f020 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"ホームの設定とショートカットの読み取り"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ウィジェットを表示できません"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"セットアップ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"このシステムアプリはアンインストールできません。"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"名前の編集"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"名前のないフォルダ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」は無効です"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> の通知が <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 件あります</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"タップして変更後の名前を保存します"</string>
<string name="folder_closed" msgid="4100806530910930934">"フォルダは閉じています"</string>
<string name="folder_renamed" msgid="1794088362165669656">"フォルダの名前を「<xliff:g id="NAME">%1$s</xliff:g>」に変更しました"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件のアイテム"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>、<xliff:g id="SIZE">%2$d</xliff:g> 件以上のアイテム"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"スタイルと壁紙"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"通知ドットを表示するには、「<xliff:g id="NAME">%1$s</xliff:g>」のアプリ通知を ON にしてください"</string>
<string name="title_change_settings" msgid="1376365968844349552">"設定を変更"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"通知ドットの表示"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ホーム画面にアプリのアイコンを追加"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ホーム画面にアイコンを追加"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"個人データは仕事用アプリとは別に保存され、一緒に表示されません"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"仕事用アプリと仕事用データは IT 管理者に公開されます"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"次へ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"仕事用プロファイルが一時停止しています"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"仕事用アプリは、通知の送信、電池の使用、位置情報へのアクセスを行えません"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"仕事用プロファイルが一時停止しています。仕事用アプリから通知の送信、電池の使用、位置情報へのアクセスを行えません"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"仕事用のアプリと通知を一時停止します"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ここには仕事用アプリが表示されます"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"仕事用アプリにはバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"組織によって管理されています"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"通知とアプリは OFF です"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"閉じる"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"終了"</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 44ea386..e9afef9 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"პრობლემა ვიჯეტის ჩატვირთვისას"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"დაყენება"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"სახელის რედაქტირება"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"უსახელო საქაღალდე"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაითიშა"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ში არის <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> შეტყობინება</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"შეეხეთ გადარქმეული სახელის შესანახად"</string>
<string name="folder_closed" msgid="4100806530910930934">"საქაღალდე დაიხურა"</string>
<string name="folder_renamed" msgid="1794088362165669656">"საქაღალდეს შეეცვალა სახელი „<xliff:g id="NAME">%1$s</xliff:g>“-ად"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ერთეული"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ან მეტი ერთეული"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"სტილები და ფონები"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"აპის ხატულების მთავარ ეკრანზე დამატება"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ხატულას მთავარ ეკრანზე დამატება"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"სამსახურის პროფილი"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"პერსონალური მონაცემები განცალკევებულია და თქვენი სამსახურის აპებისთვის უხილავია"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"თქვენი IT ადმინისტრატორი თქვენს სამსახურის აპებში არსებულ მონაცემებს ხედავს"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"შემდეგ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"გასაგებია"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"სამსახურის პროფილი დაპაუზებულია"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"სამსახურის აპებს არ შეუძლია თქვენთვის შეტყობინებების გამოგზავნა, თქვენი ბატარეის გამოყენება, ან თქვენს მდებარეობაზე წვდომა"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"სამსახურის პროფილი დაპაუზებულია. სამსახურის აპებს არ შეუძლია თქვენთვის შეტყობინებების გამოგზავნა, თქვენი ბატარეის გამოყენება, ან თქვენს მდებარეობაზე წვდომა"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"სამსახურის აპებისა და შეტყობინებების დაპაუზება"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"აქ თავმოყრილია სამსახურის აპები"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"სამსახურის თითოეულ აპს აქვს ბეჯი, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"იმართება თქვენი ორგანიზაციის მიერ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"შეტყობინებები და აპები გამორთულია"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"დახურვა"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"დახურული"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ვერ მოხერხდა: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 7dd4400..1cd9045 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"Негізгі экрандағы параметрлер мен төте пернелерді оқу"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Виджетті жүктеу барысында мәселе орын алды"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Орнату"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Атын өңдеу"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Атауы жоқ қалта"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өшірілді"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> хабарландыру бар</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Қайта атауды сақтау үшін түртіңіз"</string>
<string name="folder_closed" msgid="4100806530910930934">"Қалта жабылды"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> элемент бар"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> не одан көп элемент бар"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стильдер мен тұсқағаздар"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Қолданба белгішесін негізгі экранға қосу"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Негізгі экранға белгіше енгізу"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Жұмыс профилі"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Жеке деректер бөлек орналасқан және жұмыс қолданбаларынан жасырылған"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Әкімшіңіз жұмыс қолданбалары мен деректерді көре алады"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Келесі"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түсінікті"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Жұмыс профилі кідіртілді"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Жұмыс қолданбалары сізге хабарландырулар жібермейді, батареяңызды немесе геодерегіңізді пайдаланбайды."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Жұмыс профилі уақытша тоқтатылды. Жұмыс қолданбалары сізге хабарландырулар жібермейді, батареяңызды немесе геодерегіңізді пайдаланбайды."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Жұмыс қолданбалары мен хабарландыруларды кідірту"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Жұмыс қолданбалары осы жерде берілген"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Әрбір жұмыс қолданбасында танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Ұйым арқылы басқарылады"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Хабарландырулар мен қолданбалар өшірулі"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Жабу"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Жабық"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Қате шықты: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 3ba564e..9738042 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"អានការកំណត់ និងផ្លូវកាត់អេក្រង់ដើម"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"បញ្ហាក្នុងការផ្ទុកធាតុក្រាហ្វិក"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"រៀបចំ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះជាកម្មវិធីប្រព័ន្ធ មិនអាចលុបបានទេ។"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"កែឈ្មោះ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ថតគ្មានឈ្មោះ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"បានបិទដំណើរការ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, មានការជូនដំណឹង <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ប៉ះដើម្បីរក្សាទុកឈ្មោះដែលបានប្តូរ"</string>
<string name="folder_closed" msgid="4100806530910930934">"បានបិទថត"</string>
<string name="folder_renamed" msgid="1794088362165669656">"បានប្ដូរឈ្មោះថតជា <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>, ធាតុ <xliff:g id="SIZE">%2$d</xliff:g> ឬច្រើនជាងនេះ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ធាតុក្រាហ្វិក"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំងរូបភាព"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"រចនាប័ទ្ម និងផ្ទាំងរូបភាព"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"បញ្ចូលរូបកម្មវិធីទៅក្នុងអេក្រង់ដើម"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"បញ្ចូលរូបតំណាងទៅអេក្រង់ដើម"</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>
@@ -130,19 +127,16 @@
<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="accessibility_close" msgid="2277148124685870734">"បិទ"</string>
+ <string name="action_dismiss_notification" msgid="5909461085055959187">"បដិសេធ"</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="work_profile_toggle_label" msgid="3081029915775481146">"កម្រងព័ត៌មានការងារ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ទិន្នន័យផ្ទាល់ខ្លួនស្ថិតនៅដាច់ដោយឡែក និងត្រូវបានលាក់ពីកម្មវិធីការងារ"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ទិន្នន័យ និងកម្មវិធីការងារគឺអាចមើលឃើញដោយអ្នកគ្រប់គ្រងផ្នែកព័ត៌មានវិទ្យារបស់អ្នក"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"បន្ទាប់"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"យល់ហើយ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"កម្រងព័ត៌មានការងារត្រូវបានផ្អាក"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"កម្មវិធីការងារមិនអាចផ្ញើការជូនដំណឹងទៅអ្នក ប្រើប្រាស់ថ្មរបស់អ្នក ឬចូលប្រើទីតាំងរបស់អ្នកបានទេ"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"កម្រងព័ត៌មានការងារត្រូវបានផ្អាក។ កម្មវិធីការងារមិនអាចផ្ញើការជូនដំណឹងទៅអ្នក ប្រើប្រាស់ថ្មរបស់អ្នក ឬចូលប្រើទីតាំងរបស់អ្នកបានទេ"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ផ្អាកការជូនដំណឹង និងកម្មវិធីការងារ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ស្វែងរកកម្មវិធីការងារនៅទីនេះ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"កម្មវិធីការងារនីមួយៗមានស្លាកមួយ និងត្រូវបានរក្សាទុកយ៉ាងមានសុវត្ថិភាពដោយស្ថាប័នរបស់អ្នក។ សូមផ្លាស់ទីកម្មវិធីទៅកាន់អេក្រង់ដើមរបស់អ្នក ដើម្បីងាយស្រួលចូលប្រើជាងមុន។"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"គ្រប់គ្រងដោយស្ថាប័នរបស់អ្នក"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ការជូនដំណឹង និងកម្មវិធីត្រូវបានបិទ"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"បិទ"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"បានបិទ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"បានបរាជ័យ៖ <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 09c1152..67ea6a8 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಓದಿ"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ವಿಜೆಟ್ ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ಸೆಟಪ್"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"ಹೆಸರನ್ನು ಎಡಿಟ್ ಮಾಡಿ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="folder_closed" msgid="4100806530910930934">"ಫೋಲ್ಡರ್ ಮುಚ್ಚಿದೆ"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಐಟಂಗಳು"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ಅಥವಾ ಹೆಚ್ಚಿನ ಐಟಂಗಳು"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್ಗಳು"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್ಪೇಪರ್ಗಳು"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ಶೈಲಿಗಳು & ವಾಲ್ಪೇಪರ್ಗಳು"</string>
@@ -94,11 +91,11 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಆ್ಯಪ್ ಐಕಾನ್ಗಳು ಸೇರಿಸಿ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ"</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_search" msgid="891119232568284442">"Search"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
@@ -131,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ವೈಯಕ್ತಿಕ ಡೇಟಾ ಬೇರೆಯದಾಗಿದೆ ಮತ್ತು ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ನಿಂದ ಮರೆ ಮಾಡಲಾಗಿದೆ"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ನಿಮ್ಮ IT ನಿರ್ವಾಹಕರಿಗೆ ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾ ಗೋಚರಿಸುತ್ತದೆ"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ಮುಂದೆ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ಸರಿ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳಿಗೆ ನಿಮಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು, ನಿಮ್ಮ ಬ್ಯಾಟರಿಯನ್ನು ಬಳಸಲು ಅಥವಾ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಅನ್ನು ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ. ನಿಮಗೆ ಅಧಿಸೂಚನೆಗಳನ್ನು ಕಳುಹಿಸಲು, ನಿಮ್ಮ ಬ್ಯಾಟರಿ ಬಳಸಲು ಅಥವಾ ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಲು ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳಿಗೆ ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ಕೆಲಸಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಗಳನ್ನು ವಿರಾಮಗೊಳಿಸಿ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಇಲ್ಲಿ ಹುಡುಕಿ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸರಿಸಿ."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ಮುಚ್ಚಿ"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ಮುಚ್ಚಲಾಗಿದೆ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ವಿಫಲವಾಗಿದೆ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index aaf6dff..58b65da 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -32,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"위젯을 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
<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="add_item_request_drag_hint" msgid="5899764264480397019">"길게 터치하여 직접 추가하세요."</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"길게 터치하여 직접 추가"</string>
<string name="place_automatically" msgid="8064208734425456485">"자동으로 추가"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"앱 검색"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"앱 로드 중…"</string>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"홈 설정 및 바로가기 읽기"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"위젯을 로드하는 중 문제가 발생했습니다."</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"설정"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"시스템 앱은 제거할 수 없습니다."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"이름 수정"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"이름이 없는 폴더"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> 사용 안함"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>에 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>개의 알림이 있음</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"탭하여 변경된 이름 저장"</string>
<string name="folder_closed" msgid="4100806530910930934">"폴더 닫음"</string>
<string name="folder_renamed" msgid="1794088362165669656">"폴더 이름 변경: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"폴더: <xliff:g id="NAME">%1$s</xliff:g>, 항목 <xliff:g id="SIZE">%2$d</xliff:g>개 이상"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"폴더: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"스타일 및 배경화면"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"홈 화면에 앱 아이콘 추가"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"홈 화면에 아이콘 추가"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"직장 프로필"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"개인 정보는 직장 앱에서 분리되어 숨겨짐"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"직장 앱 및 데이터가 IT 관리자에게 표시됨"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"다음"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"확인"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"직장 프로필이 일시중지됨"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"직장 앱에서 알림을 보내거나 배터리를 사용하거나 위치 정보에 액세스할 수 없습니다."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"직장 프로필이 일시중지되었습니다. 직장 앱에서 알림을 보내거나 배터리를 사용하거나 위치 정보에 액세스할 수 없습니다."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"직장 앱 및 알림 일시중지"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"여기에서 업무용 앱 찾기"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"각 업무용 앱에는 배지가 있으며 업무용 앱은 조직에서 안전하게 보호됩니다. 앱을 홈 화면으로 이동하여 더 간편하게 사용하세요."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"조직에서 관리"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"알림 및 앱 사용 중지됨"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"닫기"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"종료됨"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"실패: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index d4b4c5d..d52446c 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"Үйдүн тууралоолорун жана тез чакырмаларын окуу"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Виджетти жүктөөдө маселе бар"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Орнотуу"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Аталышын түзөтүү"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Аты жок фолдер"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> билдирмеси бар</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Өзгөртүлгөн аталышын сактоо үчүн таптаңыз"</string>
<string name="folder_closed" msgid="4100806530910930934">"Фолдер жабык"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"<xliff:g id="NAME">%1$s</xliff:g> папкасындагы объекттер: <xliff:g id="SIZE">%2$d</xliff:g> же андан көбүрөөк"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилдер жана тушкагаздар"</string>
@@ -91,10 +88,10 @@
<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="msg_missing_notification_access" msgid="281113995110910548">"Эскертме белгилерин көрсөтүү максатында, <xliff:g id="NAME">%1$s</xliff:g> үчүн колдонмонун билдирмелерин күйгүзүү керек"</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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Колдонмонун сүрөтчөсүн Башкы экранга кошуу"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Башкы экранга сүрөтчө кошуу"</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>
@@ -114,7 +111,7 @@
<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_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="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>
@@ -131,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Жумуш профили"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Жеке маалымат жумуш колдонмолорунан өзүнчө сакталат"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Жумуш колдонмолору жана маалымат IT администраторго гана көрүнөт"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Кийинки"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Түшүндүм"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Жумуш профили тындырылган"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Жумуш профили тындырылган. Жумуш колдонмолору билдирмелерди жөнөтүп, түзмөгүңүздүн батареясын керектеп же кайда жүргөнүңүздү көрө албайт"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Жумуш колдонмолорун жана билдирмелерди тындыруу"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Жумуш колдонмолорун бул жерден таап алыңыз"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Ар бир жумуш колдонмосунун бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Уюмуңуз тарабынан башкарылат"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Билдирүүлөр жана колдонмолор өчүрүлгөн"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Жабуу"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Жабык"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Аткарылган жок: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index b8ca8a4..afe7664 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ມີບັນຫາໃນການໂຫລດວິດເຈັດ"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ຕິດຕັ້ງ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"ແກ້ໄຂຊື່"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"ປິດການນຳໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
@@ -78,14 +77,13 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ແຕະເພື່ອບັນທຶກການປ່ຽນຊື່"</string>
<string name="folder_closed" msgid="4100806530910930934">"ປິດໂຟນເດີແລ້ວ"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ປ່ຽນຊື່ໂຟນເດີເປັນ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ລາຍການ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ຫຼື ລາຍການເພີ່ມເຕີມ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ຮູບແບບ ແລະ ຮູບພື້ນຫຼັງ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍຢູ່ໜ້າຫຼັກໄດ້"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</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>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ເພີ່ມໄອຄອນແອັບໄປໃສ່ໜ້າຈໍຫຼັກ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ຂໍ້ມູນສ່ວນຕົວຈະຖືກແຍກອອກ ແລະ ເຊື່ອງໄວ້ຈາກແອັບວຽກ"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ແອັບ ແລະ ຂໍ້ມູນວຽກຈະສະແດງໃຫ້ຜູ້ເບິ່ງແຍງໄອທີຂອງທ່ານເຫັນ"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ຕໍ່ໄປ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ເຂົ້າໃຈແລ້ວ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ຢຸດໂປຣໄຟລ໌ວຽກໄວ້ຊົ່ວຄາວແລ້ວ"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"ແອັບວຽກບໍ່ສາມາດສົ່ງການແຈ້ງເຕືອນໃຫ້ທ່ານ, ໃຊ້ແບັດເຕີຣີຂອງທ່ານ ຫຼື ເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ຢຸດໂປຣໄຟລ໌ບ່ອນເຮັດວຽກໄວ້ຊົ່ວຄາວແລ້ວ. ແອັບວຽກຈະບໍ່ສາມາດສົ່ງການແຈ້ງເຕືອນໃຫ້ທ່ານ, ໃຊ້ແບັດເຕີຣີຂອງທ່ານ ຫຼື ເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ຢຸດແອັບວຽກ ແລະ ການແຈ້ງເຕືອນໄວ້ຊົ່ວຄາວ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ຊອກຫາແອັບວຽກຢູ່ບ່ອນນີ້"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ແຕ່ລະແອັບວຽກຈະມີປ້າຍ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ຈັດການໂດຍອົງກອນຂອງທ່ານ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ປິດ"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ປິດແລ້ວ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ບໍ່ສຳເລັດ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index d959788..6571582 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Darbas"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Programa neįdiegta."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Programa nepasiekiama"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Įdiegti"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nesiūlyti programos"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Prisegti numatymą"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problema įkeliant valdiklį"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Sąranka"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Pavadinimo redagavimas"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Aplankas be pavadinimo"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ išjungta"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one">Programoje „<xliff:g id="APP_NAME_2">%1$s</xliff:g>“ yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimas</item>
@@ -80,8 +79,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Palieskite, kad išsaugotumėte pakeistą pavadinimą"</string>
<string name="folder_closed" msgid="4100806530910930934">"Aplankas uždarytas"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Aplankas pervardytas kaip „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“, elementų: <xliff:g id="SIZE">%2$d</xliff:g> ar daugiau"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiliai ir ekrano fonai"</string>
@@ -96,7 +94,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Kad būtų rodomi pranešimų taškai, įjunkite programos „<xliff:g id="NAME">%1$s</xliff:g>“ pranešimus."</string>
<string name="title_change_settings" msgid="1376365968844349552">"Keisti nustatymus"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Rodyti pranešimų taškus"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Pridėti progr. piktogr. pagrind. ekrane"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pridėti piktogr. prie pagrindinio ekrano"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Skirta naujoms programoms"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Panaikinti"</string>
@@ -133,18 +131,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Spartieji klavišai"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Spartieji klavišai ir pranešimai"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Atsisakyti"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Uždaryti"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Pranešimo atsisakyta"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Asmeninės"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Darbo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Darbo profilis"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Asmens duomenys laikomi atskirai ir paslėpti nuo darbo programų"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Darbo programas ir duomenis mato jūsų IT administratorius"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Kitas"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Supratau"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Darbo profilis pristabdytas"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Darbo programos negali siųsti jums pranešimų, naudoti jūsų akumuliatoriaus ar pasiekti jūsų vietovės"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Darbo profilis pristabdytas. Darbo programos negali siųsti jums pranešimų, naudoti jūsų akumuliatoriaus ar pasiekti jūsų vietovės"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pristabdykite darbo programas ir pranešimus"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Darbo programas rasite čia"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Kiekvienai darbo programai priskirtas ženklelis, o tokių programų sauga rūpinasi jūsų organizacija. Perkelkite programas į pagrindinį ekraną, kad galėtumėte lengviau jas pasiekti."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Tvarko jūsų organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Programos ir pranešimai išjungti"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Uždaryti"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Uždaryta"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Nepavyko: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index aa01d6d..3da0fbb 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Darbs"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Lietotne nav instalēta."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Lietotne nav pieejama."</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Par lietotni"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalēt"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Neieteikt lietotni"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Piespraust prognozēto lietotni"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lasīt sākuma ekrāna iestatījumus un saīsnes"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Ielādējot logrīku, radās problēma."</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Notiek iestatīšana"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Nosaukuma rediģēšana"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Mape bez nosaukuma"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> ir atspējota"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="zero">Lietotnē <xliff:g id="APP_NAME_2">%1$s</xliff:g> ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
@@ -79,8 +78,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Pieskarieties, lai saglabātu jauno nosaukumu."</string>
<string name="folder_closed" msgid="4100806530910930934">"Mape aizvērta"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Mape pārdēvēta par: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mape <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> vienumi"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mape <xliff:g id="NAME">%1$s</xliff:g>, vienumu skaits mapē: vismaz <xliff:g id="SIZE">%2$d</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mape: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stili un fona tapetes"</string>
@@ -95,7 +93,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Lai tiktu rādīti paziņojumu punkti, ieslēdziet paziņojumus lietotnei <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="title_change_settings" msgid="1376365968844349552">"Mainīt iestatījumus"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Rādīt paziņojumu punktus"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Pievienot lietotņu ikonas sākuma ekrānam"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pievienot ikonu sākuma ekrānā"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Jaunām lietotnēm"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
@@ -132,18 +130,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Saīsnes"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Saīsnes un paziņojumi"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Nerādīt"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Aizvērt"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Paziņojums netiek rādīts"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personīgās lietotnes"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Darba lietotnes"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Darba profils"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personas dati ir atsevišķi un paslēpti no darba lietotnēm"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Jūsu IT administrators var skatīt darba lietotnes un datus"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Tālāk"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Labi"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Darba profila darbība ir pārtraukta"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Darba lietotnes nevar nosūtīt jums paziņojumus, izmantot akumulatoru un piekļūt jūsu atrašanās vietai."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Darba profila darbība ir pārtraukta. Darba lietotnes nevar sūtīt jums paziņojumus, izmantot akumulatoru un piekļūt jūsu atrašanās vietai."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pārtraukt darba lietotņu darbību un paziņojumu sūtīšanu"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Meklējiet darba lietotnes šeit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Katrai darba lietotnei ir emblēma, un jūsu organizācija aizsargā šīs lietotnes. Lai varētu ērtāk piekļūt lietotnēm, pārvietojiet tās uz sākuma ekrānu."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Pārvalda jūsu organizācija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Paziņojumi un lietotnes ir izslēgtas"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Aizvērt"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Aizvērta"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Neizdevās: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 82ffdd5..ab14e89 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"чита поставки и кратенки на почетна страница"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Проблем при вчитувањето на виџетот"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Поставување"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Изменете го името"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Неименувана папка"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> е оневозможена"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известување</item>
@@ -78,14 +76,13 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Допрете за да го зачувате преименувањето"</string>
<string name="folder_closed" msgid="4100806530910930934">"Папката е затворена"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставки"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или повеќе ставки"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и тапети"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Поставки за почетен екран"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволи ротација на почетниот екран"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</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>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Додавај икони за апликации на почетен екран"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икона на почетниот екран"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Работен профил"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Личните податоци се одделни и сокриени од работните апликации"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Работните апликации и податоци се видливи за IT-администраторот"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Следно"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Сфатив"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Работниот профил е паузиран"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Работните апликации не можат да ви испраќаат известувања, да ја користат батеријата или да пристапуваат до вашата локација"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Работниот профил е паузиран. Работните апликации не можат да ви испраќаат известувања, да ја користат батеријата или да пристапуваат до вашата локација"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Паузирајте работни апликации и известувања"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Најдете апликации за работа тука"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Секоја апликација за работа има значка, а организацијата се грижи за нејзината безбедност. За полесен пристап, преместете ги апликациите на почетниот екран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Управувано од вашата организација"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Известувањата и апликациите се исклучени"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затвори"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не успеа: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 8156318..4362e7c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"ലോഞ്ചർ3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"ഹോം"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"വിജറ്റ് ലോഡുചെയ്യുന്നതിൽ പ്രശ്നമുണ്ട്"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"സജ്ജീകരിക്കുക"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്റ്റാളുചെയ്യാനാവില്ല."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"പേര് എഡിറ്റ് ചെയ്യുക"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"പേരുനൽകാത്ത ഫോൾഡർ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> എന്ന ആപ്പിന്, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> അറിയിപ്പുകൾ ഉണ്ട്</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
<string name="folder_closed" msgid="4100806530910930934">"ഫോൾഡർ അടച്ചു"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ഇനങ്ങൾ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> അല്ലെങ്കിൽ അതിലധികം ഇനങ്ങൾ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"വിഡ്ജെറ്റുകൾ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"സ്റ്റൈലുകളും വാൾപേപ്പറുകളും"</string>
@@ -94,11 +92,11 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ഹോം സ്ക്രീനിൽ ആപ്പ് ഐക്കണുകൾ ചേർക്കൂ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക"</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_search" msgid="891119232568284442">"Search"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
@@ -131,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ഔദ്യോഗിക ആപ്പുകളിൽ നിന്ന് വ്യക്തിപരമായ ഡാറ്റ വേർതിരിച്ച് മറച്ചിരിക്കുന്നു"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ഔദ്യോഗിക ആപ്പുകളും ഡാറ്റയും നിങ്ങളുടെ ഐടി അഡ്മിന് ദൃശ്യമാണ്"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"അടുത്തത്"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"മനസ്സിലായി"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ഔദ്യോഗിക പ്രൊഫൈൽ തൽക്കാലം നിർത്തിയിരിക്കുന്നു"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാനോ നിങ്ങളുടെ ബാറ്ററി ഉപയോഗിക്കാനോ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാനോ ഔദ്യോഗിക ആപ്പുകൾക്ക് കഴിയില്ല"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ഔദ്യോഗിക പ്രൊഫൈൽ തൽക്കാലം നിർത്തിയിരിക്കുന്നു. നിങ്ങൾക്ക് അറിയിപ്പുകൾ അയയ്ക്കാനോ നിങ്ങളുടെ ബാറ്ററി ഉപയോഗിക്കാനോ ലൊക്കേഷൻ ആക്സസ് ചെയ്യാനോ ഔദ്യോഗിക ആപ്പുകൾക്ക് കഴിയില്ല"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ഔദ്യോഗിക ആപ്പുകളും അറിയിപ്പുകളും താൽക്കാലികമായി നിർത്തുക"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ഔദ്യോഗിക ആപ്പുകൾ ഇവിടെ കണ്ടെത്തുക"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഒരു ബാഡ്ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്ക്രീനിലേക്ക് നീക്കുക."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"നിങ്ങളുടെ സ്ഥാപനം നിയന്ത്രിക്കുന്നത്"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"അടയ്ക്കുക"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"അടച്ചു"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"പരാജയപ്പെട്ടു: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 1d680ab..ab02ac6 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -35,7 +36,7 @@
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"Гараар байршуулахын тулд дараад хүлээнэ үү"</string>
<string name="place_automatically" msgid="8064208734425456485">"Автоматаар нэмэх"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Апп хайх"</string>
- <string name="all_apps_loading_message" msgid="5813968043155271636">"Аппыг ачаалж байна..."</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>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Нүүр"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Нүүрний тохиргоо болон товчлолыг унших"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Виджет ачаалахад асуудал гарав"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Тохируулга"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Нэр засах"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Нэргүй фолдер"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> мэдэгдэлтэй байна</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Шинэ нэрийг хадгалахын тулд дарна уу."</string>
<string name="folder_closed" msgid="4100806530910930934">"Фолдер хаагдав"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Фолдерын нэр <xliff:g id="NAME">%1$s</xliff:g> болов"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> зүйл"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> эсвэл үүнээс олон зүйл"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Загвар ба ханын зураг"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Аппын дүрс тэмдгийг Үндсэн нүүрэнд нэмэх"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Нүүр хуудаст дүрс тэмдэг нэмэх"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Ажлын профайл"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Хувийн өгөгдөл нь ажлын аппаас тусдаа бөгөөд нуугдмал байна"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Ажлын апп болон өгөгдөл нь таны мэдээлэл технологийн админд харагдана"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Дараах"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ойлголоо"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Ажлын профайлыг түр зогсоосон"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Ажлын аппууд танд мэдэгдэл илгээх, таны батарейг ашиглах эсвэл байршилд тань хандах боломжгүй"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Ажлын профайлыг түр зогсоосон. Ажлын апп танд мэдэгдэл илгээх боломжгүй тул батарейгаа ашиглах эсвэл байршилдаа хандана уу"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Ажлын апп болон мэдэгдлийг түр зогсоох"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ажлын аппыг эндээс олно уу"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Ажлын апп тус бүр тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбар хандахын тулд тэдгээрийг Үндсэн нүүр хэсэгт зөөнө үү."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Танай байгууллагаас удирддаг"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Мэдэгдэл, апп унтраалттай байна"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Хаах"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Хаасан"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Амжилтгүй болсон: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 6f50f05..49e3898 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -46,14 +47,12 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"अॅप्स सूची"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"वैयक्तिक अॅप्स सूची"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"कामाच्या ठिकाणी वापरली जाणाऱ्या ॲप्सची सूची"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"कामाच्या ठिकाणी वापरली जाणाऱ्या अॅप्सची सूची"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"होम"</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="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="1941457408239617576">"होम सेटिंग्ज आणि शॉर्टकट वाचा"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"विजेट लोड करण्यात समस्या"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अॅप आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"नाव संपादित करा"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फोल्डर"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>साठी <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहेत</item>
@@ -78,27 +77,26 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे सेव्ह करण्यासाठी टॅप करा"</string>
<string name="folder_closed" msgid="4100806530910930934">"फोल्डर बंद"</string>
<string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> आयटम"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> किंवा त्याहून अधिक आयटम"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली आणि वॉलपेपर"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्ज"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्य स्क्रीन फिरविण्यास अनुमती द्या"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्यास अनुमती द्या"</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>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"बंद"</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="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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"होम स्क्रीनवर ॲप आयकन जोडा"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन ॲप्ससाठी"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीनवर आयकन जोडा"</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_search" msgid="891119232568284442">"Search"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
@@ -131,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"वैयक्तिक डेटा वेगळा असतो आणि तो ऑफिस अॅप्सपासून लपवलेला असतो"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"कार्य अॅप्स आणि डेटा तुमच्या अॅडमिनला दृश्यमान असतो"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"पुढील"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"समजले"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्य प्रोफाइल थांबवली आहे"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"कामासंबंधित अॅप्स तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अॅक्सेस करू शकत नाहीत"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"कार्य प्रोफाइल थांबवली आहे. कार्य अॅप्स तुम्हाला सूचना पाठवू शकत नाहीत, तुमची बॅटरी वापरू शकत नाहीत किंवा तुमचे स्थान अॅक्सेस करू शकत नाहीत"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"कार्य अॅप्स आणि सूचना थांबवा"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"कामाची अॅप्स येथे मिळवा"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"प्रत्येक कार्य अॅपला एक बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अॅक्सेससाठी अॅप्स तुमच्या होम स्क्रीनवर हलवा."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"तुमच्या संस्थेकडून व्यवस्थापित"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना आणि अॅप्स बंद आहेत"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बंद करा"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बंद केले"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"हे करता आले नाही: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 38e87f1..7d05412 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kerja"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Apl tidak dipasang."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Apl tidak tersedia"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Pasang"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Jangan cadangkan apl"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Sematkan Ramalan"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"baca tetapan dan pintasan Laman Utama"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuatkan widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Persediaan"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edit Nama"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pemberitahuan</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketik untuk menyimpan penamaan semula"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder ditutup"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> item"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> atau lebih banyak item"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya & kertas dinding"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Untuk menunjukkan Titik Pemberitahuan, hidupkan pemberitahuan apl untuk <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Tukar tetapan"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Tunjukkan titik pemberitahuan"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Tambahkan ikon apl pada Skrin utama"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon pada Skrin Utama"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk apl baharu"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Pintasan"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Pintasan dan pemberitahuan"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ketepikan"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Tutup"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Pemberitahuan diketepikan"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Peribadi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Data peribadi adalah berasingan & disembunyikan daripada apl kerja"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Apl & data kerja kelihatan kepada pentadbir IT anda"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Seterusnya"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Profil kerja dijeda"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Apl kerja tidak boleh menghantar pemberitahuan kepada anda, menggunakan bateri anda atau mengakses lokasi anda"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profil kerja dijeda. Apl kerja tidak boleh menghantar pemberitahuan kepada anda, menggunakan bateri anda atau mengakses lokasi anda"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Jeda apl kerja dan pemberitahuan"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Temui apl kerja di sini"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Setiap apl kerja terdapat lencana dan dilindungi oleh organisasi anda. Alihkan apl ke Skrin Utama untuk akses yang lebih mudah."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Diurus oleh organisasi anda"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Pemberitahuan dan apl dimatikan"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Tutup"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Ditutup"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Gagal: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index d753021..78856f6 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -50,10 +50,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"စဖွင့်သတ်မှတ်ရန်"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"အမည်ကို တည်းဖြတ်ပါ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"အမည်မရှိအကန့်"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> တွင် သတိပေးချက် <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ခု ရှိသည်</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"အမည်ပြောင်းခြင်းကို သိမ်းရန် တို့ပါ"</string>
<string name="folder_closed" msgid="4100806530910930934">"ပိတ်ထားသောအကန့်"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> ဖိုင်များ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ဖိုင်တွဲ - <xliff:g id="NAME">%1$s</xliff:g>၊ <xliff:g id="SIZE">%2$d</xliff:g> သို့မဟုတ် နောက်ထပ်ဖိုင်များ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ပုံစံနှင့် နောက်ခံပုံများ"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"\'ပင်မစာမျက်နှာ\' တွင် အက်ပ်သင်္ကေတထည့်ရန်"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"အလုပ်ပရိုဖိုင်"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ကိုယ်ပိုင်ဒေတာများသည် သီးသန့်ဖြစ်ပြီး အလုပ်အက်ပ်များမှ ဖျောက်ထားသည်"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"အလုပ်သုံးအက်ပ်နှင့် ဒေတာများကို သင်၏ IT စီမံခန့်ခွဲသူက မြင်ရပါသည်"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ရှေ့သို့"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"အလုပ်ပရိုဖိုင် ခဏရပ်ထားသည်။ အလုပ်သုံးအက်ပ်များက အကြောင်းကြားချက်များ ပို့ခြင်း၊ သင့်ဘက်ထရီ သုံးခြင်း (သို့) သင့်တည်နေရာ သုံးခြင်းတို့ မပြုလုပ်နိုင်ပါ"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"အလုပ်သုံးအက်ပ်နှင့် အကြောင်းကြားချက်များ ခဏရပ်ရန်"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"အလုပ်အက်ပ်များကို ဤနေရာတွင်ရှာဖွေပါ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"အလုပ်အက်ပ်တိုင်းတွင် တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"သင်၏ အဖွဲ့အစည်းက စီမံခန့်ခွဲထားပါသည်"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ပိတ်ရန်"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ပိတ်ထားသည်"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"မအောင်မြင်ပါ− <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 47b4635..2257367 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Jobb"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installert."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgjengelig"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installer"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ikke foreslå app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fest forslaget"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lese startsideinnstillinger og -snarveier"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem ved innlasting av modul"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurering"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Rediger navn"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Mappe uten navn"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Slo av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> varsler</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Trykk for å lagre det nye navnet"</string>
<string name="folder_closed" msgid="4100806530910930934">"Mappen ble lukket"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Mappen heter nå <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementer"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller flere elementer"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiler og bakgrunner"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Slå på appvarsler for <xliff:g id="NAME">%1$s</xliff:g> for å vise varselsprikker"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Endre innstillingene"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Vis varselsprikker"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Legg til appikoner på startskjermen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Legg til ikon på startsiden"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"For nye apper"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Snarveier"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Snarveier og varsler"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Avvis"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Lukk"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Varselet ble avvist"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Jobb"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Personlige data er atskilte og skjules fra jobbapper"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Jobbapper og -data er synlige for IT-administratoren din"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Neste"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Greit"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Jobbprofilen er satt på pause"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Jobbapper kan ikke sende deg varsler, bruke batteriet eller få tilgang til posisjonen din"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Jobbprofilen er satt på pause. Jobbapper kan ikke sende deg varsler, bruke batteriet eller få tilgang til posisjonen din"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Sett jobbapper og -varsler på pause"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Finn jobbapper her"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Alle jobbapper har et merke og sikres av organisasjonen din. Flytt apper til startskjermen for å gjøre det enklere å finne dem."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administreres av organisasjonen din"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Varsler og apper er slått av"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Lukk"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Lukket"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislyktes: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 23394a7..1e7aee9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -20,10 +20,11 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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="806512411299847073">"गृह स्क्रिन"</string>
@@ -34,37 +35,35 @@
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौडाइ गुणा %2$d उचाइ"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"म्यानुअल तरिकाले थप्न छुनुहोस् र थिची राख्नुहोस्"</string>
<string name="place_automatically" msgid="8064208734425456485">"स्वतः थप्नुहोस्"</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_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="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
<string name="long_press_shortcut_to_add" msgid="4524750017792716791">"कुनै सर्टकट छनौट गर्न छोइराख्नुहोस्।"</string>
<string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"कुनै सर्टकट छनौट गर्न वा रोजेका कारबाहीहरू प्रयोग गर्न डबल ट्याप गरेर छोइराख्नुहोस्।"</string>
<string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"मन पर्ने ट्रे अब कुनै ठाँउ छैन"</string>
- <string name="all_apps_button_label" msgid="8130441508702294465">"एपको सूची"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"अनुप्रयोगको सूची"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"व्यक्तिगत अनुप्रयोगहरूको सूची"</string>
<string name="all_apps_button_work_label" msgid="7270707118948892488">"कार्यसम्बन्धी अनुप्रयोगहरूको सूची"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा एपलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा एपलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा अनुप्रयोगलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा एपलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा अनुप्रयोगलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</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="6081085226050792095">"समस्या लोडिङ गर्ने विजेट"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली एप हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"नाम सम्पादन गर्नुहोस्"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"बेनाम फोल्डर"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, का <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाहरू छन्</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनःनामाकरणलाई सुरक्षित गर्न ट्याप गर्नुहोस्"</string>
<string name="folder_closed" msgid="4100806530910930934">"फोल्डर बन्द भयो"</string>
<string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वस्तुहरू"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> वा सोभन्दा बढी वस्तुहरू"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली तथा वालपेपरहरू"</string>
@@ -94,13 +92,13 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"गृह स्क्रिनमा एपका आइकनहरू थप्नुहोस्"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"गृह स्क्रिनमा आइकन थप्नुहोस्"</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_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_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>
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेटहरू"</string>
@@ -131,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"व्यक्तिगत डेटा कामसम्बन्धी एपहरूबाट लुकाएर छुट्टै राखिन्छ"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"तपाईंका IT एड्मिनले कामसम्पबन्धी एपहरू र डेटा हेर्न सक्छन्"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"अर्को"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको यन्त्रको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ। कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको यन्त्रको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"कामसम्बन्धी एप र सूचनाहरू अस्थायी रूपमा रोक्का गर्नुहोस्"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"कार्यसम्बन्धी अनुप्रयोगहरू यहाँ प्राप्त गर्नुहोस्"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा ब्याज छ र तपाईंको संगठनले यसलाई सुरक्षित राखेको छ । अझ सजिलो गरी पहुँच राख्नका लागि अनुप्रयोगहरूलाई आफ्नो गृहस्क्रिनमा सार्नुहोस्।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"तपाईंको सङ्गठनले व्यवस्थापन गरेको"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना र अनुप्रयोगहरू निष्क्रिय छन्"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"बन्द गर्नुहोस्"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"बन्द गरियो"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-night-v26/styles.xml b/res/values-night/styles.xml
similarity index 100%
rename from res/values-night-v26/styles.xml
rename to res/values-night/styles.xml
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index ded05b0..ec30d8c 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Werk"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App is niet geïnstalleerd."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App is niet beschikbaar"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installeren"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Geen app voorstellen"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Vastzetvoorspelling"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op startscherm lezen"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Probleem bij het laden van widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuratie"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Naam bewerken"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Naamloze map"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> is uitgeschakeld"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, heeft <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> meldingen</item>
@@ -78,12 +77,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om de gewijzigde naam op te slaan"</string>
<string name="folder_closed" msgid="4100806530910930934">"Map gesloten"</string>
<string name="folder_renamed" msgid="1794088362165669656">"De naam van de map is gewijzigd in <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> items"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Map: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> of meer items"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Map: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stijl en achtergrond"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Instellingen start"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Instellingen startscherm"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Draaien van startscherm toestaan"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer de telefoon gedraaid is"</string>
@@ -94,13 +92,13 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Als je meldingsstipjes wilt weergeven, schakel je app-meldingen in voor <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Instellingen wijzigen"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Meldingsstipjes weergeven"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"App-iconen toevoegen aan startscherm"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pictogram toevoegen aan startscherm"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Voor nieuwe apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
<string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Deze app is niet geïnstalleerd"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"De app voor dit icoon is niet geïnstalleerd. Je kunt het icoon verwijderen of de app zoeken en handmatig installeren."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"De app voor dit pictogram is niet geïnstalleerd. Je kunt het pictogram verwijderen of de app zoeken en handmatig installeren."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wordt gedownload, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wacht op installatie"</string>
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-widgets"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Snelkoppelingen"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Snelkoppelingen en meldingen"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Sluiten"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Sluiten"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Melding gesloten"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Privé"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Persoonlijke gegevens zijn afgescheiden en verborgen voor werk-apps"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Werk-apps en -gegevens zijn zichtbaar voor je IT-beheerder"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Volgende"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Werkprofiel is onderbroken"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Werkprofiel is onderbroken. Werk-apps kunnen je geen meldingen sturen, niet je batterij gebruiken en geen toegang krijgen tot je locatie"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Werk-apps en -meldingen onderbreken"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Zoek hier naar werk-apps"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Elke werk-app heeft een badge en wordt beveiligd door je organisatie. Verplaats apps naar je startscherm voor snelle toegang."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Beheerd door je organisatie"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Meldingen en apps zijn uitgeschakeld"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Sluiten"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Gesloten"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Mislukt: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 994b620..4ddc903 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"ଲଞ୍ଚର୍3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"ୱାର୍କ"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"ହୋମ୍ ସେଟିଙ୍ଗ ଏବଂ ଶର୍ଟକଟ୍ ପଢ଼ନ୍ତୁ"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ୱିଜେଟ୍ ଲୋଡ୍ ହେବାରେ ସମସ୍ୟା ଅଛି"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ଏହା ଏକ ସିଷ୍ଟମ୍ ଆପ୍ ଅଟେ ଏବଂ ଏହା ଅନଇନଷ୍ଟଲ୍ କରାଯାଇ ପାରିବ ନାହିଁ।"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"ନାମ ସମ୍ପାଦନ କରନ୍ତୁ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ବେନାମୀ ଫୋଲ୍ଡର୍"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଅକ୍ଷମ କରାଗଲା"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ଟି ବିଜ୍ଞପ୍ତି ରହିଛି</item>
@@ -78,27 +77,26 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ନାମ ବଦଳାଇବା ସେଭ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="folder_closed" msgid="4100806530910930934">"ଫୋଲ୍ଡର ବନ୍ଦ କରାଗଲା"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ଫୋଲ୍ଡରର ନାମ <xliff:g id="NAME">%1$s</xliff:g>କୁ ବଦଳାଗଲା"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ଆଇଟମଗୁଡ଼ିକ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ଫୋଲ୍ଡର୍: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> କିମ୍ବା ଅଧିକ ଆଇଟମ୍"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ଫୋଲ୍ଡର: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ୱିଜେଟ୍"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍"</string>
- <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ଷ୍ଟାଇଲ୍ ଏବଂ ୱାଲ୍ପେପର୍"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ୍ ସେଟିଂସ୍"</string>
+ <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ଶୈଳୀ ଏବଂ ୱାଲ୍ପେପର୍"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ୍ ସେଟିଙ୍ଗ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ହୋମ୍ ସ୍କ୍ରିନ୍ ବୁଲାଇବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ହୋମ୍ ସ୍କ୍ରୀନ୍ ବୁଲାଇବା ଅନୁମତି ଦିଅନ୍ତୁ"</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>
+ <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="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="title_change_settings" msgid="1376365968844349552">"ସେଟିଙ୍ଗ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"ବିଜ୍ଞପ୍ତି ଡଟ୍ଗୁଡ଼ିକୁ ଦେଖାନ୍ତୁ"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ହୋମ୍ ସ୍କ୍ରିନରେ ଆପ୍ ଆଇକନଗୁଡ଼ିକୁ ଯୋଗ କରନ୍ତୁ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ହୋମ୍ ସ୍କ୍ରୀନରେ ଆଇକନ୍କୁ ଯୋଡ଼ନ୍ତୁ"</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_search" msgid="891119232568284442">"Search"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ଡାଉନଲୋଡ୍ ହେଉଛି, <xliff:g id="PROGRESS">%2$s</xliff:g> ସମ୍ପୂର୍ଣ୍ଣ"</string>
@@ -131,18 +129,15 @@
<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="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_personal_apps" msgid="4155536355149317441">"ବ୍ୟକ୍ତିଗତ ଡାଟା କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଠାରୁ ପୃଥକ୍ ଓ ଲୁକ୍କାୟିତ ଅଟେ"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଓ ଡାଟା ଆପଣଙ୍କ IT ଆଡମିନଙ୍କୁ ଦେଖାଯାଏ"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ପରବର୍ତ୍ତୀ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ବିରତ କରାଯାଇଛି"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପଗୁଡ଼ିକ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ବିରତ କରାଯାଇଛି। କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରନ୍ତୁ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ଏଠାରେ କାମ ଆପ୍ ଖୋଜନ୍ତୁ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ପ୍ରତ୍ୟେକ କାଯ୍ୟକାରୀ ଆପ୍ର ଗୋଟିଏ ବ୍ୟାଜ୍ (ଚିହ୍ନ) ଅଛି, ଯାହାକୁ ଆପଣଙ୍କ ସଂସ୍ଥା ସୁରକ୍ଷିତ ରଖିଥାଏ। ସହଜରେ ଆକ୍ସେସ୍ କରିବା ପାଇଁ ଆପ୍କୁ ହୋମ୍ ସ୍କ୍ରୀନ୍ ଉପରକୁ ଆଣନ୍ତୁ।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ପରିଚାଳିତ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ବିଜ୍ଞପ୍ତି ଓ ଆପ୍ଗୁଡ଼ିକ ବନ୍ଦ ଅଛି"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ବନ୍ଦ ହୋଇଯାଇଛି"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index c1d7dfa..d4cd5be 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"ਕਾਰਜ-ਸਥਾਨ"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ਸਥਾਪਤ ਕਰੋ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"ਨਾਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ਬਿਨਾਂ ਨਾਮ ਦਿੱਤਾ ਫੋਲਡਰ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ਦੀ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾ</item>
@@ -78,15 +77,14 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"ਬਦਲੇ ਗਏ ਨਾਮ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="folder_closed" msgid="4100806530910930934">"ਫੋਲਡਰ ਬੰਦ ਕੀਤਾ"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ਫੋਲਡਰ ਨੂੰ <xliff:g id="NAME">%1$s</xliff:g> ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਆਈਟਮਾਂ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ਜਾਂ ਹੋਰ ਆਈਟਮਾਂ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ਸ਼ੈਲੀਆਂ ਅਤੇ ਵਾਲਪੇਪਰ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ਜਦੋਂ ਫ਼ੋਨ ਘੁਮਾਇਆ ਜਾਂਦਾ ਹੈ"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</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>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"ਬੰਦ"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਐਪ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ਨਿੱਜੀ ਡਾਟਾ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੋਂ ਵੱਖ ਅਤੇ ਲੁਕਾਇਆ ਹੋਇਆ ਹੈ"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਤੁਹਾਡੇ ਆਈ.ਟੀ. ਪ੍ਰਸ਼ਾਸਕ ਨੂੰ ਦਿਖਣਯੋਗ ਹੈ"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ਅੱਗੇ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ਸਮਝ ਲਿਆ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਨੂੰ ਰੋਕਿਆ ਗਿਆ ਹੈ। ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੁਹਾਨੂੰ ਸੂਚਨਾਵਾਂ ਨਹੀਂ ਭੇਜ ਸਕਦੀਆਂ, ਤੁਹਾਡੀ ਬੈਟਰੀ ਨਹੀਂ ਵਰਤ ਸਕਦੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕਰ ਸਕਦੀਆਂ"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਅਤੇ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕੋ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ ਇੱਥੇ ਲੱਭੋ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ਬੰਦ ਕਰੋ"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ਇਹ ਕਾਰਵਾਈ ਅਸਫਲ ਹੋਈ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 24fe6f9..6885960 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Praca"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacja nie jest zainstalowana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacja niedostępna"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikacji"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Zainstaluj"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nie proponuj aplikacji"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Przypnij podpowiedź"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalowanie skrótów"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"odczytywanie ustawień i skrótów na ekranie głównym"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem podczas ładowania widżetu"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguracja"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edytuj nazwę"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Folder bez nazwy"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wyłączona"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomienia</item>
@@ -80,12 +79,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Kliknij, by zapisać nową nazwę"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folder zamknięty"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Nazwa folderu zmieniona na <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> elementy"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, liczba elementów: <xliff:g id="SIZE">%2$d</xliff:g> lub więcej"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Style i tapety"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia ekranu głównego"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia strony głównej"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Zezwalaj na obrót ekranu głównego"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
@@ -96,7 +94,7 @@
<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="title_change_settings" msgid="1376365968844349552">"Zmień ustawienia"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaż plakietki z powiadomieniami"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Dodaj ikony aplikacji do ekranu głównego"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikonę do ekranu głównego"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"W przypadku nowych aplikacji"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
@@ -132,19 +130,16 @@
<string name="widget_resized" msgid="9130327887929620">"Szerokość i wysokość widżetu zmieniła się na <xliff:g id="NUMBER_0">%1$s</xliff:g> x <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
<string name="action_deep_shortcut" msgid="2864038805849372848">"Skróty"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Skróty i powiadomienia"</string>
- <string name="action_dismiss_notification" msgid="5909461085055959187">"Zamknij"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zamknij"</string>
+ <string name="action_dismiss_notification" msgid="5909461085055959187">"Odrzuć"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Powiadomienie odrzucone"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobiste"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Służbowe"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil do pracy"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Dane osobiste znajdują się w innym miejscu i są niewidoczne dla aplikacji do pracy"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Dane i aplikacje z profilu do pracy są widoczne dla Twojego administratora IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Dalej"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Wstrzymano profil służbowy"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplikacje do pracy nie mogą wysyłać powiadomień, używać baterii ani mieć dostępu do Twojej lokalizacji"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Wstrzymano profil do pracy. Aplikacje do pracy nie mogą wysyłać powiadomień, używać baterii ani uzyskiwać dostępu do Twojej lokalizacji"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Wstrzymaj aplikacje do pracy i powiadomienia"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Praca"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil służbowy"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Aplikacje do pracy"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Każda aplikacja do pracy ma plakietkę, a o jej bezpieczeństwo dba Twoja organizacja. Aplikacje można przenieść na ekran główny, by były łatwiej dostępne."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Profil zarządzany przez Twoją organizację"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Powiadomienia i aplikacje są wyłączone"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zamknij"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zamknięto"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Niepowodzenie: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index a6b72db..e0d2c74 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -21,8 +21,8 @@
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">"Trabalho"</string>
- <string name="activity_not_found" msgid="8071924732094499514">"A app não está instalada."</string>
- <string name="activity_not_available" msgid="7456344436509528827">"A app não está disponível"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"A aplicação não está instalada."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"A aplicação não está disponível"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicação transferida desativada no Modo de segurança"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no Modo de segurança"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
@@ -36,7 +36,7 @@
<string name="place_automatically" msgid="8064208734425456485">"Adicionar automaticamente"</string>
<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_no_search_results" msgid="3200346862396363786">"Nenhuma aplicação 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="notifications_header" msgid="1404149926117359025">"Notificações"</string>
@@ -50,25 +50,23 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ecrã principal"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da app"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicação"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma app adicionar atalhos sem a intervenção do utilizador."</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler definições e atalhos do Ecrã Principal"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite à app ler as definições e os atalhos no Ecrã Principal."</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite à aplicação ler as definições e os atalhos no Ecrã Principal."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"escrever definições e atalhos do Ecrã principal"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite à app alterar as definições e os atalhos no Ecrã Principal."</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite à aplicação alterar as definições e os atalhos no Ecrã Principal."</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"O <xliff:g id="APP_NAME">%1$s</xliff:g> não tem autorização para efetuar chamadas telefónicas"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma app de sistema e não pode ser desinstalada."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Edite o nome"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma aplicação de sistema e não pode ser desinstalada."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
- <item quantity="other">A app <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
- <item quantity="one">A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação</item>
+ <item quantity="other">A aplicação <xliff:g id="APP_NAME_2">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificações.</item>
+ <item quantity="one">A aplicação <xliff:g id="APP_NAME_0">%1$s</xliff:g> tem <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificação</item>
</plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocar para guardar o nome novo"</string>
<string name="folder_closed" msgid="4100806530910930934">"Pasta fechada"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Nome de pasta alterado para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e imagens de fundo"</string>
@@ -94,13 +91,13 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar os Pontos de notificação, ative as notificações de aplicações para o <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Alterar definições"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar pontos de notificação"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Adic. ícones de apps ao ecrã principal"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Adicionar ícone ao ecrã principal"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicações"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
<string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
- <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta app não está instalada"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A app deste ícone não está instalada. Pode removê-lo ou pesquisar a app e instalá-la manualmente."</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicação não está instalada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"A transferir o <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"A aguardar a instalação do <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Atalhos"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Atalhos e notificações"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificação ignorada"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Os dados pessoais são separados e ocultos das apps de trabalho"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Os dados e apps de trabalho estão visíveis para o seu administrador de TI"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Seguinte"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Perfil de trabalho em pausa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"As apps de trabalho não podem enviar-lhe notificações, utilizar a sua bateria ou aceder à sua localização."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"O perfil de trabalho está em pausa. As apps de trabalho não podem enviar-lhe notificações, utilizar a sua bateria ou aceder à sua localização."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Coloque as apps de trabalho e as notificações em pausa."</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Encontrar as aplicações de trabalho aqui"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Cada aplicação de trabalho apresenta um emblema, pelo que a sua entidade a mantém em segurança. Pode mover as aplicações para o ecrã principal para facilitar o acesso."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gerido pela sua entidade"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"As notificações e as aplicações estão desativadas."</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fechar"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fechado"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Falhou: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 165c8fc..bb4834f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Informações do app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Não sugerir esse app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fixar previsão"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler configurações e atalhos da tela inicial"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um app do sistema e não pode ser desinstalado."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Editar nome"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one">O app <xliff:g id="APP_NAME_2">%1$s</xliff:g>tem <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificação</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Toque para salvar o novo nome"</string>
<string name="folder_closed" msgid="4100806530910930934">"Pasta fechada"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Pasta renomeada para <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> itens"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ou mais itens"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e planos de fundo"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar pontos de notificação, ative as notificações de app para <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Alterar configurações"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Mostrar pontos de notificação"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Adicionar ícones de apps à tela inicial"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Adicionar ícone à tela inicial"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novos apps"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Atalhos"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Atalhos e notificações"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Dispensar"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Fechar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificação dispensada"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoais"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Comerciais"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Os dados pessoais ficam separados e ocultos dos apps de trabalho"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Os dados de apps de trabalho ficam visíveis para seu administrador de TI"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Próxima"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ok"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"O perfil de trabalho está pausado"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Apps de trabalho não podem enviar notificações, usar a bateria nem acessar o local"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"O perfil de trabalho está pausado. Apps de trabalho não podem enviar notificações, usar a bateria nem acessar seu local"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pausar apps e notificações de trabalho"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Localizar apps de trabalho aqui"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Cada app de trabalho tem um selo e é mantido em segurança pela sua organização. Mova os apps para sua tela inicial para facilitar o acesso."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gerenciados pela sua organização"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"As notificações e os apps estão desativados"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Fechar"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Fechado"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Falha: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
deleted file mode 100644
index 0758148..0000000
--- a/res/values-rm/strings.xml
+++ /dev/null
@@ -1,203 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2008 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for application_name (5181331383435256801) -->
- <skip />
- <!-- no translation found for home (7658288663002113681) -->
- <skip />
- <!-- no translation found for uid_name (7820867637514617527) -->
- <skip />
- <string name="folder_name" msgid="7371454440695724752"></string>
- <!-- no translation found for activity_not_found (8071924732094499514) -->
- <skip />
- <!-- no translation found for widgets_tab_label (2921133187116603919) -->
- <skip />
- <!-- no translation found for widget_adder (3201040140710381657) -->
- <skip />
- <!-- no translation found for toggle_weight_watcher (5645299835184636119) -->
- <skip />
- <!-- no translation found for long_press_widget_to_add (7699152356777458215) -->
- <skip />
- <!-- no translation found for market (2619650989819296998) -->
- <skip />
- <!-- no translation found for widget_dims_format (2370757736025621599) -->
- <skip />
- <!-- no translation found for external_drop_widget_error (3165821058322217155) -->
- <skip />
- <!-- no translation found for external_drop_widget_pick_title (3486317258037690630) -->
- <skip />
- <!-- no translation found for rename_folder_label (3727762225964550653) -->
- <skip />
- <!-- no translation found for rename_folder_title (3771389277707820891) -->
- <skip />
- <!-- no translation found for rename_action (5559600076028658757) -->
- <skip />
- <!-- no translation found for cancel_action (7009134900002915310) -->
- <skip />
- <!-- no translation found for menu_item_add_item (1264911265836810421) -->
- <skip />
- <!-- no translation found for group_applications (3797214114206693605) -->
- <skip />
- <!-- no translation found for group_shortcuts (6012256992764410535) -->
- <skip />
- <!-- no translation found for group_widgets (1569030723286851002) -->
- <skip />
- <!-- no translation found for completely_out_of_space (6106288382070760318) -->
- <skip />
- <!-- no translation found for out_of_space (4691004494942118364) -->
- <skip />
- <!-- no translation found for hotseat_out_of_space (7448809638125333693) -->
- <skip />
- <!-- no translation found for invalid_hotseat_item (5779907847267573691) -->
- <skip />
- <!-- no translation found for shortcut_installed (1701742129426969556) -->
- <skip />
- <!-- no translation found for shortcut_uninstalled (8176767991305701821) -->
- <skip />
- <!-- no translation found for shortcut_duplicate (9167217446062498127) -->
- <skip />
- <!-- no translation found for title_select_shortcut (6680642571148153868) -->
- <skip />
- <!-- no translation found for title_select_application (3280812711670683644) -->
- <skip />
- <!-- no translation found for all_apps_button_label (9110807029020582876) -->
- <skip />
- <!-- no translation found for all_apps_home_button_label (252062713717058851) -->
- <skip />
- <!-- no translation found for delete_zone_label_workspace (4009607676751398685) -->
- <skip />
- <!-- no translation found for delete_zone_label_all_apps (8083826390278958980) -->
- <skip />
- <!-- no translation found for delete_target_label (1822697352535677073) -->
- <skip />
- <!-- no translation found for delete_target_uninstall_label (5100785476250872595) -->
- <skip />
- <!-- no translation found for info_target_label (8053346143994679532) -->
- <skip />
- <!-- no translation found for accessibility_search_button (1628520399424565142) -->
- <skip />
- <!-- no translation found for accessibility_voice_search_button (4637324840434406584) -->
- <skip />
- <!-- no translation found for accessibility_all_apps_button (2603132375383800483) -->
- <skip />
- <!-- no translation found for accessibility_delete_button (6466114477993744621) -->
- <skip />
- <!-- no translation found for delete_zone_label_all_apps_system_app (449755632749610895) -->
- <skip />
- <!-- no translation found for cab_menu_delete_app (7435191475867183689) -->
- <skip />
- <!-- no translation found for cab_menu_app_info (8593722221450362342) -->
- <skip />
- <!-- no translation found for cab_app_selection_text (374688303047985416) -->
- <skip />
- <!-- no translation found for cab_widget_selection_text (1833458597831541241) -->
- <skip />
- <!-- no translation found for cab_folder_selection_text (7999992513806132118) -->
- <skip />
- <!-- no translation found for cab_shortcut_selection_text (2103811025667946450) -->
- <skip />
- <!-- no translation found for permlab_install_shortcut (5632423390354674437) -->
- <skip />
- <!-- no translation found for permdesc_install_shortcut (923466509822011139) -->
- <skip />
- <!-- no translation found for permlab_uninstall_shortcut (864595034498083837) -->
- <skip />
- <!-- no translation found for permdesc_uninstall_shortcut (5134129545001836849) -->
- <skip />
- <!-- no translation found for permlab_read_settings (1941457408239617576) -->
- <skip />
- <!-- no translation found for permdesc_read_settings (5833423719057558387) -->
- <skip />
- <!-- no translation found for permlab_write_settings (3574213698004620587) -->
- <skip />
- <!-- no translation found for permdesc_write_settings (5440712911516509985) -->
- <skip />
- <!-- no translation found for gadget_error_text (6081085226050792095) -->
- <skip />
- <!-- no translation found for uninstall_system_app_text (4172046090762920660) -->
- <skip />
- <!-- no translation found for dream_name (1530253749244328964) -->
- <skip />
- <!-- no translation found for folder_hint_text (6617836969016293992) -->
- <skip />
- <!-- no translation found for workspace_description_format (2950174241104043327) -->
- <skip />
- <!-- no translation found for default_scroll_format (7475544710230993317) -->
- <skip />
- <!-- no translation found for workspace_scroll_format (8458889198184077399) -->
- <skip />
- <!-- no translation found for apps_customize_apps_scroll_format (370005296147130238) -->
- <skip />
- <!-- no translation found for apps_customize_widgets_scroll_format (3106209519974971521) -->
- <skip />
- <!-- no translation found for first_run_cling_title (2459738000155917941) -->
- <skip />
- <!-- no translation found for first_run_cling_description (6447072552696253358) -->
- <skip />
- <!-- no translation found for first_run_cling_create_screens_hint (6950729526680114157) -->
- <skip />
- <!-- no translation found for migration_cling_title (9181776667882933767) -->
- <skip />
- <!-- no translation found for migration_cling_description (2752413805582227644) -->
- <skip />
- <!-- no translation found for migration_cling_copy_apps (946331230090919440) -->
- <skip />
- <!-- no translation found for migration_cling_use_default (2626475813981258626) -->
- <skip />
- <!-- no translation found for workspace_cling_title (5626202359865825661) -->
- <skip />
- <!-- no translation found for workspace_cling_move_item (528201129978005352) -->
- <skip />
- <!-- no translation found for folder_cling_title (3894908818693254164) -->
- <skip />
- <!-- no translation found for folder_cling_create_folder (6158215559475836131) -->
- <skip />
- <!-- no translation found for cling_dismiss (8962359497601507581) -->
- <skip />
- <!-- no translation found for folder_opened (94695026776264709) -->
- <skip />
- <!-- no translation found for folder_tap_to_close (1884479294466410023) -->
- <skip />
- <!-- no translation found for folder_tap_to_rename (9191075570492871147) -->
- <skip />
- <!-- no translation found for folder_closed (4100806530910930934) -->
- <skip />
- <!-- no translation found for folder_renamed (1794088362165669656) -->
- <skip />
- <!-- no translation found for folder_name_format (6629239338071103179) -->
- <skip />
- <!-- no translation found for widget_button_text (2880537293434387943) -->
- <skip />
- <!-- no translation found for wallpaper_button_text (8404103075899945851) -->
- <skip />
- <!-- no translation found for settings_button_text (8119458837558863227) -->
- <skip />
- <!-- no translation found for package_state_enqueued (6227252464303085641) -->
- <skip />
- <!-- no translation found for package_state_downloading (4088770468458724721) -->
- <skip />
- <!-- no translation found for package_state_installing (7588193972189849870) -->
- <skip />
- <!-- no translation found for package_state_unknown (7592128424511031410) -->
- <skip />
- <!-- no translation found for package_state_error (7672093962724223588) -->
- <skip />
-</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index fb8e361..f8a61f1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</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="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="1941457408239617576">"citește setări și comenzi rapide pentru ecranul de pornire"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problemă la încărcarea widgetului"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configurați"</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="6617836969016293992">"Dosar fără nume"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"S-a dezactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificări</item>
@@ -79,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Atingeți 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>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> sau mai multe elemente"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiluri și imagini de fundal"</string>
@@ -95,7 +92,7 @@
<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="auto_add_shortcuts_label" msgid="3698776050751790653">"Adaugă pictograme de aplicații pe ecran"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Adaugă pictograme în 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>
@@ -132,18 +129,15 @@
<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="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>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil de serviciu"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Datele cu caracter personal sunt separate și ascunse de aplicațiile pentru lucru"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Aplicațiile și datele pentru lucru sunt vizibile pentru administratorul IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Înainte"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Profilul de serviciu este întrerupt"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"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="7553586952985486433">"Profilul de serviciu este întrerupt. Aplicațiile pentru lucru nu pot să vă trimită notificări, să folosească bateria sau să vă acceseze locația"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Întrerupeți aplicațiile pentru lucru și notificările"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Găsiți aplicații de serviciu aici"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Fiecare aplicație de serviciu are o insignă și este păstrată în siguranță de organizația dvs. Mutați aplicațiile pe ecranul de pornire pentru acces mai ușor."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gestionat de organizația dvs."</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notificările și aplicațiile sunt dezactivate"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Închideți"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Închis"</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 4d8ba88..23b00d0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"Доступ к настройкам и ярлыкам главного экрана"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Не удалось загрузить виджет"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Настройка"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Это системное приложение, его нельзя удалить."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Измените название"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Папка без названия"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> отключено"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомление</item>
@@ -80,15 +79,14 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Нажмите, чтобы подтвердить переименование"</string>
<string name="folder_closed" msgid="4100806530910930934">"Папка закрыта"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Папка переименована в \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g>)"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\" (объектов: <xliff:g id="SIZE">%2$d</xliff:g> или больше)"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стили и обои"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Главный экран"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Разрешить поворачивать главный экран"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"При повороте телефона"</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>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Отключены"</string>
@@ -96,7 +94,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Добавлять значки на главный экран"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавлять значки"</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>
@@ -133,18 +131,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Рабочий профиль"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Личные данные скрыты от рабочих приложений и недоступны им."</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Рабочие приложения и данные видны системному администратору."</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Далее"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"ОК"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Действие рабочего профиля приостановлено."</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к вашему местоположению."</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Рабочий профиль приостановлен. Рабочие приложения не могут отправлять уведомления, расходовать заряд батареи и получать доступ к данным о вашем местоположении."</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Приостановить рабочие приложения и уведомления"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Приложения для работы"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Рабочие приложения отмечены специальным значком. Их безопасность обеспечивает ваша организация. Для удобства перенесите эти приложения на главный экран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Управляется вашей организацией"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Уведомления и приложения отключены."</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрыть"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрыта"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не удалось выполнить действие (<xliff:g id="WHAT">%1$s</xliff:g>)."</string>
</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index bfc6b7a..ef99a59 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ගැටලු පූරණ විජට් එක"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ස්ථාපනය කරන්න"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"නම සංස්කරණය කරන්න"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"නම් නොකළ ෆෝල්ඩරය"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> අබල කෙරිණි"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"යළි නම් කිරීම සුරැකීමට තට්ටු කරන්න"</string>
<string name="folder_closed" msgid="4100806530910930934">"ෆෝල්ඩරය වසා ඇත"</string>
<string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ෆෝල්ඩර: <xliff:g id="NAME">%1$s</xliff:g>, අයිතම <xliff:g id="SIZE">%2$d</xliff:g>ක් හෝ වැඩි ගණනක්"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"විලාස සහ වෝල්පේපර"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"මුල් තිරයට යෙදුම් අයිකන එක් කරන්න"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"කාර්යාල පැතිකඩ"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"පෞද්ගලික දත්ත කාර්යාල යෙදුම්වලින් වෙන් කර සඟවා ඇත"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"කාර්යාල යෙදුම් & දත්ත ඔබගේ IT පරිපාලකට දෘශ්යමාන වේ"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ඊළඟ"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"තේරුණා"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"කාර්යාල පැතිකඩ විරාම කර ඇත"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"කාර්යාල යෙදුම්වලට ඔබට දැනුම් දීම් එවීමට, ඔබගේ බැටරිය භාවිත කිරීමට හෝ ඔබගේ ස්ථානයට ප්රවේශ විය නොහැකිය"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"කාර්යාල පැතිකඩ විරාම කර ඇත. කාර්යාල යෙදුම්වලට ඔබට දැනුම් දීම් එවීමට, ඔබගේ බැටරිය භාවිත කිරීමට හෝ ඔබගේ ස්ථානයට ප්රවේශ විය නොහැකිය"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"කාර්යාල යෙදුම් සහ දැනුම් දීම් විරාම කරන්න"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"මෙහි කාර්යාල යෙදුම් සොයා ගන්න"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"සෑම කාර්යාල යෙදුමකම ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය මගින් සුරක්ෂිතව තබා ගනී. වඩාත් පහසු ප්රවේශයකට යෙදුම් ඔබේ මුල් පිටු තිරය වෙත ගෙන යන්න."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ඔබේ සංවිධානය විසින් කළමනාකරණය කරනු ලැබේ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"දැනුම්දීම් සහ යෙදුම් ක්රියාවිරහිතයි"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"වසන්න"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"වසා ඇත"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"අසාර්ථකයි: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e3b7894..5bbf7c3 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikácii"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Inštalovať"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Nenavrhovať aplikáciu"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Pripnúť predpoveď"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalovať odkazy"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"čítanie nastavení a odkazov plochy"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problém s načítaním miniaplikácií"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Nastavenie"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikácia a nedá sa odinštalovať."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Úprava názvu"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Nepomenovaný priečinok"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je deaktivovaná"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
@@ -80,8 +78,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Nový názov uložíte klepnutím"</string>
<string name="folder_closed" msgid="4100806530910930934">"Priečinok je uzavretý"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Priečinok bol premenovaný na <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> položky"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> alebo viac položiek"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Štýly a tapety"</string>
@@ -96,7 +93,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Ak chcete, aby sa zobrazovali bodky upozornení, zapnite upozornenia aplikácie <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Zmeniť nastavenia"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Zobrazovať bodky upozornení"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Pridať ikony aplikácií na plochu"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Pridať ikonu 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>
@@ -133,18 +130,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Skratky"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Odkazy a upozornenia"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Zavrieť"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zavrieť"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Upozornenie bolo zavreté"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobné"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovné"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovný profil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osobné údaje sú oddelené a sú pred pracovnými aplikáciami skryté"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Pracovné aplikácie a údaje môže vidieť váš správca IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Ďalej"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Dobre"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Pracovný profil je pozastavený"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Pracovný profil je pozastavený. Pracovné aplikácie nemôžu posielať upozornenia, používať batériu ani polohu"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pozastavte pracovné aplikácie a upozornenia"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Tu nájdete pracovné aplikácie"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Všetky pracovné aplikácie majú štítok a sú bezpečne uchovávané vašou organizáciou. Ak chcete mať k aplikáciám ľahší prístup, presuňte ich na plochu."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Spravované vašou organizáciou"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Upozornenia a aplikácie sú vypnuté"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zavrieť"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zavreté"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Zlyhalo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 7ec3652..044a4b4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Služba"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija ni nameščena."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija ni na voljo"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odmesti"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Podatki o aplikaciji"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Namesti"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ne predlagaj aplikacij"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Predvidevanje pripenjanja"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Težava pri nalaganju pripomočka"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Nastavitev"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To je sistemska aplikacija in je ni mogoče odstraniti."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Urejanje imena"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočena"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestilo</item>
@@ -80,12 +79,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Dotaknite se, da shranite preimenovanje"</string>
<string name="folder_closed" msgid="4100806530910930934">"Mapa je zaprta"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana v <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, št. elementov: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ali več elementov"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Slogi in ozadja"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Domače nastavitve"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Nastavitve začetnega zaslona"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Omogočanje sukanja začetnega zaslona"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Ko se telefon zasuka"</string>
@@ -96,7 +94,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz obvestilnih pik vklopite obvestila aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Spremeni nastavitve"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Pokaži obvestilne pike"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Dodaj ikone aplikacij na začetni zaslon"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodaj ikono na začetni zaslon"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
@@ -133,18 +131,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Bližnjice"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Bližnjice in obvestila"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Opusti"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Zapri"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obvestilo je bilo opuščeno"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Osebno"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Delo"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Služba"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Delovni profil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Osebni podatki so ločeni in skriti pred delovnimi aplikacijami"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Delovne aplikacije in službeni podatki so vidni skrbniku za IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Naprej"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Razumem"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Delovni profil je začasno zaustavljen"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Delovne aplikacije ne smejo pošiljati obvestil, porabljati energije baterije ali dostopati do lokacije"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Delovni profil je začasno zaustavljen. Delovne aplikacije ne smejo pošiljati obvestil, porabljati energije baterije ali dostopati do lokacije"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Začasna zaustavitev delovnih aplikacij in obvestil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Tukaj poiščite delovne aplikacije"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Vsaka delovna aplikacija ima značko. Za varnost teh aplikacij skrbi vaša organizacija. Za preprostejši dostop premaknite aplikacije na začetni zaslon."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Upravlja vaša organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Obvestila in aplikacije – izklopljeno"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Zapri"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Zaprto"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Ni uspelo: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index c021061..7f2567c 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -50,10 +50,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Faqja kryesore"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Hiqe"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Info mbi aplikacionin"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacion mbi aplikacionin"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalo"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Mos sugjero aplikacion"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Gozhdo parashikimin"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalo shkurtore"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problem në ngarkimin e miniaplikacionit"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguro"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Redakto emrin"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Dosje e paemërtuar"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> u çaktivizua"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> njoftime</item>
@@ -78,12 +76,11 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Trokit për të ruajtur riemërtimin"</string>
<string name="folder_closed" msgid="4100806530910930934">"Dosja u mbyll"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Dosja u riemërtua në <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> artikuj"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ose më shumë artikuj"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilet dhe imazhet e sfondit"</string>
- <string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e ekranit bazë"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit kryesor"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Për të shfaqur \"Pikat e njoftimeve\", aktivizo njoftimet e aplikacionit për <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Ndrysho cilësimet"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Shfaq pikat e njoftimeve"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Shto ikona aplikacionesh në ekranin bazë"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Shto ikonë në ekranin bazë"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Për aplikacionet e reja"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Shkurtoret"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Shkurtoret dhe njoftimet"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Hiqe"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Mbyll"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Njoftimi u hoq"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Punë"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profili i punës"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Të dhënat personale janë të ndara dhe të fshehura nga aplikacionet e punës"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Aplikacionet e punës dhe të dhënat janë të dukshme për administratorin e teknologjisë së informacionit."</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Para"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"E kuptova"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Profili i punës është në pauzë"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Aplikacionet e punës nuk mund të të dërgojnë njoftime, të përdorin baterinë tënde apo të kenë qasje në vendndodhjen tënde"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Profili i punës është në pauzë. Aplikacionet e punës nuk mund të të dërgojnë njoftime, të përdorin baterinë tënde apo të kenë qasje në vendndodhjen tënde"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Vendos në pauzë aplikacionet e punës dhe njoftimet"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Gjej këtu aplikacionet e punës"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Secili aplikacion pune ka një distinktiv dhe mbahet i sigurt nga organizata jote. Zhvendosi aplikacionet e punës në ekranin tënd kryesor për qasje më të lehtë."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Menaxhohet nga organizata jote"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Njoftimet dhe aplikacionet janë joaktive"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Mbyll"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Mbyllur"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Dështoi: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index a197f9e..4e52592 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Апликација није инсталирана."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Апликација није доступна"</string>
@@ -50,10 +51,8 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна"</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>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"читање подешавања и пречица на почетном екрану"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Проблем при учитавању виџета"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Подешавање"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ово је системска апликација и не може да се деинсталира."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Измените назив"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Неименовани директоријум"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућена"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештење</item>
@@ -79,8 +78,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Додирните да бисте сачували преименовање"</string>
<string name="folder_closed" msgid="4100806530910930934">"Директоријум је затворен"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Директоријум је преименован у <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ставке"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> или више ставки"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и позадине"</string>
@@ -95,7 +93,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Додај иконе апликација на почетни екран"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икону на почетни екран"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нове апликације"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
@@ -132,18 +130,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Пословни профил"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Лични подаци су одвојени и сакривени од апликација за посао"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ИТ администратор види пословне апликације и податке"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Даље"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Важи"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Пословни профил је паузиран"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Пословни профил је паузиран. Пословне апликације не могу да вам шаљу обавештења, користе батерију нити приступају локацији"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Паузирајте пословне апликације и обавештења"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Профил за Work"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Пронађите пословне апликације овде"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Свака пословна апликација има значку и штити је ваша организација. Преместите апликације на почетни екран да бисте им лакше приступали."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Овим управља организација"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Обавештења и апликације су искључени"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Затвори"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Затворено"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Није успело: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index b8beb9b..e7400a6 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Arbete"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen är inte installerad."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen är inte tillgänglig"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Installera"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Föreslå inte app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Fäst förslag"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"läsa inställningar och genvägar för startsidan"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Det gick inte att läsa in widgeten"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Det här är en systemapp som inte kan avinstalleras."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Redigera namn"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Namnlös mapp"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverats"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> aviseringar</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryck för att spara namnändringen"</string>
<string name="folder_closed" msgid="4100806530910930934">"Mappen är stängd"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Mappen har bytt namn till <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> objekt"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> eller fler objekt"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Format och bakgrunder"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Aktivera appaviseringar för <xliff:g id="NAME">%1$s</xliff:g> om du vill att aviseringsprickar ska visas"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Ändra inställningar"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Visa aviseringsprickar"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Lägg till appikoner på startskärmen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lägg till ikonen på startskärmen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"För nya appar"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Genvägar"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Genvägar och aviseringar"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorera"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Stäng"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Aviseringen togs bort"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbete"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Privat data lagras för sig och är inte synlig för jobbapparna"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Jobbappar och jobbdata är synliga för IT-administratören"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Nästa"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Jobbprofilen är pausad"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Jobbappar kan inte skicka aviseringar, använda batteriet eller komma åt din plats"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Jobbprofilen är pausad. Jobbappar kan inte skicka aviseringar, använda batteriet eller komma åt din plats"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Pausa jobbappar och jobbaviseringar"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Här hittar du jobbappar"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Alla jobbappar har ett märke och organisationen ser till att de är skyddade. Flytta apparna till startskärmen så kommer du åt dem lättare."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Hanteras av organisationen"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Aviseringar och appar är inaktiverade"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Stäng"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Stängd"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Misslyckades: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index b91ed0a..445e382 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sakinua"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Maelezo ya programu"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Sakinisha"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Isipendekeze programu"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Bandika Utabiri"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"soma mipangilio ya Mwanzo na njia za mkato"</string>
@@ -64,22 +62,23 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Tatizo la kupakia wijeti"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Sanidi"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Badilisha Jina"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Folda isiyo na jina"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezimwa"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ina arifa <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
<item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ina arifa <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
</plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Ukurasa%1$d wa %2$d"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"Skrini ya mwanzo %1$d ya %2$d"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for workspace_scroll_format (8458889198184077399) -->
+ <skip />
<string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
<string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Gusa ili ufunge folda"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Gusa ili ubadilishe jina"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folda imefungwa"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folda: <xliff:g id="NAME">%1$s</xliff:g>, vipengee <xliff:g id="SIZE">%2$d</xliff:g> au zaidi"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mitindo na mandhari"</string>
@@ -94,7 +93,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Ili kuonyesha Vitone vya Arifa, washa kipengele cha arifa za programu katika <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Badilisha mipangilio"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Onyesha vitone vya arifa"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Weka aikoni za programu kwenye Skrini ya kwanza"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ongeza aikoni kwenye Skrini ya kwanza"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Kwa ajili ya programu mpya"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
@@ -131,18 +130,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Njia za mkato"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Arifa na njia za mkato"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ondoa"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Funga"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Arifa imeondolewa"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Binafsi"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Wasifu wa kazini"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Data binafsi iko kando na haionyeshwi kwenye programu za kazini"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Programu na data ya kazini huonekana kwa msimamizi wako wa TEHAMA"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Endelea"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Nimeelewa"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Wasifu wa kazini umesimamishwa"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Programu za kazini haziwezi kukutumia arifa, kutumia betri yako au kufikia maelezo ya mahali ulipo"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Wasifu wa kazini umesimamishwa. Programu za kazini haziwezi kukutumia arifa, kutumia betri yako au kufikia maelezo ya mahali ulipo"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Simamisha arifa na programu za kazini"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pata programu za kazi hapa"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Kila programu ya kazi ina beji na hulindwa na shirika lako. Hamishia programu kwenye skrini yako ya kwanza ili uzifikie kwa urahisi."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Inasimamiwa na shirika lako"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Vipenge vya arifa na programu vimezimwa"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Funga"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Imefungwa"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Hitilafu: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-sw720dp/config.xml b/res/values-sw720dp/config.xml
index 1f401c4..ec07591 100644
--- a/res/values-sw720dp/config.xml
+++ b/res/values-sw720dp/config.xml
@@ -1,5 +1,4 @@
<resources>
- <bool name="config_largeHeap">true</bool>
<!-- All Apps & Widgets -->
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index f322e9f..c1e6eca 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -18,16 +18,6 @@
-->
<resources>
-
- <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
- <item name="android:windowBackground">@android:color/transparent</item>
- <item name="android:colorBackgroundCacheHint">@null</item>
- <item name="android:windowShowWallpaper">true</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:windowActionModeOverlay">true</item>
- <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
- </style>
-
<!-- Workspace -->
<style name="DropTargetButton" parent="DropTargetButtonBase">
<item name="android:paddingLeft">60dp</item>
@@ -36,5 +26,4 @@
<item name="android:shadowDy">0.0</item>
<item name="android:shadowRadius">2.0</item>
</style>
-
</resources>
\ No newline at end of file
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 2740c2a..6e933a1 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -27,21 +27,21 @@
<string name="safemode_widget_error" msgid="4863470563535682004">"பாதுகாப்புப் பயன்முறையில் விட்ஜெட்கள் முடக்கப்பட்டுள்ளன"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"ஷார்ட்கட் இல்லை"</string>
<string name="home_screen" msgid="806512411299847073">"முகப்புத் திரை"</string>
- <string name="custom_actions" msgid="3747508247759093328">"பிரத்தியேக செயல்கள்"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"தனிப்பயன் செயல்கள்"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்."</string>
- <string name="long_accessible_way_to_add" msgid="4289502106628154155">"விட்ஜெட்டைத் தேர்ந்தெடுக்க இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேக செயல்களைப் பயன்படுத்தவும்."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"விட்ஜெட்டைத் தேர்ந்தெடுக்க இருமுறை தட்டிப் பிடிக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
<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="add_item_request_drag_hint" msgid="5899764264480397019">"நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்"</string>
<string name="place_automatically" msgid="8064208734425456485">"தானாகவே சேர்"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"பயன்பாடுகளில் தேடுக"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"ஆப்ஸை ஏற்றுகிறது…"</string>
- <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் ஆப்ஸ் இல்லை"</string>
+ <string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
<string name="label_application" msgid="8531721983832654978">"ஆப்ஸ்"</string>
<string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
<string name="long_press_shortcut_to_add" msgid="4524750017792716791">"ஷார்ட்கட்டைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்."</string>
- <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) பிரத்தியேக செயல்களைப் பயன்படுத்தவும்."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"ஷார்ட்கட்டைச் சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் (அ) தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
<string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"ஆப்ஸின் பட்டியல்"</string>
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"விட்ஜெட்டை ஏற்றுவதில் சிக்கல்"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"அமைவு"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு ஆப்ஸ் என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"பெயரைத் திருத்துதல்"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"பெயரிடப்படாத கோப்புறை"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> முடக்கப்பட்டது"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ஆப்ஸில் <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> அறிவிப்புகள் வந்துள்ளன</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"மாற்றிய பெயரைச் சேமிக்க, தட்டவும்"</string>
<string name="folder_closed" msgid="4100806530910930934">"கோப்புறை மூடப்பட்டது"</string>
<string name="folder_renamed" msgid="1794088362165669656">"கோப்புறை <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> கோப்புகள்"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> அல்லது அதற்கு அதிகமான கோப்புகள்"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"விட்ஜெட்கள்"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ஸ்டைல்கள் & வால்பேப்பர்கள்"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"முகப்புத் திரையில் ஆப்ஸ் ஐகான்களைச் சேர்"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"பணிக் கணக்கு"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"தனிப்பட்ட தரவு பணி ஆப்ஸுடன் சேர்ந்ததல்ல என்பதோடு பணி ஆப்ஸில் இருந்து அவை மறைக்கப்பட்டிருக்கும்"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"பணி ஆப்ஸையும் தரவையும் உங்கள் IT நிர்வாகியால் பார்க்க முடியும்"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"அடுத்து"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"முடிந்தது"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"பணிக் கணக்கு இடைநிறுத்தப்பட்டது"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"பணி ஆப்ஸால் அறிவிப்புகளை அனுப்பவோ பேட்டரியைப் பயன்படுத்தவோ இருப்பிடத்தை அணுகவோ முடியாது"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"பணிக் கணக்கு இடைநிறுத்தப்பட்டது. பணி ஆப்ஸால் அறிவிப்புகளை அனுப்பவோ பேட்டரியைப் பயன்படுத்தவோ இருப்பிடத்தை அணுகவோ முடியாது"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"பணி தொடர்பான ஆப்ஸையும் அறிவிப்புகளையும் இடைநிறுத்தும்"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"பணி ஆப்ஸை இங்கு காணலாம்"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ஒவ்வொரு பணிப் பயன்பாடும் ஒரு பேட்ஜைக் கொண்டிருக்கும். இவை, ஆப்ஸ் உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன என்பதைக் குறிக்கின்றன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"உங்கள் நிறுவனம் நிர்வகிக்கிறது"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ஆப்ஸும் அறிவிப்புகளும் ஆஃப் செய்யப்பட்டுள்ளன"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"மூடுக"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"மூடப்பட்டது"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"தோல்வி: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 3f6f9a6..1f8d3b8 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడం"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"విడ్జెట్ను లోడ్ చేయడంలో సమస్య"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"సెటప్ చేయి"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ యాప్ మరియు దీన్ని అన్ఇన్స్టాల్ చేయడం సాధ్యపడదు."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"పేరును ఎడిట్ చేయండి"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"పేరు లేని ఫోల్డర్"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> నిలిపివేయబడింది"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, నుంచి <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> నోటిఫికేషన్లు ఉన్నాయి</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"పేరు మార్పును సేవ్ చేయడానికి నొక్కండి"</string>
<string name="folder_closed" msgid="4100806530910930934">"ఫోల్డర్ మూసివేయబడింది"</string>
<string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> ఐటెమ్లు"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> లేదా అంతకంటే ఎక్కువ ఐటెమ్లు"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్లు"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్పేపర్లు"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"స్టయిల్స్ & వాల్పేపర్లు"</string>
@@ -94,11 +91,11 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"యాప్ చిహ్నాలను హోమ్ స్క్రీన్కు జోడించు"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"హోమ్ స్క్రీన్కి చిహ్నాన్ని జోడించు"</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_search" msgid="891119232568284442">"సెర్చ్"</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="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
@@ -131,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"కార్యాలయ ప్రొఫైల్"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"వ్యక్తిగత డేటా అనేది విడిగా & ఆఫీస్ యాప్లకు యాక్సెస్ లేకుండా, దాచబడి ఉంటుంది"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ఆఫీస్ యాప్లు & డేటా మీ IT అడ్మిన్కు కనిపిస్తాయి"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"తర్వాత"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"అర్థమైంది"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"ఆఫీస్ ప్రొఫైల్ పాజ్ చేయబడింది"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"మీకు నోటిఫికేషన్లు పంపడం, మీ బ్యాటరీని ఉపయోగించడం, లేదా మీ లొకేషన్ను యాక్సెస్ చేయడం \'వర్క్ యాప్\'లకు సాధ్యపడదు"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ఆఫీస్ ప్రొఫైల్ పాజ్ చేయబడింది. \'ఆఫీస్ యాప్\'లు మీకు నోటిఫికేషన్లు పంపడం, మీ బ్యాటరీని ఉపయోగించడం, లేదా మీ లొకేషన్ను యాక్సెస్ చేయడం చేయలేవు"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ఆఫీస్ యాప్లు, నోటిఫికేషన్లను పాజ్ చేయండి"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"కార్యాలయ యాప్లను ఇక్కడ కనుగొనండి"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ప్రతి కార్యాలయ యాప్కు బ్యాడ్జ్ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్లను మీ హోమ్ స్క్రీన్కి తరలించండి."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"మీ సంస్థ ద్వారా నిర్వహించబడతాయి"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"నోటిఫికేషన్లు మరియు యాప్లు ఆఫ్ చేయబడ్డాయి"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"మూసివేయి"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"మూసివేయబడింది"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"విఫలమైంది: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 6a9a485..5ab9b86 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -52,8 +52,6 @@
<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="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="1941457408239617576">"อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"มีปัญหาขณะโหลดวิดเจ็ต"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ตั้งค่า"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"แก้ไขชื่อ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"โฟลเดอร์ที่ไม่มีชื่อ"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"ปิดใช้ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> รายการ</item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"แตะเพื่อบันทึกการเปลี่ยนชื่อ"</string>
<string name="folder_closed" msgid="4100806530910930934">"โฟลเดอร์ปิดอยู่"</string>
<string name="folder_renamed" msgid="1794088362165669656">"เปลี่ยนชื่อโฟลเดอร์เป็น <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>, อย่างน้อย <xliff:g id="SIZE">%2$d</xliff:g> รายการ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"รูปแบบและวอลเปเปอร์"</string>
@@ -94,7 +91,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"เพิ่มไอคอนแอปในหน้าจอหลัก"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"เพิ่มไอคอนในหน้าจอหลัก"</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,18 +128,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"โปรไฟล์งาน"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ข้อมูลส่วนตัวจะอยู่แยกต่างหากและซ่อนจากแอปงาน"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ผู้ดูแลระบบไอทีจะดูแอปและข้อมูลงานได้"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"ถัดไป"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"รับทราบ"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"โปรไฟล์งานหยุดชั่วคราว"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"โปรไฟล์งานปิดชั่วคราว แอปงานจะส่งการแจ้งเตือน ใช้แบตเตอรี่ หรือเข้าถึงตำแหน่งของคุณไม่ได้"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"หยุดแอปงานและการแจ้งเตือนไว้ชั่วคราว"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"หาแอปงานที่นี่"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"แอปงานแต่ละแอปมีป้ายและได้รับการรักษาความปลอดภัยจากองค์กรของคุณ ย้ายแอปไปยังหน้าจอหลักเพื่อให้เข้าถึงได้ง่ายขึ้น"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"จัดการโดยองค์กร"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ปิดการแจ้งเตือนและแอปอยู่"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"ปิด"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"ปิด"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ไม่สำเร็จ: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index ac78aeb..0df94c7 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabaho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Hindi naka-install ang app."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Hindi available ang app"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Impormasyon ng app"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"I-install"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Huwag magmungkahi ng app"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"I-pin ang Hula"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"basahin ang mga setting at shortcut ng Home"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Problema sa pag-load ng widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"I-setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Isa itong app ng system at hindi maaaring i-uninstall."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"I-edit ang Pangalan"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Walang Pangalang Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Naka-disable ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> (na) notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"I-tap upang i-save ang bagong pangalan"</string>
<string name="folder_closed" msgid="4100806530910930934">"Nakasara ang folder"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Pinalitan ang pangalan ng folder ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> (na) item"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Folder: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> o higit pang item"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mga istilo at wallpaper"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Upang ipakita ang Mga Notification Dot, i-on ang mga notification ng app para sa <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Baguhin ang mga setting"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Ipakita ang mga notification dot"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Magdagdag ng mga app icon sa Home screen"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Idagdag ang icon sa Home screen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para sa mga bagong app"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Mga Shortcut"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Mga shortcut at notification"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"I-dismiss"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Isara"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Na-dismiss ang notification"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabaho"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profile sa trabaho"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Hiwalay at nakatago ang personal na data sa mga app para sa trabaho"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Makikita ng iyong IT admin ang mga app at data para sa trabaho"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Susunod"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Naka-pause ang profile sa trabaho"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Ang mga app para sa trabaho ay hindi makakapagpadala sa iyo ng mga notification, makakagamit ng battery mo, o makaka-access ng iyong lokasyon"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Naka-pause ang profile sa trabaho. Hindi makakapagpadala sa iyo ng mga notification, makakagamit ng battery mo, o makaka-access ng iyong lokasyon ang mga app para sa trabaho"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"I-pause ang mga app at notification para sa trabaho"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Maghanap ng mga app para sa trabaho rito"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Ang bawat app para sa trabaho ay may badge at pinapanatiling ligtas ng iyong organisasyon. Ilipat ang mga app sa iyong Home screen para mas madaling ma-access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Pinamamahalaan ng iyong organisasyon"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Naka-off ang mga notification at app"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Isara"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Nakasara"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Hindi nagawa: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index c84e670..56f8447 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Uygulama yüklü değil."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Uygulama kullanılamıyor"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Yüklemeyi kaldır"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Uygulama bilgileri"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Yükle"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Uygulama önerme"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Tahmini Sabitle"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Ana ekran ayarlarını ve kısayollarını oku"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Widget yüklenirken sorun oluştu"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Kurulum"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Adı Düzenle"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Klasör"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> devre dışı"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> uygulamasının <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> bildirimi var</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Yeni adın kaydedilmesi için dokunun"</string>
<string name="folder_closed" msgid="4100806530910930934">"Klasör kapatıldı"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Klasörün adı <xliff:g id="NAME">%1$s</xliff:g> olarak değiştirildi"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> öğe"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> veya daha fazla öğe"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiller ve duvar kağıtları"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Bildirim Noktaları\'nı göstermek için <xliff:g id="NAME">%1$s</xliff:g> uygulamasının bildirimlerini açın"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Ayarları değiştir"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Bildirim noktalarını göster"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Uygulama simgelerini Ana ekrana ekleyin"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ana ekrana simge ekle"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni uygulamalar için"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Kısayollar"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Kısayollar ve bildirimler"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Kapat"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Kapat"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Bildirim kapatıldı"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Kişisel"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Kişisel veriler ayrı olup iş uygulamalarından gizlenir"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"BT yöneticiniz iş uygulamalarını ve verilerini görebilir"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"İleri"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Anladım"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"İş profili duraklatıldı"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"İş uygulamaları size bildirim gönderemez, pilinizi kullanamaz veya konum bilginize erişemez"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"İş profili duraklatıldı. İş uygulamaları size bildirim gönderemez, pilinizi kullanamaz veya konum bilginize erişemez"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"İş uygulamalarını ve bildirimlerini duraklatın"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"İş uygulamalarını burada bulabilirsiniz"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Her iş uygulamasında, uygulama güvenliğinin kuruluşunuz tarafından sağlandığını gösteren bir rozet bulunur. Daha kolay erişim için uygulamaları Ana ekranınıza taşıyın."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Kuruluşunuz tarafından yönetiliyor"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirimler ve uygulamalar kapalı"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Kapat"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Kapalı"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Başarısız: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 57e7c5a..13ba701 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"читати налаштування та ярлики головного екрана"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Проблема із завантаженням віджета"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Налаштування"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Це системна програма, її неможливо видалити."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Редагувати назву"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назви"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> вимкнено"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
@@ -80,8 +79,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Торкніться, щоб зберегти зміни"</string>
<string name="folder_closed" msgid="4100806530910930934">"Папку закрито"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Папку перейменовано на <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g>"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Папка \"<xliff:g id="NAME">%1$s</xliff:g>\", елементів: <xliff:g id="SIZE">%2$d</xliff:g> або більше"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стиль і фон"</string>
@@ -96,7 +94,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Додавати значки додатків на головний екран"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додавати значок на головний екран"</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>
@@ -133,18 +131,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"Робочий профіль"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Персональні дані зберігаються окремо, вони недоступні для робочих додатків"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"ІТ-адміністратор бачить ваші робочі додатки й дані"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Далі"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Робочий профіль призупинено"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Робочі додатки не можуть надсилати сповіщення, споживати заряд акумулятора й використовувати геодані"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Робочий профіль призупинено. Робочі додатки не можуть надсилати сповіщення, споживати заряд акумулятора й використовувати геодані"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Призупинити робочі додатки й сповіщення"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Робочі додатки містяться тут"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Кожний робочий додаток має значок і перебуває під захистом організації. Перенесіть додатки на головний екран, щоб швидко запускати їх."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Профілем керує ваша організація"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Сповіщення та додатки вимкнено"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Закрити"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Закрито"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Не вдалося <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 881ae53..4f77670 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"ویجیٹ کو لوڈ کرنے میں مسئلہ"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"ترتیب دیں"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"نام میں ترمیم کریں"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"بلا نام فولڈر"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیر فعال ہے"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> میں<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اطلاعات ہیں</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"نام کی تبدیلی محفوظ کرنے کیلئے تھپتھپائیں"</string>
<string name="folder_closed" msgid="4100806530910930934">"فولڈر بند ہو گیا"</string>
<string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> آئٹمز"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>، <xliff:g id="SIZE">%2$d</xliff:g> یا مزید آئٹمز"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"طرزیں اور وال پیپر"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"ہوم اسکرین میں ایپ آئیکنز شامل کریں"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"آئیکن کو ہوم اسکرین میں شامل کریں"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"دفتری پروفائل"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"ذاتی ڈیٹا ورک ایپس سے الگ اور پوشیدہ ہے"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"آپ کا IT منتظم ورک ایپس اور ڈیٹا کو دیکھ سکتا ہے"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"اگلا"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"سمجھ آ گئی"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"دفتری پروفائل روک دی گئی ہے"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"ورک ایپس آپ کو اطلاعات نہیں بھیج سکتیں، آپ کی بیٹری استعمال، یا آپ کے مقام تک رسائی حاصل نہیں کر سکتی ہیں"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"دفتری پروفائل موقوف کر دی گئی ہے۔ ورک ایپس آپ کو اطلاعت نہیں بھیج سکتیں، آپ کی بیٹری کا استعمال، یا آپ کے مقام تک رسائی حاصل نہیں کر سکتی ہیں"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"ورک ایپس اور اطلاعات کو روکیں"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"یہاں دفتری ایپس تلاش کریں"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ہر دفتری ایپ میں ایک بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"آپ کی تنظیم کے زیر انتظام"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"اطلاعات اور ایپس آف ہیں"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"بند کریں"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"بند"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"ناکام ہو گيا: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 1532124..69084d7 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -20,7 +20,8 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
- <string name="work_folder_name" msgid="3753320833950115786">"Ish"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Ishga oid"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Ilova o‘rnatilmadi."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ilova mavjud emas"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi"</string>
@@ -46,14 +47,12 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Ilovalar ro‘yxati"</string>
<string name="all_apps_button_personal_label" msgid="1315764287305224468">"Shaxsiy ilovalar ro‘yxati"</string>
- <string name="all_apps_button_work_label" msgid="7270707118948892488">"Ishga oid ilovalar ro‘yxati"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Ishchi ilovalar ro‘yxati"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Bosh sahifa"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"O‘rnatish"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Tavsiya qilinmasin"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Tavsiyani mahkamlash"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Vidjetni yuklashda muammo"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Sozlash"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Nomini tahrirlash"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Nomsiz jild"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi o‘chirib qo‘yildi"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ilovasida <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ta bildirishnoma bor</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"O‘zgarishni saqlash uchun ustiga bosing"</string>
<string name="folder_closed" msgid="4100806530910930934">"Jild yopildi"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> fayllar"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Jild: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> va undan ortiq fayllar"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mavzu va fon rasmlari"</string>
@@ -94,7 +92,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Bildirishnoma belgilarini ko‘rsatish uchun <xliff:g id="NAME">%1$s</xliff:g> ilovasida bildirishnomalarni yoqing"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Sozlamalarni o‘zgartirish"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Bildirishnoma belgilarini chiqarish"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Ilova ikonkalarini bosh ekranga chiqarish"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka chiqarish"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Olib tashlash"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Tezkor tugmalar"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Yorliqlar va bildirishnomalar"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Yopish"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Yopish"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Bildirishnoma yopildi"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Shaxsiy"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Ish"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ish profili"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Shaxsiy maʼlumotlar ishga oid ilovalardan alohida va berkitilgan"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Ishga oid ilovalar va maʼlumotlarni AT administratoringiz koʻra oladi"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Keyingisi"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Ish profili pauzada"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Ishga oid ilovalar batareya sarfi haqida bildirishnomalar yubora olmaydi va joylashuv axborotidan foydalana olmaydi"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Ish profili pauzada. Ishga oid ilovalar batareya sarfi haqida bildirishnomalar yubora olmaydi va joylashuv axborotidan foydalana olmaydi"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Ishga oid ilova va bildirishnomalarni pauza qilish"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Ishchi"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ishchi profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ishga oid ilovalarni shu yerdan topish mumkin"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Nishonga ega har bir ishga oid ilova tashkilotingiz tomonidan himoyalanadi. Ishga oid ilovalarga osonroq kirish uchun ularni bosh ekranga chiqaring."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Tashkilotingiz tomonidan boshqariladi"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirishnomalar va ilovalar faol emas"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Yopish"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Yopiq"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Xato: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-v26/bools.xml b/res/values-v26/bools.xml
deleted file mode 100644
index ad8c7a1..0000000
--- a/res/values-v26/bools.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 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.
-*/
--->
-
-<resources>
- <bool name="notification_dots_enabled">true</bool>
-
- <bool name="enable_install_shortcut_api">false</bool>
-</resources>
\ No newline at end of file
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
deleted file mode 100644
index d2f0802..0000000
--- a/res/values-v26/styles.xml
+++ /dev/null
@@ -1,31 +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.
-*/
--->
-<resources>
- <!-- Theme for the widget container. -->
- <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
- <item name="android:colorPrimaryDark">#E8EAED</item>
- <item name="android:textColorSecondary">?android:attr/textColorPrimary</item>
- <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
- </style>
- <style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
- <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
- <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
- </style>
-
-</resources>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
new file mode 100644
index 0000000..6baf39e
--- /dev/null
+++ b/res/values-v31/colors.xml
@@ -0,0 +1,33 @@
+<?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.
+*/
+-->
+<resources>
+ <color name="popup_color_primary_light">@android:color/system_primary_50</color>
+ <color name="popup_color_secondary_light">@android:color/system_primary_100</color>
+ <color name="popup_color_tertiary_light">@android:color/system_primary_300</color>
+ <color name="popup_color_primary_dark">@android:color/system_primary_800</color>
+ <color name="popup_color_secondary_dark">@android:color/system_primary_900</color>
+ <color name="popup_color_tertiary_dark">@android:color/system_primary_700</color>
+
+ <color name="workspace_text_color_light">@android:color/system_primary_50</color>
+ <color name="workspace_text_color_dark">@android:color/system_primary_900</color>
+
+ <color name="text_color_primary_dark">@android:color/system_primary_50</color>
+ <color name="text_color_secondary_dark">@android:color/system_primary_200</color>
+ <color name="text_color_tertiary_dark">@android:color/system_primary_400</color>
+</resources>
\ No newline at end of file
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index c4f8304..71decfc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -20,15 +20,16 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Trình chạy 3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Ứng dụng chưa được cài đặt."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ứng dụng không có sẵn"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Ứng dụng đã tải xuống bị tắt ở chế độ An toàn"</string>
- <string name="safemode_widget_error" msgid="4863470563535682004">"Tiện ích bị vô hiệu hóa ở chế độ an toàn"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Tiện ích con bị vô hiệu hóa ở chế độ an toàn"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"Lối tắt không khả dụng"</string>
<string name="home_screen" msgid="806512411299847073">"Màn hình chính"</string>
<string name="custom_actions" msgid="3747508247759093328">"Tác vụ tùy chỉnh"</string>
- <string name="long_press_widget_to_add" msgid="7699152356777458215">"Chạm và giữ để chọn tiện ích."</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Chạm và giữ để chọn tiện ích con."</string>
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Nhấn đúp và giữ để chọn tiện ích hoặc sử dụng tác vụ tùy chỉnh."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
@@ -52,8 +53,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Thông tin ứng dụng"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Cài đặt"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Không đề xuất ứng dụng"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Ghim ứng dụng dự đoán"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"đọc cài đặt và lối tắt trên Màn hình chính"</string>
@@ -61,10 +60,10 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"ghi cài đặt và lối tắt trên Màn hình chính"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Cho phép ứng dụng thay đổi cài đặt và lối tắt trên Màn hình chính."</string>
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> không được phép thực hiện cuộc gọi điện thoại"</string>
- <string name="gadget_error_text" msgid="6081085226050792095">"Sự cố khi tải tiện ích"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Sự cố khi tải tiện ích con"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Thiết lập"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Đây là ứng dụng hệ thống và không thể gỡ cài đặt."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Chỉnh sửa tên"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Thư mục chưa đặt tên"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Đã vô hiệu hóa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, có <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> thông báo</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Nhấn để lưu đổi tên"</string>
<string name="folder_closed" msgid="4100806530910930934">"Đã đóng thư mục"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Đã đổi tên thư mục thành <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> mục trở lên"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Tiện ích"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Kiểu và hình nền"</string>
@@ -88,13 +86,13 @@
<string name="allow_rotation_title" msgid="7728578836261442095">"Cho phép xoay Màn hình chính"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Khi xoay điện thoại"</string>
<string name="notification_dots_title" msgid="9062440428204120317">"Dấu chấm thông báo"</string>
- <string name="notification_dots_desc_on" msgid="1679848116452218908">"Đang bật"</string>
+ <string name="notification_dots_desc_on" msgid="1679848116452218908">"Bật"</string>
<string name="notification_dots_desc_off" msgid="1760796511504341095">"Tắt"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Cần quyền truy cập thông báo"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Để hiển thị Dấu chấm thông báo, hãy bật thông báo ứng dụng cho <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Thay đổi cài đặt"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Hiển thị dấu chấm thông báo"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Thêm biểu tượng ứng dụng vào Màn hình chính"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Thêm biểu tượng vào màn hình chính"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Cho ứng dụng mới"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
@@ -131,18 +129,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Lối tắt"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Phím tắt và thông báo"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Loại bỏ"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Đóng"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Đã loại bỏ thông báo"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Cá nhân"</string>
- <string name="all_apps_work_tab" msgid="4884822796154055118">"Công việc"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Cơ quan"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Hồ sơ công việc"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Dữ liệu cá nhân được lưu trữ riêng biệt và ẩn khỏi các ứng dụng công việc"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Quản trị viên CNTT của bạn có thể xem dữ liệu và các ứng dụng công việc"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Tiếp theo"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"OK"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Hồ sơ công việc của bạn đã bị tạm dừng"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Các ứng dụng công việc không thể gửi thông báo cho bạn, sử dụng pin hoặc truy cập thông tin vị trí của bạn"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Hồ sơ công việc của bạn đã bị tạm dừng. Các ứng dụng công việc không thể: gửi thông báo cho bạn, sử dụng pin hoặc xem dữ liệu vị trí"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Tạm dừng các ứng dụng và thông báo liên quan tới công việc"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Tìm ứng dụng công việc tại đây"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Mỗi ứng dụng công việc đều có một huy hiệu và được tổ chức của bạn bảo mật. Bạn có thể di chuyển ứng dụng đến Màn hình chính để truy cập dễ dàng hơn."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Do tổ chức của bạn quản lý"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Thông báo và ứng dụng đang tắt"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Đóng"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Đã đóng"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Không thực hiện được thao tác: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 095c0e3..9804af1 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"未安装该应用。"</string>
<string name="activity_not_available" msgid="7456344436509528827">"应用不可用"</string>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"读取主屏幕设置和快捷方式"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"加载微件时出现问题"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"设置"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"这是系统应用,无法卸载。"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"修改名称"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"未命名文件夹"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"已停用<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 个通知</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"点按可保存新名称"</string>
<string name="folder_closed" msgid="4100806530910930934">"文件夹已关闭"</string>
<string name="folder_renamed" msgid="1794088362165669656">"已将文件夹重命名为“<xliff:g id="NAME">%1$s</xliff:g>”"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个项目"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 个或更多项目"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"样式和壁纸"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"将应用图标添加到主屏幕"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"将图标添加到主屏幕"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"工作资料"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"工作应用与个人数据相互独立,它们无法获取此类数据"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"您的 IT 管理员可以查看工作应用和工作数据"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"继续"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"工作资料已被暂停"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作资料已被暂停。现在,工作应用无法向您发送通知、不能耗用电池电量,也无法获取您的位置信息"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"暂停工作应用及其通知"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"请在此处查找工作应用"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"每个工作应用均有一个徽标,并由贵单位负责确保其安全。请将工作应用移到主屏幕,以便轻松访问。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"由贵单位管理"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"通知和应用均已关闭"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"关闭"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"已关闭"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失败:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index f497c58..e737744 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,8 +53,6 @@
<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="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="1941457408239617576">"讀取主畫面的設定和捷徑"</string>
@@ -64,7 +63,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"載入小工具時發生問題"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"設定"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,無法將其解除安裝。"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"編輯名稱"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 項通知</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"輕按即可儲存新名稱"</string>
<string name="folder_closed" msgid="4100806530910930934">"已關閉資料夾"</string>
<string name="folder_renamed" msgid="1794088362165669656">"資料夾已重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個或以上的項目"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"樣式和桌布"</string>
@@ -94,8 +92,8 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"將應用程式圖示新增至主畫面"</string>
- <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"新安裝的應用程式"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主畫面"</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_search" msgid="891119232568284442">"搜尋"</string>
@@ -131,18 +129,15 @@
<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="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_personal_apps" msgid="4155536355149317441">"個人資料會獨立儲存,並在工作應用程式中隱藏。"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"IT 管理員能看到工作應用程式的資料"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"繼續"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"知道了"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"工作設定檔已暫停使用"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"工作應用程式無法向您傳送通知、使用電池或存取位置"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作設定檔已暫停。工作應用程式無法向您傳送通知、使用電池或存取位置"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"暫停工作應用程式和通知"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"請在此處尋找工作應用程式"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"每個工作應用程式都有一個徽章,並由您的機構負責保持安全。您可以將工作應用程式移至主畫面,以便輕鬆存取。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"由您的機構管理"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"通知和應用程式已關閉"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"關閉"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"已關閉"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"操作失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index d735715..e971b69 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -20,6 +20,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></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>
@@ -52,19 +53,17 @@
<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="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="1941457408239617576">"讀取主螢幕的設定和捷徑"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"允許應用程式讀取主畫面中的設定和捷徑。"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"允許應用程式讀取主螢幕中的設定和捷徑。"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"寫入主螢幕設定和捷徑"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"允許應用程式變更主畫面中的設定和捷徑。"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"允許應用程式變更主螢幕中的設定和捷徑。"</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="6081085226050792095">"載入小工具時發生問題"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"設定"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,不可解除安裝。"</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"編輯名稱"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"已停用 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 則通知</item>
@@ -78,8 +77,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"輕觸即可儲存新名稱"</string>
<string name="folder_closed" msgid="4100806530910930934">"資料夾已關閉"</string>
<string name="folder_renamed" msgid="1794088362165669656">"已將資料夾重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個項目"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>,<xliff:g id="SIZE">%2$d</xliff:g> 個以上的項目"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"樣式和桌布"</string>
@@ -94,7 +92,7 @@
<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>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"將應用程式圖示新增到主畫面"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主螢幕"</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,18 +129,15 @@
<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="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="work_profile_toggle_label" msgid="3081029915775481146">"工作資料夾"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"系統會區隔個人資料與工作資料,因此兩者不會同時顯示"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"你的 IT 管理員可以查看工作應用程式和工作資料"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"繼續"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"我知道了"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"工作資料夾已暫停"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"工作應用程式無法傳送通知給你、不能使用電池電量,也無法存取你的位置資訊"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"工作資料夾已暫停。工作應用程式無法傳送通知給你、不能使用電池電量,也無法存取你的位置資訊"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"暫停工作應用程式和通知"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"在這裡尋找辦公應用程式"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"每個辦公應用程式都有徽章,並由貴機構負責管理及確保其安全。請將辦公應用程式移至主螢幕以便輕鬆存取。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"由貴機構所管理"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"已關閉通知和應用程式"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"關閉"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"已關閉"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"失敗:<xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index d9dc210..b937764 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -52,8 +52,6 @@
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
<string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi lohlelo lokusebenza"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Faka"</string>
- <string name="dismiss_prediction_label" msgid="3357562989568808658">"Ungaphakamisi uhlelo lokusebenza"</string>
- <string name="pin_prediction" msgid="4196423321649756498">"Ukubikezela Iphinikhodi"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"funda izilungiselelo zokuthi Ikhaya nezinqamuleli"</string>
@@ -64,7 +62,7 @@
<string name="gadget_error_text" msgid="6081085226050792095">"Inkinga yokulayisha iwijethi"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Ukumisa"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</string>
- <string name="folder_hint_text" msgid="5174843001373488816">"Hlela igama"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Ifolda engenagama"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Kukhutshaziwe <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<plurals name="dotted_app_label" formatted="false" msgid="5194538107138265416">
<item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, unezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
@@ -78,8 +76,7 @@
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Thepha ukuze ulondoloze ukuqamba kabusha"</string>
<string name="folder_closed" msgid="4100806530910930934">"Ifolda ivaliwe"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Ifolda iqanjwe kabusha ngo-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format_exact" msgid="8626242716117004803">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> izinto"</string>
- <string name="folder_name_format_overflow" msgid="4270108890534995199">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="SIZE">%2$d</xliff:g> noma izinto eziningi"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
<string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Izitayela nezithombe zangemuva"</string>
@@ -94,7 +91,7 @@
<string name="msg_missing_notification_access" msgid="281113995110910548">"Ukuze ubonisa amcashazi esaziso, vula izaziso zohlelo lokusebenza ze-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Shintsha izilungiselelo"</string>
<string name="notification_dots_service_title" msgid="4284221181793592871">"Bonisa amacashazi esaziso"</string>
- <string name="auto_add_shortcuts_label" msgid="3698776050751790653">"Engeza izithonjana zohlelo lokusebenza kusikrini sasekhaya"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Engeza isithonjana eskrinini sasekhaya"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Kwezinhlelo zokusebenza ezintsha"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
@@ -131,18 +128,15 @@
<string name="action_deep_shortcut" msgid="2864038805849372848">"Izinqamuleli"</string>
<string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"Izinqamuleli nezaziso"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Cashisa"</string>
- <string name="accessibility_close" msgid="2277148124685870734">"Vala"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Isaziso sicashisiwe"</string>
<string name="all_apps_personal_tab" msgid="4190252696685155002">"Okomuntu siqu"</string>
<string name="all_apps_work_tab" msgid="4884822796154055118">"Umsebenzi"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Iphrofayela yomsebenzi"</string>
- <string name="work_profile_edu_personal_apps" msgid="4155536355149317441">"Idatha yomuntu siqu yehlukile futhi ifihliwe kusuka kuzinhlelo zokusebenza zomsebenzi"</string>
- <string name="work_profile_edu_work_apps" msgid="237051938268703058">"Izinhlelo zokusebenza zomsebenzi nedatha kuyobonakala kumphathi wakho we-IT"</string>
- <string name="work_profile_edu_next" msgid="8783418929296503629">"Okulandelayo"</string>
- <string name="work_profile_edu_accept" msgid="6069788082535149071">"Ngiyezwa"</string>
- <string name="work_apps_paused_title" msgid="2389865654362803723">"Iphrofayela yomsebenzi iphunyuziwe"</string>
- <string name="work_apps_paused_body" msgid="2197210494568696054">"Izinhlelo zokusebenza zomsebenzi azikwazi ukukuthumela izaziso, ukusebenzisa ibhethri lakho, noma ukufinyelela indawo yakho"</string>
- <string name="work_apps_paused_content_description" msgid="7553586952985486433">"Iphrofayela yomsebenzi iphunyuziwe. Izinhlelo zokusebenza zomsebenzi azikwazi ukukuthumela izaziso, ukusebenzisa ibhethri yakho, noma ukufinyelela indawo yakho"</string>
- <string name="work_switch_tip" msgid="808075064383839144">"Phumuza izinhlelo zokusebenza zomsebenzi nezaziso"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Thola izinhlelo zokusebenza lapha"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"Uhlo lokusebenza ngalunye lomsebenzi linebheji futhi igcinwa iphephile inhlangano yakho. Hambisa izinhlelo zokusebenza esikrinini sakho sasekhaya ngokufinyelela okulula."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Kuphethwe inhlangano yakho"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Izaziso nezinhlelo zokusebenza kuvaliwe"</string>
+ <string name="bottom_work_tab_user_education_close_button" msgid="4224492243977802135">"Vala"</string>
+ <string name="bottom_work_tab_user_education_closed" msgid="1098340939861869465">"Kuvaliwe"</string>
<string name="remote_action_failed" msgid="1383965239183576790">"Yehlulekile: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 26e6cba..4078ef4 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
/* Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -30,13 +29,13 @@
<attr name="isWorkspaceDarkText" format="boolean" />
<attr name="workspaceTextColor" format="color" />
<attr name="workspaceShadowColor" format="color" />
- <attr name="workspaceAmbientShadowColor" format="color"/>
+ <attr name="workspaceAmbientShadowColor" format="color" />
<attr name="workspaceKeyShadowColor" format="color" />
<attr name="workspaceStatusBarScrim" format="reference" />
<attr name="widgetsTheme" format="reference" />
<attr name="loadingIconColor" format="color" />
- <attr name="iconOnlyShortcutColor" format="color"/>
- <attr name="eduHalfSheetBGColor" format="color"/>
+ <attr name="iconOnlyShortcutColor" format="color" />
+ <attr name="eduHalfSheetBGColor" format="color" />
<attr name="folderDotColor" format="color" />
<attr name="folderFillColor" format="color" />
@@ -57,6 +56,8 @@
<enum name="folder" value="2" />
<enum name="widget_section" value="3" />
<enum name="shortcut_popup" value="4" />
+ <enum name="hero_app" value="5" />
+ <enum name="taskbar" value="6" />
</attr>
<attr name="centerVertically" format="boolean" />
</declare-styleable>
@@ -68,6 +69,12 @@
<attr name="folderDotColor" />
</declare-styleable>
+ <declare-styleable name="SearchResultSuggestion">
+ <attr name="customIcon" format="reference" />
+ <attr name="matchTextInsetWithQuery" format="boolean" />
+ </declare-styleable>
+
+
<declare-styleable name="ShadowInfo">
<attr name="ambientShadowColor" format="color" />
<attr name="ambientShadowBlur" format="dimension" />
@@ -126,6 +133,18 @@
<attr name="numAllAppsColumns" format="integer" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
+ <attr name="isScalable" format="boolean" />
+
+ </declare-styleable>
+
+ <declare-styleable name="DevicePadding">
+ <attr name="maxEmptySpace" format="dimension" />
+ </declare-styleable>
+
+ <declare-styleable name="DevicePaddingFormula">
+ <attr name="a" format="float|dimension" />
+ <attr name="b" format="float|dimension" />
+ <attr name="c" format="float|dimension" />
</declare-styleable>
<declare-styleable name="ProfileDisplayOption">
@@ -133,6 +152,12 @@
<attr name="minWidthDps" format="float" />
<attr name="minHeightDps" format="float" />
+ <!-- These min cell values are only used if GridDisplayOption#isScalable is true-->
+ <attr name="minCellHeightDps" format="float" />
+ <attr name="minCellWidthDps" format="float" />
+
+ <attr name="borderSpacingDps" format="float" />
+
<attr name="iconImageSize" format="float" />
<!-- landscapeIconSize defaults to iconSize, if not specified -->
<attr name="landscapeIconSize" format="float" />
@@ -159,15 +184,27 @@
<attr name="android:src" />
<attr name="android:shadowColor" />
<attr name="android:elevation" />
- <attr name="darkTintColor" format="color"/>
+ <attr name="darkTintColor" format="color" />
</declare-styleable>
<declare-styleable name="RecyclerViewFastScroller">
<attr name="canThumbDetach" format="boolean" />
</declare-styleable>
+ <declare-styleable name="LoggablePref">
+ <attr name="android:key" />
+ <attr name="android:defaultValue" />
+ <!-- Ground truth of this Pref integer can be found in StatsLogManager -->
+ <attr name="logIdOn" format="integer" />
+ <attr name="logIdOff" format="integer" />
+ </declare-styleable>
+
<declare-styleable name="PreviewFragment">
<attr name="android:name" />
<attr name="android:id" />
</declare-styleable>
+
+ <declare-styleable name="WidgetsListRowHeader">
+ <attr name="appIconSize" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/res/values/bools.xml b/res/values/bools.xml
deleted file mode 100644
index bc2c678..0000000
--- a/res/values/bools.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* Copyright 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.
-*/
--->
-
-<resources>
- <bool name="notification_dots_enabled">false</bool>
-
- <bool name="enable_install_shortcut_api">true</bool>
-</resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 043ad9a..0b30253 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -35,11 +35,24 @@
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
- <color name="all_apps_bg_hand_fill">#E5E5E5</color>
- <color name="all_apps_bg_hand_fill_dark">#9AA0A6</color>
-
<color name="gesture_tutorial_ripple_color">#A0C2F9</color> <!-- Light Blue -->
<color name="gesture_tutorial_fake_task_view_color">#6DA1FF</color> <!-- Light Blue -->
+ <color name="gesture_tutorial_fake_previous_task_view_color">#9CCC65</color> <!-- Light Green -->
<color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
<color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
+
+ <color name="popup_color_primary_light">#FFF</color>
+ <color name="popup_color_secondary_light">#F1F3F4</color>
+ <color name="popup_color_tertiary_light">#E0E0E0</color> <!-- Gray 300 -->
+ <color name="popup_color_primary_dark">#3C4043</color> <!-- Gray 800 -->
+ <color name="popup_color_secondary_dark">#202124</color>
+ <color name="popup_color_tertiary_dark">#757575</color> <!-- Gray 600 -->
+
+ <color name="workspace_text_color_light">#FFF</color>
+ <color name="workspace_text_color_dark">#FF212121</color>
+
+ <color name="text_color_primary_dark">#FFFFFFFF</color>
+ <color name="text_color_secondary_dark">#FFFFFFFF</color>
+ <color name="text_color_tertiary_dark">#CCFFFFFF</color>
+
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 75fcc90..8184c98 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -24,7 +24,7 @@
<!-- AllApps & Launcher transitions -->
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
- <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
+ <integer name="config_workspaceSpringLoadShrinkPercentage">85</integer>
<!-- The duration of the animation from search hint to text entry -->
<integer name="config_searchHintAnimationDuration">50</integer>
@@ -59,8 +59,6 @@
<bool name="hotseat_transpose_layout_with_orientation">true</bool>
<!-- Various classes overriden by projects/build flavors. -->
- <string name="app_filter_class" translatable="false"></string>
- <string name="user_event_dispatcher_class" translatable="false"></string>
<string name="folder_name_provider_class" translatable="false"></string>
<string name="stats_log_manager_class" translatable="false"></string>
<string name="app_transition_manager_class" translatable="false"></string>
@@ -69,7 +67,7 @@
<string name="app_launch_tracker_class" translatable="false"></string>
<string name="test_information_handler_class" translatable="false"></string>
<string name="launcher_activity_logic_class" translatable="false"></string>
- <string name="prediction_model_class" translatable="false"></string>
+ <string name="model_delegate_class" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
@@ -92,6 +90,7 @@
<string name="wallpaper_picker_package" translatable="false"></string>
<string name="calendar_component_name" translatable="false"></string>
<string name="clock_component_name" translatable="false"></string>
+ <string name="local_colors_extraction_class" translatable="false"></string>
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
@@ -113,6 +112,7 @@
<!-- QSB IDs. DO not change -->
<item type="id" name="search_container_workspace" />
<item type="id" name="search_container_all_apps" />
+ <item type="id" name="search_container_hotseat" />
<!-- Recents -->
<item type="id" name="overview_panel"/>
@@ -187,4 +187,10 @@
<string-array name="live_wallpapers_remove_sysui_scrims">
</string-array>
+
+ <string-array name="filtered_components" ></string-array>
+
+ <!-- Name of the class used to generate colors from the wallpaper colors. Must be implementing the LauncherAppWidgetHostView.ColorGenerator interface. -->
+ <string name="color_generator_class" translatable="false"/>
+
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 947e635..da43758 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,10 +20,12 @@
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">8dp</dimen>
- <dimen name="dynamic_grid_icon_drawable_padding">8dp</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>
+ <dimen name="dynamic_grid_cell_border_spacing">16dp</dimen>
<dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
<dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
@@ -35,19 +37,14 @@
<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_left_right_margin">22dp</dimen>
+
<!-- Workspace page indicator -->
<dimen name="workspace_page_indicator_height">24dp</dimen>
<dimen name="workspace_page_indicator_line_height">1dp</dimen>
<dimen name="workspace_page_indicator_overlap_workspace">0dp</dimen>
- <!-- Hotseat/all-apps scrim -->
- <dimen name="all_apps_scrim_blur">4dp</dimen>
- <dimen name="vertical_drag_handle_width">18dp</dimen>
- <dimen name="vertical_drag_handle_height">6dp</dimen>
- <dimen name="vertical_drag_handle_elevation">1dp</dimen>
- <dimen name="vertical_drag_handle_touch_size">48dp</dimen>
- <dimen name="vertical_drag_handle_padding_in_vertical_bar_layout">16dp</dimen>
-
<!-- Drop target bar -->
<dimen name="dynamic_grid_drop_target_size">48dp</dimen>
<dimen name="drop_target_vertical_gap">20dp</dimen>
@@ -55,6 +52,7 @@
<!-- App Widget resize frame -->
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
+ <dimen name="resize_frame_margin">22dp</dimen>
<!-- Fast scroll -->
<dimen name="fastscroll_track_min_width">6dp</dimen>
@@ -96,7 +94,6 @@
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
-
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
<dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
@@ -108,8 +105,8 @@
<dimen name="work_profile_footer_text_size">16sp</dimen>
<!-- Widget tray -->
- <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
- <dimen name="widget_preview_label_horizontal_padding">16dp</dimen>
+ <dimen name="widget_cell_vertical_padding">8dp</dimen>
+ <dimen name="widget_cell_horizontal_padding">16dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
<dimen name="widget_preview_key_shadow_distance">1dp</dimen>
@@ -147,16 +144,19 @@
<dimen name="drag_flingToDeleteMinVelocity">-1500dp</dimen>
<dimen name="spring_loaded_panel_border">1dp</dimen>
+ <dimen name="keyboard_drag_stroke_width">4dp</dimen>
<!-- Folders -->
<dimen name="page_indicator_dot_size">8dp</dimen>
<dimen name="folder_cell_x_padding">9dp</dimen>
<dimen name="folder_cell_y_padding">6dp</dimen>
- <dimen name="folder_child_text_size">13sp</dimen>
- <dimen name="folder_label_padding_top">4dp</dimen>
- <dimen name="folder_label_padding_bottom">12dp</dimen>
- <dimen name="folder_label_text_size">14sp</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_content_padding_left_right">8dp</dimen>
+ <dimen name="folder_content_padding_top">16dp</dimen>
<!-- Sizes for managed profile badges -->
<dimen name="profile_badge_size">24dp</dimen>
@@ -175,7 +175,8 @@
<!-- Deep shortcuts -->
<dimen name="deep_shortcuts_elevation">9dp</dimen>
- <dimen name="bg_popup_item_width">220dp</dimen>
+ <!-- also update deep_shortcuts_divider_width -->
+ <dimen name="bg_popup_item_width">234dp</dimen>
<dimen name="bg_popup_item_height">56dp</dimen>
<dimen name="bg_popup_item_condensed_height">48dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
@@ -187,18 +188,16 @@
<dimen name="popup_padding_start">10dp</dimen>
<dimen name="popup_padding_end">16dp</dimen>
<dimen name="popup_vertical_padding">4dp</dimen>
- <dimen name="popup_arrow_width">10dp</dimen>
- <dimen name="popup_arrow_height">8dp</dimen>
- <dimen name="popup_arrow_vertical_offset">-2dp</dimen>
+ <dimen name="popup_arrow_width">12dp</dimen>
+ <dimen name="popup_arrow_height">10dp</dimen>
+ <dimen name="popup_arrow_vertical_offset">-1dp</dimen>
<!-- popup_padding_start + deep_shortcut_icon_size / 2 -->
- <dimen name="popup_arrow_horizontal_center_start">28dp</dimen>
- <!-- popup_padding_end + deep_shortcut_drag_handle_size / 2 -->
- <dimen name="popup_arrow_horizontal_center_end">24dp</dimen>
+ <dimen name="popup_arrow_horizontal_center_offset">28dp</dimen>
<dimen name="popup_arrow_corner_radius">2dp</dimen>
<!-- popup_padding_start + icon_size + 10dp -->
<dimen name="deep_shortcuts_text_padding_start">56dp</dimen>
<!-- popup_item_width - deep_shortcuts_text_padding_start -->
- <dimen name="deep_shortcuts_divider_width">164dp</dimen>
+ <dimen name="deep_shortcuts_divider_width">178dp</dimen>
<dimen name="system_shortcut_icon_size">24dp</dimen>
<!-- popup_arrow_center_start - system_shortcut_icon_size / 2 -->
<dimen name="system_shortcut_margin_start">16dp</dimen>
@@ -247,10 +246,26 @@
<dimen name="snackbar_min_text_size">12sp</dimen>
<dimen name="snackbar_max_text_size">14sp</dimen>
+<!-- Developer Options -->
+ <dimen name="developer_options_filter_margins">10dp</dimen>
+
<!-- Theming related -->
<dimen name="default_dialog_corner_radius">8dp</dimen>
<!-- Onboarding bottomsheet related -->
<dimen name="bottom_sheet_edu_padding">24dp</dimen>
+ <!-- Search related -->
+ <dimen name="search_hero_title_size">16sp</dimen>
+ <dimen name="search_hero_subtitle_size">14sp</dimen>
+ <dimen name="search_hero_inline_button_size">12sp</dimen>
+ <dimen name="search_settings_icon_size">36dp</dimen>
+ <dimen name="search_settings_icon_vertical_offset">16dp</dimen>
+ <dimen name="search_line_spacing">4dp</dimen>
+ <dimen name="search_decoration_corner_radius">28dp</dimen>
+ <dimen name="search_decoration_padding">1dp</dimen>
+
+<!-- Taskbar related (placeholders to compile in Launcher3 without Quickstep) -->
+ <dimen name="taskbar_size">0dp</dimen>
+
</resources>
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
deleted file mode 100644
index 7d63142..0000000
--- a/res/values/drawables.xml
+++ /dev/null
@@ -1,22 +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.
--->
-<resources>
- <drawable name="ic_setup_shadow">@drawable/ic_setting</drawable>
- <drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
- <drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
- <drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
- <drawable name="all_apps_arrow_shadow">@drawable/drag_handle_indicator_no_shadow</drawable>
-</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 935bb40..6fb72b1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -36,9 +36,7 @@
<!-- Message shown when a shortcut is not available. It could have been temporarily disabled and may start working again after some time. -->
<string name="shortcut_not_available">Shortcut isn\'t available</string>
<!-- User visible name for the launcher/home screen. [CHAR_LIMIT=30] -->
- <string name="home_screen">Home screen</string>
- <!-- Label for showing custom action list of a shortcut or widget. [CHAR_LIMIT=30] -->
- <string name="custom_actions">Custom actions</string>
+ <string name="home_screen">Home</string>
<!-- Widgets -->
<!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
@@ -54,10 +52,37 @@
<string name="add_item_request_drag_hint">Touch & hold to place manually</string>
<!-- Button label to automatically add icon on home screen [CHAR_LIMIT=50] -->
<string name="place_automatically">Add automatically</string>
+ <!-- Label for showing the number of widgets an app has in the full widgets picker.
+ [CHAR_LIMIT=25] -->
+ <plurals name="widgets_count">
+ <item quantity="one"><xliff:g id="widgets_count" example="1">%1$d</xliff:g> widget</item>
+ <item quantity="other"><xliff:g id="widgets_count" example="2">%1$d</xliff:g> widgets</item>
+ </plurals>
+ <!-- Label for showing the number of shortcut an app has in the full widgets picker.
+ [CHAR_LIMIT=25] -->
+ <plurals name="shortcuts_count">
+ <item quantity="one"><xliff:g id="shortcuts_count" example="1">%1$d</xliff:g> shortcut</item>
+ <item quantity="other"><xliff:g id="shortcuts_count" example="2">%1$d</xliff:g> shortcuts</item>
+ </plurals>
+ <!-- Label for showing both the number of widgets and shortcuts an app has in the full widgets
+ picker. [CHAR_LIMIT=50] -->
+ <string name="widgets_and_shortcuts_count"><xliff:g id="widgets_count" example="5 widgets">%1$s</xliff:g>, <xliff:g id="shortcuts_count" example="1 shortcut">%2$s</xliff:g></string>
+
+ <!-- Text for both the tile of a popup view, which shows all available widgets installed on
+ the device, and the text of a button, which opens this popup view. [CHAR LIMIT=30]-->
+ <string name="widget_button_text">Widgets</string>
+ <!-- Search bar text shown in the popup view showing all available widgets installed on the
+ device. [CHAR_LIMIT=50] -->
+ <string name="widgets_full_sheet_search_bar_hint">Search</string>
+ <!-- Text shown when there is no widgets shown in the popup view showing all available widgets
+ installed on the device. [CHAR_LIMIT=none] -->
+ <string name="no_widgets_available">No widgets available</string>
<!-- All Apps -->
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
<string name="all_apps_search_bar_hint">Search apps</string>
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <string name="all_apps_on_device_search_bar_hint">Search this phone and more…</string>
<!-- Loading apps text. [CHAR_LIMIT=50] -->
<string name="all_apps_loading_message">Loading apps…</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
@@ -67,6 +92,12 @@
<!-- Label for an icon representing any generic app. [CHAR_LIMIT=50] -->
<string name="label_application">App</string>
+ <!--All apps Search-->
+ <!-- Section title for apps [CHAR_LIMIT=50] -->
+ <string name="search_corpus_apps">Apps</string>
+ <!-- try instant app action for play search result [CHAR_LIMIT=50 -->
+ <string name="search_action_try_now">Try Now</string>
+
<!-- Popup items -->
<!-- Text to display as the header above notifications. [CHAR_LIMIT=30] -->
<string name="notifications_header">Notifications</string>
@@ -88,10 +119,6 @@
<string name="all_apps_button_personal_label">Personal apps list</string>
<string name="all_apps_button_work_label">Work apps list</string>
- <!-- Label for button in all applications label to go back home (to the workspace / desktop)
- for accessibilty (spoken when the button gets focus). -->
- <string name="all_apps_home_button_label">Home</string>
-
<!-- Label for remove drop target (from the homescreen only).
May appear next to uninstall_drop_target_label [CHAR_LIMIT=20] -->
<string name="remove_drop_target_label">Remove</string>
@@ -178,13 +205,11 @@
<string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
<!-- Strings for the customization mode -->
- <!-- Text for widget add button -->
- <string name="widget_button_text">Widgets</string>
- <!-- Text for wallpaper change button -->
+ <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
<string name="wallpaper_button_text">Wallpapers</string>
- <!-- Text for wallpaper change button -->
+ <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
<string name="styles_wallpaper_button_text">Styles & wallpapers</string>
- <!-- Text for settings button [CHAR LIMIT=20]-->
+ <!-- Text for settings button [CHAR LIMIT=30]-->
<string name="settings_button_text">Home settings</string>
<!-- Message shown when a feature is disabled by the administrator -->
<string name="msg_disabled_by_admin">Disabled by your admin</string>
@@ -228,6 +253,8 @@
<string name="abandoned_promise_explanation">The app for this icon isn\'t installed.
You can remove it, or search for the app and install it manually.
</string>
+ <!-- Title for an app which is being installed. -->
+ <string name="app_installing_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> installing, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
<!-- Title for an app which is being downloaded. -->
<string name="app_downloading_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> downloading, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
<!-- Title for an app whose download has been started. -->
@@ -348,7 +375,8 @@
<!-- content description for paused work apps list -->
<string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
-
+ <!-- A hint shown in launcher settings develop options filter box -->
+ <string name="developer_options_filter_hint">Filter</string>
<!-- A tip shown pointing at work toggle -->
<string name="work_switch_tip">Pause work apps and notifications</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 25f21f3..adc2238 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -31,25 +31,25 @@
<style name="LauncherTheme" parent="@style/BaseLauncherTheme">
<item name="android:textColorSecondary">#DE000000</item>
- <item name="allAppsScrimColor">#FFFFFFFF</item>
+ <item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item>
<item name="allAppsInterimScrimAlpha">46</item>
<item name="allAppsNavBarScrimColor">#66FFFFFF</item>
<item name="allAppsTheme">@style/AllAppsTheme</item>
- <item name="popupColorPrimary">#FFF</item>
- <item name="popupColorSecondary">#F1F3F4</item>
- <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+ <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>
<item name="isMainColorDark">false</item>
<item name="isWorkspaceDarkText">false</item>
- <item name="workspaceTextColor">@android:color/white</item>
+ <item name="workspaceTextColor">@color/workspace_text_color_light</item>
<item name="workspaceShadowColor">#B0000000</item>
<item name="workspaceAmbientShadowColor">#33000000</item>
<item name="workspaceKeyShadowColor">#44000000</item>
<item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="folderDotColor">?android:attr/colorPrimary</item>
- <item name="folderFillColor">#CDFFFFFF</item>
+ <item name="folderFillColor">?android:attr/colorBackground</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
- <item name="folderTextColor">#FF212121</item>
+ <item name="folderTextColor">?android:attr/textColorPrimary</item>
<item name="folderHintColor">#89616161</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="iconOnlyShortcutColor">?android:attr/textColorSecondary</item>
@@ -74,7 +74,7 @@
</style>
<style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
- <item name="workspaceTextColor">#FF212121</item>
+ <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
<item name="allAppsInterimScrimAlpha">128</item>
<item name="workspaceShadowColor">@android:color/transparent</item>
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
@@ -88,24 +88,24 @@
</style>
<style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
- <item name="android:textColorPrimary">#FFFFFFFF</item>
- <item name="android:textColorSecondary">#FFFFFFFF</item>
- <item name="android:textColorTertiary">#CCFFFFFF</item>
+ <item name="android:textColorPrimary">@color/text_color_primary_dark</item>
+ <item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
+ <item name="android:textColorTertiary">@color/text_color_tertiary_dark</item>
<item name="android:textColorHint">#A0FFFFFF</item>
<item name="android:colorControlHighlight">#A0FFFFFF</item>
<item name="android:colorPrimary">#FF212121</item>
- <item name="allAppsScrimColor">#FF000000</item>
+ <item name="allAppsScrimColor">?android:attr/colorBackgroundFloating</item>
<item name="allAppsInterimScrimAlpha">102</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
<item name="allAppsTheme">@style/AllAppsTheme.Dark</item>
- <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
- <item name="popupColorSecondary">#202124</item>
- <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+ <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>
<item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
- <item name="folderDotColor">#FF464646</item>
- <item name="folderFillColor">#DD3C4043</item> <!-- 87% GM2 800 -->
- <item name="folderIconBorderColor">#FF80868B</item>
- <item name="folderTextColor">@android:color/white</item>
+ <item name="folderDotColor">?android:attr/colorPrimary</item>
+ <item name="folderFillColor">?android:attr/colorBackground</item>
+ <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
+ <item name="folderTextColor">?android:attr/textColorPrimary</item>
<item name="folderHintColor">#89CCCCCC</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#99FFFFFF</item>
@@ -125,7 +125,7 @@
<item name="allAppsInterimScrimAlpha">25</item>
<item name="folderFillColor">#CDFFFFFF</item>
<item name="folderTextColor">?attr/workspaceTextColor</item>
- <item name="workspaceTextColor">#FF212121</item>
+ <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
<item name="workspaceShadowColor">@android:color/transparent</item>
<item name="workspaceAmbientShadowColor">@android:color/transparent</item>
<item name="workspaceKeyShadowColor">@android:color/transparent</item>
@@ -143,12 +143,21 @@
<style name="AppTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark.DarkMainColor" />
<style name="AppTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark.DarkText" />
- <style name="AppItemActivityTheme" parent="@android:style/Theme.Material.Light.Dialog.Alert">
+ <style name="AppItemActivityTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
</style>
<style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="preferenceTheme">@style/HomeSettingsPreferenceTheme</item>
+ </style>
+
+ <style name="HomeSettingsPreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
+ <item name="preferenceFragmentCompatStyle">@style/HomeSettingsFragmentCompatStyle</item>
+ </style>
+
+ <style name="HomeSettingsFragmentCompatStyle" parent="@style/PreferenceFragment.Material">
+ <item name="android:layout">@layout/home_settings</item>
</style>
<!--
@@ -162,14 +171,16 @@
<item name="android:textColorSecondary">?attr/workspaceTextColor</item>
</style>
- <!-- Theme for the widget container. Overridden on API 26. -->
+ <!-- Theme for the widget container. -->
<style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
- <item name="android:colorEdgeEffect">?android:attr/textColorSecondaryInverse</item>
- <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
- <item name="android:textColorSecondary">?android:attr/textColorSecondaryInverse</item>
+ <item name="android:colorPrimaryDark">#E8EAED</item>
+ <item name="android:textColorSecondary">?android:attr/textColorPrimary</item>
+ <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
</style>
-
- <style name="WidgetContainerTheme.Dark" />
+ <style name="WidgetContainerTheme.Dark" parent="AppTheme.Dark">
+ <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+ <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
+ </style>
<style name="FastScrollerPopup" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
<item name="android:layout_width">wrap_content</item>
@@ -212,6 +223,16 @@
<item name="android:lines">1</item>
</style>
+ <!-- Base theme for AllApps BubbleTextViews -->
+ <style name="BaseIcon.AllApps" parent="BaseIcon">
+ <item name="android:layout_width">match_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:stateListAnimator">@animator/all_apps_fastscroll_icon_anim</item>
+ <item name="android:paddingLeft">@dimen/dynamic_grid_cell_padding_x</item>
+ <item name="android:paddingRight">@dimen/dynamic_grid_cell_padding_x</item>
+ </style>
+
+
<!-- Icon displayed on the workspace -->
<style name="BaseIcon.Workspace" >
<item name="android:shadowRadius">2.0</item>
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 3455cb8..e4bea50 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -15,7 +15,8 @@
-->
<androidx.preference.PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto">
<com.android.launcher3.settings.NotificationDotsPreference
android:key="pref_icon_badging"
@@ -30,25 +31,31 @@
</intent>
</com.android.launcher3.settings.NotificationDotsPreference>
+ <!--
+ LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613)
+ LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_DISABLED(614)
+ -->
<SwitchPreference
android:key="pref_add_icon_to_home"
android:title="@string/auto_add_shortcuts_label"
android:summary="@string/auto_add_shortcuts_description"
android:defaultValue="true"
- android:persistent="true" />
+ android:persistent="true"
+ launcher:logIdOn="613"
+ launcher:logIdOff="614" />
+ <!--
+ LAUNCHER_HOME_SCREEN_ROTATION_ENABLED(615)
+ LAUNCHER_HOME_SCREEN_ROTATION_DISABLED(616)
+ -->
<SwitchPreference
android:key="pref_allowRotation"
android:title="@string/allow_rotation_title"
android:summary="@string/allow_rotation_desc"
android:defaultValue="@bool/allow_rotation"
- android:persistent="true" />
-
- <SwitchPreference
- android:key="pref_grid_options"
- android:title="Enable grid options"
- android:defaultValue="false"
- android:persistent="true" />
+ android:persistent="true"
+ launcher:logIdOn="615"
+ launcher:logIdOff="616" />
<androidx.preference.PreferenceScreen
android:key="pref_developer_options"
diff --git a/res/xml/size_limits.xml b/res/xml/size_limits.xml
new file mode 100644
index 0000000..ba57014
--- /dev/null
+++ b/res/xml/size_limits.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
+
+ <device-padding
+ launcher:maxEmptySpace="88dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="0"/>
+ <workspaceBottomPadding
+ launcher:a="0.52"
+ launcher:b="0"/>
+ <hotseatBottomPadding
+ launcher:a="0.48"
+ launcher:b="0"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="97dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="16dp"/>
+ <workspaceBottomPadding
+ launcher:a="0.50"
+ launcher:b="0"
+ launcher:c="-16dp"/>
+ <hotseatBottomPadding
+ launcher:a="0.50"
+ launcher:b="0"
+ launcher:c="16dp"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="107dp">
+ <workspaceTopPadding
+ launcher:a="0"
+ launcher:b="16dp"/>
+ <workspaceBottomPadding
+ launcher:a="0"
+ launcher:b="36dp"/>
+ <hotseatBottomPadding
+ launcher:a="1"
+ launcher:b="0"
+ launcher:c="52dp"/>
+ </device-padding>
+
+ <device-padding
+ launcher:maxEmptySpace="9999dp">
+ <workspaceTopPadding
+ launcher:a="0.38"
+ launcher:c="36dp"/>
+ <workspaceBottomPadding
+ launcher:a="0.62"
+ launcher:c="36dp"/>
+ <hotseatBottomPadding
+ launcher:a="0"
+ launcher:b="36dp"/>
+ </device-padding>
+
+</device-paddings>
\ No newline at end of file
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
new file mode 100644
index 0000000..c738df9
--- /dev/null
+++ b/robolectric_tests/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+//
+// Launcher Robolectric test target.
+//
+// "robolectric_android-all-stub", not needed, we write our own stubs
+android_robolectric_test {
+ name: "LauncherRoboTests",
+ srcs: [
+ "src/**/*.java",
+ ],
+ java_resource_dirs: [
+ "resources",
+ "res",
+ "config",
+ ],
+ static_libs: [
+ "truth-prebuilt",
+ "Launcher3TestCommon",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "mockito-robolectric-prebuilt",
+ ],
+ //robolectric_prebuilt_version: "4.4",
+ libs: [
+ "platform-robolectric-4.4-prebuilt",
+ ],
+ instrumentation_for: "Launcher3",
+
+ test_options: {
+ timeout: 36000,
+ },
+}
+
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
deleted file mode 100644
index 79c26c3..0000000
--- a/robolectric_tests/Android.mk
+++ /dev/null
@@ -1,68 +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.
-
-#############################################
-# Launcher Robolectric test target. #
-#############################################
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := LauncherRoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-
-LOCAL_SDK_VERSION := system_current
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src) \
- $(call all-java-files-under, ../tests/src_common)
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- androidx.test.runner \
- androidx.test.rules \
- mockito-robolectric-prebuilt \
- truth-prebuilt
-LOCAL_JAVA_LIBRARIES := \
- platform-robolectric-4.3.1-prebuilt
-
-LOCAL_JAVA_RESOURCE_DIRS := resources config
-
-LOCAL_INSTRUMENTATION_FOR := Launcher3
-LOCAL_MODULE_TAGS := optional
-
-# Generate test_config.properties
-include external/robolectric-shadows/gen_test_config.mk
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-############################################
-# Target to run the previous target. #
-############################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := RunLauncherRoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
-LOCAL_SDK_VERSION := system_current
-LOCAL_JAVA_LIBRARIES := LauncherRoboTests
-
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-LOCAL_TEST_PACKAGE := Launcher3
-LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/Launcher3/src
-
-LOCAL_ROBOTEST_TIMEOUT := 36000
-
-include prebuilts/misc/common/robolectric/4.3.1/run_robotests.mk
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
index a8e0cb3..1b170e1 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/config/robolectric.properties
@@ -1,19 +1,15 @@
sdk=29
+
shadows= \
- com.android.launcher3.shadows.LShadowApplicationPackageManager \
com.android.launcher3.shadows.LShadowAppPredictionManager \
com.android.launcher3.shadows.LShadowAppWidgetManager \
com.android.launcher3.shadows.LShadowBackupManager \
- com.android.launcher3.shadows.LShadowBitmap \
com.android.launcher3.shadows.LShadowDisplay \
com.android.launcher3.shadows.LShadowLauncherApps \
- com.android.launcher3.shadows.LShadowTypeface \
- com.android.launcher3.shadows.LShadowUserManager \
- com.android.launcher3.shadows.LShadowWallpaperManager \
com.android.launcher3.shadows.ShadowDeviceFlag \
com.android.launcher3.shadows.ShadowLooperExecutor \
com.android.launcher3.shadows.ShadowMainThreadInitializedObject \
com.android.launcher3.shadows.ShadowOverrides \
com.android.launcher3.shadows.ShadowSurfaceTransactionApplier \
-application=com.android.launcher3.util.LauncherTestApplication
\ No newline at end of file
+application=com.android.launcher3.util.LauncherTestApplication
diff --git a/robolectric_tests/resources/widgets_predication_update_task_data.txt b/robolectric_tests/resources/widgets_predication_update_task_data.txt
new file mode 100644
index 0000000..941d195
--- /dev/null
+++ b/robolectric_tests/resources/widgets_predication_update_task_data.txt
@@ -0,0 +1,24 @@
+# Model data used by WidgetsPredictionUpdateTasksTest
+
+classMap s com.android.launcher3.model.data.WorkspaceItemInfo
+classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
+
+# Items for the BgDataModel
+
+# App shortcuts
+bgItem s itemType=0 title=app1-class1 intent=component=app1/class1 id=1
+bgItem s itemType=0 title=app1-class2 intent=component=app1/class2 id=2
+bgItem s itemType=0 title=app2-class1 intent=component=app2/class1 id=3
+bgItem s itemType=0 title=app2-class2 intent=component=app2/class2 id=4
+
+# Promise icons for app3
+bgItem s itemType=0 status=2 title=app3-class1 intent=component=app3/class1 id=5
+bgItem s itemType=0 status=2 title=app3-class2 intent=component=app3/class2 id=6
+bgItem s itemType=1 status=1 title=app3-shrt intent=component=app3/class3 id=7
+
+# Promise icon for app4
+bgItem s itemType=1 status=1 title=app4-shrt intent=component=app4/class1 id=8
+
+# Widget
+bgItem w providerName=app4/provider1 id=9
+bgItem w providerName=app5/provider1 id=10
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index b7ba106..2a94d9b 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -25,16 +25,20 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.util.Executors;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
public final class FolderNameProviderTest {
private Context mContext;
private WorkspaceItemInfo mItem1;
@@ -58,18 +62,20 @@
}
@Test
- public void getSuggestedFolderName_workAssignedToEnd() {
+ public void getSuggestedFolderName_workAssignedToEnd() throws Exception {
ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
list.add(mItem1);
list.add(mItem2);
FolderNameInfos nameInfos = new FolderNameInfos();
- new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+ Executors.MODEL_EXECUTOR.submit(() ->
+ new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos)).get();
assertEquals("Work", nameInfos.getLabels()[0]);
nameInfos.setLabel(0, "candidate1", 1.0f);
nameInfos.setLabel(1, "candidate2", 1.0f);
nameInfos.setLabel(2, "candidate3", 1.0f);
- new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+ Executors.MODEL_EXECUTOR.submit(() ->
+ new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos)).get();
assertEquals("Work", nameInfos.getLabels()[3]);
assertTrue(nameInfos.hasSuggestions());
assertTrue(nameInfos.hasPrimary());
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index c892618..01b23ba 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -9,8 +9,8 @@
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.util.Scheduler;
+import org.robolectric.annotation.LooperMode;
+import org.robolectric.annotation.LooperMode.Mode;
import java.io.File;
import java.io.PrintWriter;
@@ -21,11 +21,10 @@
* Tests for {@link FileLog}
*/
@RunWith(RobolectricTestRunner.class)
+@LooperMode(Mode.PAUSED)
public class FileLogTest {
private File mTempDir;
- private boolean mTestActive;
-
@Before
public void setUp() {
int count = 0;
@@ -35,14 +34,6 @@
} while (!mTempDir.mkdir());
FileLog.setDir(mTempDir);
-
- mTestActive = true;
- Scheduler scheduler = Shadows.shadowOf(FileLog.getHandler().getLooper()).getScheduler();
- new Thread(() -> {
- while (mTestActive) {
- scheduler.advanceToLastPostedRunnable();
- }
- }).start();
}
@After
@@ -52,8 +43,6 @@
new File(mTempDir, "log-" + i).delete();
}
mTempDir.delete();
-
- mTestActive = false;
}
@Test
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 90313ab..8baf5a3 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -41,7 +41,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.shadows.LShadowBackupManager;
-import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.util.LauncherModelHelper;
import org.junit.Before;
@@ -51,6 +50,7 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowUserManager;
/**
* Tests to verify backup and restore flow.
@@ -68,7 +68,7 @@
private static final int FLAG_SYSTEM = 0x00000800;
private static final int FLAG_PROFILE = 0x00001000;
- private LShadowUserManager mUserManager;
+ private ShadowUserManager mUserManager;
private BackupManager mBackupManager;
private LauncherModelHelper mModelHelper;
private SQLiteDatabase mDb;
diff --git a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
index 5610b0e..9ac3fe7 100644
--- a/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/CacheDataUpdatedTaskTest.java
@@ -53,10 +53,10 @@
mModelHelper = new LauncherModelHelper();
mModelHelper.initializeData("/cache_data_updated_task_data.txt");
- // Add dummy entries in the cache to simulate update
+ // Add placeholder entries in the cache to simulate update
Context context = RuntimeEnvironment.application;
IconCache iconCache = LauncherAppState.getInstance(context).getIconCache();
- CachingLogic<ItemInfo> dummyLogic = new CachingLogic<ItemInfo>() {
+ CachingLogic<ItemInfo> placeholderLogic = new CachingLogic<ItemInfo>() {
@Override
public ComponentName getComponent(ItemInfo info) {
return info.getTargetComponent();
@@ -81,7 +81,7 @@
UserManager um = context.getSystemService(UserManager.class);
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
- iconCache.addIconToDBAndMemCache(info, dummyLogic, new PackageInfo(),
+ iconCache.addIconToDBAndMemCache(info, placeholderLogic, new PackageInfo(),
um.getSerialNumberForUser(info.user), true);
}
}
diff --git a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
index bbbe21e..be03c7d 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -165,7 +165,7 @@
@Override
public void onOpen(SQLiteDatabase db) { }
};
- // Insert dummy data
+ // Insert mock data
for (int i = 0; i < 10; i++) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, i);
diff --git a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
index 8f3a83e..655237d 100644
--- a/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/DefaultLayoutProviderTest.java
@@ -109,7 +109,7 @@
public void testCustomProfileLoaded_with_widget() throws Exception {
String pendingAppPkg = "com.test.pending";
- // Add a dummy session info so that the widget exists
+ // Add a placeholder session info so that the widget exists
SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
params.setAppPackageName(pendingAppPkg);
@@ -120,7 +120,7 @@
setField(sessionInfo, "appIcon", BitmapInfo.LOW_RES_ICON);
writeLayoutAndLoad(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
- .putWidget(pendingAppPkg, "DummyWidget", 2, 2));
+ .putWidget(pendingAppPkg, "PlaceholderWidget", 2, 2));
// Verify widget
assertEquals(1, mModelHelper.getBgDataModel().appWidgets.size());
diff --git a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 3a252dc..fb08c56 100644
--- a/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -51,7 +51,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
@@ -92,9 +92,9 @@
SCREEN, CELLX, CELLY, RESTORED, INTENT
});
- mLoaderCursor = new LoaderCursor(mCursor, LauncherSettings.Favorites.CONTENT_URI, mApp,
- new UserManagerState());
- mLoaderCursor.allUsers.put(0, Process.myUserHandle());
+ UserManagerState ums = new UserManagerState();
+ mLoaderCursor = new LoaderCursor(mCursor, Favorites.CONTENT_URI, mApp, ums);
+ ums.allUsers.put(0, Process.myUserHandle());
}
private void initCursor(int itemType, String title) {
@@ -110,7 +110,7 @@
public void getAppShortcutInfo_dontAllowMissing_invalidComponent() {
initCursor(ITEM_TYPE_APPLICATION, "");
assertTrue(mLoaderCursor.moveToNext());
- ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+ ComponentName cn = new ComponentName(mContext.getPackageName(), "placeholder-do");
assertNull(mLoaderCursor.getAppShortcutInfo(
new Intent().setComponent(cn), false /* allowMissingTarget */, true));
}
@@ -136,7 +136,7 @@
initCursor(ITEM_TYPE_APPLICATION, "");
assertTrue(mLoaderCursor.moveToNext());
- ComponentName cn = new ComponentName(mContext.getPackageName(), "dummy-do");
+ ComponentName cn = new ComponentName(mContext.getPackageName(), "placeholder-do");
WorkspaceItemInfo info = Executors.MODEL_EXECUTOR.submit(() ->
mLoaderCursor.getAppShortcutInfo(
new Intent().setComponent(cn), true /* allowMissingTarget */, true))
diff --git a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
index 87fe3c0..aab6c25 100644
--- a/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/ModelMultiCallbacksTest.java
@@ -30,6 +30,7 @@
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.shadows.ShadowLooperExecutor;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
@@ -43,8 +44,8 @@
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
+import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Arrays;
@@ -74,7 +75,8 @@
// Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread,
// so that we can wait appropriately for the loader to complete.
mTempMainExecutor = new LooperExecutor(createAndStartNewForegroundLooper("tempMain"));
- ReflectionHelpers.setField(mModelHelper.getModel(), "mMainExecutor", mTempMainExecutor);
+ ShadowLooperExecutor sle = Shadow.extract(Executors.MAIN_EXECUTOR);
+ sle.setHandler(mTempMainExecutor.getHandler());
}
@Test
diff --git a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index e43df21..412ace0 100644
--- a/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -67,7 +67,7 @@
for (ItemInfo info : mModelHelper.getBgDataModel().itemsIdMap) {
if (info instanceof WorkspaceItemInfo) {
assertEquals(updates.contains(info.id) ? progress: 0,
- ((WorkspaceItemInfo) info).getInstallProgress());
+ ((WorkspaceItemInfo) info).getProgressLevel());
} else {
assertEquals(updates.contains(info.id) ? progress: -1,
((LauncherAppWidgetInfo) info).installProgress);
diff --git a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
index ee73b82..4184d33 100644
--- a/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
+++ b/robolectric_tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -44,7 +44,7 @@
@Test
public void testMigrateProfileId() throws Exception {
SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
- // Add some dummy data
+ // Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, i);
@@ -64,7 +64,7 @@
@Test
public void testChangeDefaultColumn() throws Exception {
SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
- // Add some dummy data
+ // Add some mock data
for (int i = 0; i < 5; i++) {
ContentValues values = new ContentValues();
values.put(Favorites._ID, i);
diff --git a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
index 0ca5ce6..e3694ae 100644
--- a/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
+++ b/robolectric_tests/src/com/android/launcher3/secondarydisplay/SDWorkModeTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
import android.content.Context;
import android.os.UserManager;
@@ -30,8 +29,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.allapps.AllAppsPagedView;
import com.android.launcher3.allapps.AllAppsRecyclerView;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
@@ -62,20 +59,15 @@
private InvariantDeviceProfile mIdp;
private LauncherModelHelper mModelHelper;
- private LauncherLayoutBuilder mLayoutBuilder;
-
@Before
public void setup() throws Exception {
mModelHelper = new LauncherModelHelper();
mTargetContext = RuntimeEnvironment.application;
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
- ShadowOverrides.setProvider(UserEventDispatcher.class,
- c -> mock(UserEventDispatcher.class));
Settings.Global.putFloat(mTargetContext.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 0);
mModelHelper.installApp(TEST_PACKAGE);
- mLayoutBuilder = new LauncherLayoutBuilder();
}
@Test
@@ -91,7 +83,7 @@
public void testAllAppsList_workProfile() throws Exception {
ShadowUserManager sum = Shadow.extract(mTargetContext.getSystemService(UserManager.class));
sum.addUser(SYSTEM_USER, "me", FLAG_SYSTEM);
- sum.addUser(WORK_PROFILE_ID, "work", FLAG_PROFILE);
+ sum.addProfile(SYSTEM_USER, WORK_PROFILE_ID, "work", FLAG_PROFILE);
SecondaryDisplayLauncher launcher = loadLauncher();
launcher.showAppDrawer(true);
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
deleted file mode 100644
index da8b919..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowApplicationPackageManager.java
+++ /dev/null
@@ -1,36 +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.launcher3.shadows;
-
-import android.os.Process;
-import android.os.UserHandle;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowApplicationPackageManager;
-
-/**
- * Shadow for {@link ShadowApplicationPackageManager} which create mock predictors
- */
-@Implements(className = "android.app.ApplicationPackageManager")
-public class LShadowApplicationPackageManager extends ShadowApplicationPackageManager {
-
- @Implementation
- public CharSequence getUserBadgedLabel(CharSequence label, UserHandle user) {
- return Process.myUserHandle().equals(user) ? label : "Work " + label;
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java
deleted file mode 100644
index abd90bb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowBitmap.java
+++ /dev/null
@@ -1,36 +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.shadows;
-
-import android.graphics.Bitmap;
-import android.graphics.Paint;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowBitmap;
-
-/**
- * Extension of {@link ShadowBitmap} with missing shadow methods
- */
-@Implements(value = Bitmap.class)
-public class LShadowBitmap extends ShadowBitmap {
-
- @Implementation
- protected Bitmap extractAlpha(Paint paint, int[] offsetXY) {
- return extractAlpha();
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
deleted file mode 100644
index 0e7c1de..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowTypeface.java
+++ /dev/null
@@ -1,38 +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.launcher3.shadows;
-
-import android.graphics.Typeface;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowTypeface;
-
-/**
- * Extension of {@link ShadowTypeface} with missing shadow methods
- */
-@Implements(Typeface.class)
-public class LShadowTypeface extends ShadowTypeface {
-
- @Implementation
- public static Typeface create(Typeface family, int weight, boolean italic) {
- int style = italic ? Typeface.ITALIC : Typeface.NORMAL;
- if (weight >= 400) {
- style |= Typeface.BOLD;
- }
- return create(family, style);
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
deleted file mode 100644
index edf8edb..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowUserManager.java
+++ /dev/null
@@ -1,53 +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.shadows;
-
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.SparseBooleanArray;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowUserManager;
-
-/**
- * Extension of {@link ShadowUserManager} with missing shadow methods
- */
-@Implements(value = UserManager.class)
-public class LShadowUserManager extends ShadowUserManager {
-
- private final SparseBooleanArray mQuietUsers = new SparseBooleanArray();
- private final SparseBooleanArray mLockedUsers = new SparseBooleanArray();
-
- @Implementation
- protected boolean isQuietModeEnabled(UserHandle userHandle) {
- return mQuietUsers.get(userHandle.hashCode());
- }
-
- public void setQuietModeEnabled(UserHandle userHandle, boolean enabled) {
- mQuietUsers.put(userHandle.hashCode(), enabled);
- }
-
- @Implementation
- protected boolean isUserUnlocked(UserHandle userHandle) {
- return !mLockedUsers.get(userHandle.hashCode());
- }
-
- public void setUserLocked(UserHandle userHandle, boolean enabled) {
- mLockedUsers.put(userHandle.hashCode(), enabled);
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java b/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
deleted file mode 100644
index d60251c..0000000
--- a/robolectric_tests/src/com/android/launcher3/shadows/LShadowWallpaperManager.java
+++ /dev/null
@@ -1,56 +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.shadows;
-
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowApplication;
-import org.robolectric.shadows.ShadowUserManager;
-import org.robolectric.shadows.ShadowWallpaperManager;
-
-/**
- * Extension of {@link ShadowUserManager} with missing shadow methods
- */
-@Implements(WallpaperManager.class)
-public class LShadowWallpaperManager extends ShadowWallpaperManager {
-
- @Implementation
- protected static WallpaperManager getInstance(Context context) {
- return context.getSystemService(WallpaperManager.class);
- }
-
- /**
- * Remove this once the fix for
- * https://github.com/robolectric/robolectric/issues/5285
- * is available
- */
- public static void initializeMock() {
- WallpaperManager wm = mock(WallpaperManager.class);
- ShadowApplication shadowApplication = Shadows.shadowOf(RuntimeEnvironment.application);
- shadowApplication.setSystemService(Context.WALLPAPER_SERVICE, wm);
- doReturn(0).when(wm).getDesiredMinimumWidth();
- doReturn(0).when(wm).getDesiredMinimumHeight();
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
index a3b7dc7..57eda7e 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowLooperExecutor.java
@@ -23,6 +23,8 @@
import android.os.Handler;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.util.LooperExecutor;
import org.robolectric.annotation.Implementation;
@@ -37,8 +39,13 @@
@RealObject private LooperExecutor mRealExecutor;
+ private Handler mOverriddenHandler;
+
@Implementation
protected Handler getHandler() {
+ if (mOverriddenHandler != null) {
+ return mOverriddenHandler;
+ }
Handler handler = directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
Thread thread = handler.getLooper().getThread();
if (!thread.isAlive()) {
@@ -49,4 +56,8 @@
}
return directlyOn(mRealExecutor, LooperExecutor.class, "getHandler");
}
+
+ public void setHandler(@Nullable Handler handler) {
+ mOverriddenHandler = handler;
+ }
}
diff --git a/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java b/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java
new file mode 100644
index 0000000..dbf4b3e
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/testing/TestActivity.java
@@ -0,0 +1,41 @@
+/*
+ * 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.testing;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
+
+/** An empty activity for {@link android.app.Fragment}s, {@link android.view.View}s testing. */
+public class TestActivity extends BaseActivity implements ActivityContext {
+
+ private DeviceProfile mDeviceProfile;
+
+ @Override
+ public BaseDragLayer getDragLayer() {
+ return null;
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mDeviceProfile;
+ }
+
+ public void setDeviceProfile(DeviceProfile deviceProfile) {
+ mDeviceProfile = deviceProfile;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
index d330d10..34cb2ad 100644
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import static org.mockito.Mockito.mock;
import android.content.Context;
import android.os.SystemClock;
@@ -36,12 +35,10 @@
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.FolderPagedView;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
import com.android.launcher3.util.LauncherModelHelper;
-import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
import org.junit.Before;
import org.junit.Test;
@@ -70,8 +67,6 @@
mModelHelper = new LauncherModelHelper();
mTargetContext = RuntimeEnvironment.application;
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
- ShadowOverrides.setProvider(UserEventDispatcher.class,
- c -> mock(UserEventDispatcher.class));
Settings.Global.putFloat(mTargetContext.getContentResolver(),
Settings.Global.WINDOW_ANIMATION_SCALE, 0);
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 0388087..849f98b 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -48,10 +48,12 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.shadows.ShadowLooperExecutor;
import org.mockito.ArgumentCaptor;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowContentResolver;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.util.ReflectionHelpers;
@@ -81,7 +83,7 @@
public static final int NO__ICON = -1;
public static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
- // Authority for providing a dummy default-workspace-layout data.
+ // Authority for providing a test default-workspace-layout data.
private static final String TEST_PROVIDER_AUTHORITY =
LauncherModelHelper.class.getName().toLowerCase();
private static final int DEFAULT_BITMAP_SIZE = 10;
@@ -252,7 +254,7 @@
}
/**
- * Adds a dummy item in the DB.
+ * Adds a mock item in the DB.
* @param type {@link #APP_ICON} or {@link #SHORTCUT} or >= 2 for
* folder (where the type represents the number of items in the folder).
*/
@@ -310,7 +312,7 @@
}
/**
- * Initializes the DB with dummy elements to represent the provided grid structure.
+ * Initializes the DB with mock elements to represent the provided grid structure.
* @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
* type definitions. The first dimension represents the screens and the next
* two represent the workspace grid.
@@ -347,7 +349,7 @@
}
/**
- * Sets up a dummy provider to load the provided layout by default, next time the layout loads
+ * Sets up a mock provider to load the provided layout by default, next time the layout loads
*/
public LauncherModelHelper setupDefaultLayoutProvider(LauncherLayoutBuilder builder)
throws Exception {
@@ -360,7 +362,7 @@
"launcher3.layout.provider", TEST_PROVIDER_AUTHORITY);
shadowOf(context.getPackageManager())
- .addProviderIfNotPresent(new ComponentName("com.test", "Dummy")).authority =
+ .addProviderIfNotPresent(new ComponentName("com.test", "Mock")).authority =
TEST_PROVIDER_AUTHORITY;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -403,14 +405,16 @@
public void loadModelSync() throws ExecutionException, InterruptedException {
// Since robolectric tests run on main thread, we run the loader-UI calls on a temp thread,
// so that we can wait appropriately for the loader to complete.
- ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.UI_HELPER_EXECUTOR);
+ ShadowLooperExecutor sle = Shadow.extract(Executors.MAIN_EXECUTOR);
+ sle.setHandler(Executors.UI_HELPER_EXECUTOR.getHandler());
Callbacks mockCb = mock(Callbacks.class);
getModel().addCallbacksAndLoad(mockCb);
Executors.MODEL_EXECUTOR.submit(() -> { }).get();
Executors.UI_HELPER_EXECUTOR.submit(() -> { }).get();
- ReflectionHelpers.setField(getModel(), "mMainExecutor", Executors.MAIN_EXECUTOR);
+
+ sle.setHandler(null);
getModel().removeCallbacks(mockCb);
}
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
index 6dd4df8..efac150 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherTestApplication.java
@@ -19,7 +19,6 @@
import android.app.Application;
-import com.android.launcher3.shadows.LShadowWallpaperManager;
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -37,9 +36,6 @@
// Disable plugins
PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
-
- // Initialize mock wallpaper manager
- LShadowWallpaperManager.initializeMock();
}
@Override
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
index f019a20..fdddab4 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
@@ -18,6 +18,8 @@
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.launcher3.Utilities.createHomeIntent;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -46,10 +48,7 @@
*/
public static String getLauncherClassName() {
Context context = RuntimeEnvironment.application;
- Intent homeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(context.getPackageName())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent homeIntent = createHomeIntent().setPackage(context.getPackageName());
List<ResolveInfo> launchers = context.getPackageManager()
.queryIntentActivities(homeIntent, 0);
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
new file mode 100644
index 0000000..92f77f2
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.graphics.Point;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public final class LauncherAppWidgetProviderInfoTest {
+
+ private static final int CELL_SIZE = 50;
+
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ }
+
+ @Test
+ public void initSpans_minWidthSmallerThanCellWidth_shouldInitializeSpansToOne() {
+ LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+ info.minWidth = 20;
+ info.minHeight = 20;
+ InvariantDeviceProfile idp = createIDP();
+
+ info.initSpans(mContext, idp);
+
+ assertThat(info.spanX).isEqualTo(1);
+ assertThat(info.spanY).isEqualTo(1);
+ }
+
+ @Test
+ public void initSpans_minWidthLargerThanCellWidth_shouldInitializeSpans() {
+ LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+ info.minWidth = 80;
+ info.minHeight = 80;
+ InvariantDeviceProfile idp = createIDP();
+
+ info.initSpans(mContext, idp);
+
+ assertThat(info.spanX).isEqualTo(2);
+ assertThat(info.spanY).isEqualTo(2);
+ }
+
+ @Test
+ public void initSpans_minResizeWidthUnspecified_shouldInitializeMinSpansToOne() {
+ LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+ InvariantDeviceProfile idp = createIDP();
+
+ info.initSpans(mContext, idp);
+
+ assertThat(info.minSpanX).isEqualTo(1);
+ assertThat(info.minSpanY).isEqualTo(1);
+ }
+
+ @Test
+ public void initSpans_minResizeWidthSmallerThanCellWidth_shouldInitializeMinSpansToOne() {
+ LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+ info.minResizeWidth = 20;
+ info.minResizeHeight = 20;
+ InvariantDeviceProfile idp = createIDP();
+
+ info.initSpans(mContext, idp);
+
+ assertThat(info.minSpanX).isEqualTo(1);
+ assertThat(info.minSpanY).isEqualTo(1);
+ }
+
+ @Test
+ public void initSpans_minResizeWidthLargerThanCellWidth_shouldInitializeMinSpans() {
+ LauncherAppWidgetProviderInfo info = new LauncherAppWidgetProviderInfo();
+ info.minResizeWidth = 80;
+ info.minResizeHeight = 80;
+ InvariantDeviceProfile idp = createIDP();
+
+ info.initSpans(mContext, idp);
+
+ assertThat(info.minSpanX).isEqualTo(2);
+ assertThat(info.minSpanY).isEqualTo(2);
+ }
+
+ private InvariantDeviceProfile createIDP() {
+ DeviceProfile profile = Mockito.mock(DeviceProfile.class);
+ Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
+
+ InvariantDeviceProfile idp = new InvariantDeviceProfile();
+ idp.landscapeProfile = idp.portraitProfile = profile;
+ return idp;
+ }
+
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
deleted file mode 100644
index 84c65b1..0000000
--- a/robolectric_tests/src/com/android/launcher3/widget/WidgetsListAdapterTest.java
+++ /dev/null
@@ -1,151 +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.widget;
-
-import static org.mockito.Matchers.eq;
-import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.view.LayoutInflater;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.PackageItemInfo;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.shadows.ShadowPackageManager;
-import org.robolectric.util.ReflectionHelpers;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
-@RunWith(RobolectricTestRunner.class)
-public class WidgetsListAdapterTest {
-
- @Mock private LayoutInflater mMockLayoutInflater;
- @Mock private WidgetPreviewLoader mMockWidgetCache;
- @Mock private RecyclerView.AdapterDataObserver mListener;
- @Mock private IconCache mIconCache;
-
- private WidgetsListAdapter mAdapter;
- private InvariantDeviceProfile mTestProfile;
- private Context mContext;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mTestProfile = new InvariantDeviceProfile();
- mTestProfile.numRows = 5;
- mTestProfile.numColumns = 5;
- mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
- mIconCache, null, null);
- mAdapter.registerAdapterDataObserver(mListener);
- }
-
- @Test
- public void test_notifyDataSetChanged() throws Exception {
- mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).onChanged();
- }
-
- @Test
- public void test_notifyItemInserted() throws Exception {
- mAdapter.setWidgets(generateSampleMap(1));
- mAdapter.setWidgets(generateSampleMap(2));
- verify(mListener, times(1)).onChanged();
- verify(mListener, times(1)).onItemRangeInserted(eq(1), eq(1));
- }
-
- @Test
- public void test_notifyItemRemoved() throws Exception {
- mAdapter.setWidgets(generateSampleMap(2));
- mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).onChanged();
- verify(mListener, times(1)).onItemRangeRemoved(eq(1), eq(1));
- }
-
- @Test
- public void testNotifyItemChanged_PackageIconDiff() throws Exception {
- mAdapter.setWidgets(generateSampleMap(1));
- mAdapter.setWidgets(generateSampleMap(1));
- verify(mListener, times(1)).onChanged();
- verify(mListener, times(1)).onItemRangeChanged(eq(0), eq(1), isNull());
- }
-
- @Test
- public void testNotifyItemChanged_widgetItemInfoDiff() throws Exception {
- // TODO: same package name but item number changed
- }
-
- @Test
- public void testNotifyItemInsertedRemoved_hodgepodge() throws Exception {
- // TODO: insert and remove combined. curMap
- // newMap [A, C, D] [A, B, E]
- // B - C < 0, removed B from index 1 [A, E]
- // E - C > 0, C inserted to index 1 [A, C, E]
- // E - D > 0, D inserted to index 2 [A, C, D, E]
- // E - null = -1, E deleted from index 3 [A, C, D]
- }
-
- /**
- * Helper method to generate the sample widget model map that can be used for the tests
- * @param num the number of WidgetItem the map should contain
- */
- private ArrayList<WidgetListRowEntry> generateSampleMap(int num) {
- ArrayList<WidgetListRowEntry> result = new ArrayList<>();
- if (num <= 0) return result;
- ShadowPackageManager spm = shadowOf(mContext.getPackageManager());
-
- for (int i = 0; i < num; i++) {
- ComponentName cn = new ComponentName("com.dummy.apk" + i, "DummyWidet");
-
- AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
- widgetInfo.provider = cn;
- ReflectionHelpers.setField(widgetInfo, "providerInfo", spm.addReceiverIfNotPresent(cn));
-
- WidgetItem wi = new WidgetItem(LauncherAppWidgetProviderInfo
- .fromProviderInfo(mContext, widgetInfo), mTestProfile, mIconCache);
-
- PackageItemInfo pInfo = new PackageItemInfo(wi.componentName.getPackageName());
- pInfo.title = pInfo.packageName;
- pInfo.user = wi.user;
- pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
-
- result.add(new WidgetListRowEntry(pInfo, new ArrayList<>(Collections.singleton(wi))));
- }
-
- return result;
- }
-}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
new file mode 100644
index 0000000..b972c6f
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsDiffReporterTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+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.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsDiffReporterTest {
+ private static final String TEST_PACKAGE_PREFIX = "com.google.test";
+ private static final WidgetListBaseRowEntryComparator COMPARATOR =
+ new WidgetListBaseRowEntryComparator();
+
+ @Mock private IconCache mIconCache;
+ @Mock private RecyclerView.Adapter mAdapter;
+
+ private InvariantDeviceProfile mTestProfile;
+ private WidgetsDiffReporter mWidgetsDiffReporter;
+ private Context mContext;
+ private WidgetsListHeaderEntry mHeaderA;
+ private WidgetsListHeaderEntry mHeaderB;
+ private WidgetsListHeaderEntry mHeaderC;
+ private WidgetsListHeaderEntry mHeaderD;
+ private WidgetsListHeaderEntry mHeaderE;
+ private WidgetsListContentEntry mContentC;
+ private WidgetsListContentEntry mContentE;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+
+ doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+ .getComponent().getPackageName())
+ .when(mIconCache).getTitleNoCache(any());
+
+ mContext = RuntimeEnvironment.application;
+ mWidgetsDiffReporter = new WidgetsDiffReporter(mIconCache, mAdapter);
+ mHeaderA = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A",
+ /* appName= */ "A", /* numOfWidgets= */ 3);
+ mHeaderB = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "B",
+ /* appName= */ "B", /* numOfWidgets= */ 3);
+ mHeaderC = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "C",
+ /* appName= */ "C", /* numOfWidgets= */ 3);
+ mContentC = createWidgetsContentEntry(TEST_PACKAGE_PREFIX + "C",
+ /* appName= */ "C", /* numOfWidgets= */ 3);
+ mHeaderD = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "D",
+ /* appName= */ "D", /* numOfWidgets= */ 3);
+ mHeaderE = createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "E",
+ /* appName= */ "E", /* numOfWidgets= */ 3);
+ mContentE = createWidgetsContentEntry(TEST_PACKAGE_PREFIX + "E",
+ /* appName= */ "E", /* numOfWidgets= */ 3);
+ }
+
+ @Test
+ public void listNotChanged_shouldNotInvokeAnyCallbacks() {
+ // GIVEN the current list has app headers [A, B, C].
+ ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+ List.of(mHeaderA, mHeaderB, mHeaderC));
+
+ // WHEN computing the list difference.
+ mWidgetsDiffReporter.process(currentList, currentList, COMPARATOR);
+
+ // THEN there is no adaptor callback.
+ verifyZeroInteractions(mAdapter);
+ // THEN the current list contains the same entries.
+ assertThat(currentList).containsExactly(mHeaderA, mHeaderB, mHeaderC);
+ }
+
+ @Test
+ public void headersOnly_emptyListToNonEmpty_shouldInvokeNotifyDataSetChanged() {
+ // GIVEN the current list has app headers [A, B, C].
+ ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>();
+
+ List<WidgetsListBaseEntry> newList = List.of(
+ createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "A", "A", 3),
+ createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "B", "B", 3),
+ createWidgetsHeaderEntry(TEST_PACKAGE_PREFIX + "C", "C", 3));
+
+ // WHEN computing the list difference.
+ mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+ // THEN notifyDataSetChanged is called
+ verify(mAdapter).notifyDataSetChanged();
+ // THEN the current list contains all elements from the new list.
+ assertThat(currentList).containsExactlyElementsIn(newList);
+ }
+
+ @Test
+ public void headersOnly_nonEmptyToEmptyList_shouldInvokeNotifyDataSetChanged() {
+ // GIVEN the current list has app headers [A, B, C].
+ ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+ List.of(mHeaderA, mHeaderB, mHeaderC));
+ // GIVEN the new list is empty.
+ List<WidgetsListBaseEntry> newList = List.of();
+
+ // WHEN computing the list difference.
+ mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+ // THEN notifyDataSetChanged is called.
+ verify(mAdapter).notifyDataSetChanged();
+ // THEN the current list isEmpty.
+ assertThat(currentList).isEmpty();
+ }
+
+ @Test
+ public void headersOnly_itemAddedAndRemovedInTheNewList_shouldInvokeCorrectCallbacks() {
+ // GIVEN the current list has app headers [A, B, D].
+ ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+ List.of(mHeaderA, mHeaderB, mHeaderD));
+ // GIVEN the new list has app headers [A, C, E].
+ List<WidgetsListBaseEntry> newList = List.of(mHeaderA, mHeaderC, mHeaderE);
+
+ // WHEN computing the list difference.
+ mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+ // THEN "B" is removed from position 1.
+ verify(mAdapter).notifyItemRemoved(/* position= */ 1);
+ // THEN "D" is removed from position 2.
+ verify(mAdapter).notifyItemRemoved(/* position= */ 2);
+ // THEN "C" is inserted at position 1.
+ verify(mAdapter).notifyItemInserted(/* position= */ 1);
+ // THEN "E" is inserted at position 2.
+ verify(mAdapter).notifyItemInserted(/* position= */ 2);
+ // THEN the current list contains all elements from the new list.
+ assertThat(currentList).containsExactlyElementsIn(newList);
+ }
+
+ @Test
+ public void headersContentsMix_itemAddedAndRemovedInTheNewList_shouldInvokeCorrectCallbacks() {
+ // GIVEN the current list has app headers [A, B, E content].
+ ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+ List.of(mHeaderA, mHeaderB, mContentE));
+ // GIVEN the new list has app headers [A, C content, D].
+ List<WidgetsListBaseEntry> newList = List.of(mHeaderA, mContentC, mHeaderD);
+
+ // WHEN computing the list difference.
+ mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+ // THEN "B" is removed from position 1.
+ verify(mAdapter).notifyItemRemoved(/* position= */ 1);
+ // THEN "C content" is inserted at position 1.
+ verify(mAdapter).notifyItemInserted(/* position= */ 1);
+ // THEN "D" is inserted at position 2.
+ verify(mAdapter).notifyItemInserted(/* position= */ 2);
+ // THEN "E content" is removed from position 3.
+ verify(mAdapter).notifyItemRemoved(/* position= */ 3);
+ // THEN the current list contains all elements from the new list.
+ assertThat(currentList).containsExactlyElementsIn(newList);
+ }
+
+ @Test
+ public void headersContentsMix_userInteractWithHeader_shouldInvokeCorrectCallbacks() {
+ // GIVEN the current list has app headers [A, B, E content].
+ ArrayList<WidgetsListBaseEntry> currentList = new ArrayList<>(
+ List.of(mHeaderA, mHeaderB, mContentE));
+ // GIVEN the new list has app headers [A, B, E content].
+ List<WidgetsListBaseEntry> newList = List.of(mHeaderA, mHeaderB, mContentE);
+ // GIVEN the user has interacted with B.
+ mHeaderB.setIsWidgetListShown(true);
+
+ // WHEN computing the list difference.
+ mWidgetsDiffReporter.process(currentList, newList, COMPARATOR);
+
+ // THEN notify "B" has been changed.
+ verify(mAdapter).notifyItemChanged(/* position= */ 1);
+ // THEN the current list contains all elements from the new list.
+ assertThat(currentList).containsExactlyElementsIn(newList);
+ }
+
+
+ private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName,
+ int numOfWidgets) {
+ List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+ PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+ widgetItems.get(0).user);
+
+ return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+ }
+
+ private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
+ int numOfWidgets) {
+ List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+ PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+ widgetItems.get(0).user);
+
+ return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+ }
+
+ private PackageItemInfo createPackageItemInfo(String packageName, String appName,
+ UserHandle userHandle) {
+ PackageItemInfo pInfo = new PackageItemInfo(packageName);
+ pInfo.title = appName;
+ pInfo.user = userHandle;
+ pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+ return pInfo;
+ }
+
+ private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+ for (int i = 0; i < numOfWidgets; i++) {
+ ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
+ AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+ widgetInfo.provider = cn;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(cn));
+
+ WidgetItem widgetItem = new WidgetItem(
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+ mTestProfile, mIconCache);
+ widgetItems.add(widgetItem);
+ }
+ return widgetItems;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
new file mode 100644
index 0000000..a7c8d92
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -0,0 +1,252 @@
+/*
+ * 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.widget.picker;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsListAdapterTest {
+ private static final String TEST_PACKAGE_PLACEHOLDER = "com.google.test";
+
+ @Mock private LayoutInflater mMockLayoutInflater;
+ @Mock private WidgetPreviewLoader mMockWidgetCache;
+ @Mock private RecyclerView.AdapterDataObserver mListener;
+ @Mock private IconCache mIconCache;
+
+ private WidgetsListAdapter mAdapter;
+ private InvariantDeviceProfile mTestProfile;
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+ mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater, mMockWidgetCache,
+ mIconCache, null, null);
+ mAdapter.registerAdapterDataObserver(mListener);
+
+ doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+ .getComponent().getPackageName())
+ .when(mIconCache).getTitleNoCache(any());
+ }
+
+ @Test
+ public void setWidgets_shouldNotifyDataSetChanged() {
+ mAdapter.setWidgets(generateSampleMap(1));
+
+ verify(mListener).onChanged();
+ }
+
+ @Test
+ public void setWidgets_withItemInserted_shouldNotifyItemInserted() {
+ mAdapter.setWidgets(generateSampleMap(1));
+ mAdapter.setWidgets(generateSampleMap(2));
+
+ verify(mListener).onItemRangeInserted(eq(1), eq(1));
+ }
+
+ @Test
+ public void setWidgets_withItemRemoved_shouldNotifyItemRemoved() {
+ mAdapter.setWidgets(generateSampleMap(2));
+ mAdapter.setWidgets(generateSampleMap(1));
+
+ verify(mListener).onItemRangeRemoved(eq(1), eq(1));
+ }
+
+ @Test
+ public void setWidgets_appIconChanged_shouldNotifyItemChanged() {
+ mAdapter.setWidgets(generateSampleMap(1));
+ mAdapter.setWidgets(generateSampleMap(1));
+
+ verify(mListener).onItemRangeChanged(eq(0), eq(1), isNull());
+ }
+
+ @Test
+ public void headerClick_expanded_shouldNotifyItemChange() {
+ // GIVEN a list of widgets entries:
+ // [com.google.test0, com.google.test0 content,
+ // com.google.test1, com.google.test1 content,
+ // com.google.test2, com.google.test2 content]
+ // The visible widgets entries: [com.google.test0, com.google.test1, com.google.test2].
+ mAdapter.setWidgets(generateSampleMap(3));
+
+ // WHEN com.google.test.1 header is expanded.
+ mAdapter.onHeaderClicked(/* isExpanded= */ true, TEST_PACKAGE_PLACEHOLDER + 1);
+
+ // THEN the visible entries list becomes:
+ // [com.google.test0, com.google.test1, com.google.test1 content, com.google.test2]
+ // com.google.test.1 content is inserted into position 2.
+ verify(mListener).onItemRangeInserted(eq(2), eq(1));
+ }
+
+ @Test
+ public void setWidgets_expandedApp_moreWidgets_shouldNotifyItemChangedWithWidgetItemInfoDiff() {
+ // GIVEN the adapter was first populated with com.google.test0 & com.google.test1. Each app
+ // has one widget.
+ ArrayList<WidgetsListBaseEntry> allEntries = generateSampleMap(2);
+ mAdapter.setWidgets(allEntries);
+ // GIVEN test com.google.test1 is expanded.
+ // Visible entries in the adapter are:
+ // [com.google.test0, com.google.test1, com.google.test1 content]
+ mAdapter.onHeaderClicked(/* isExpanded= */ true, TEST_PACKAGE_PLACEHOLDER + 1);
+ Mockito.reset(mListener);
+
+ // WHEN the adapter is updated with the same list of apps but com.google.test1 has 2 widgets
+ // now.
+ WidgetsListContentEntry testPackage1ContentEntry =
+ (WidgetsListContentEntry) allEntries.get(3);
+ WidgetItem widgetItem = testPackage1ContentEntry.mWidgets.get(0);
+ WidgetsListContentEntry newTestPackage1ContentEntry = new WidgetsListContentEntry(
+ testPackage1ContentEntry.mPkgItem,
+ testPackage1ContentEntry.mTitleSectionName, List.of(widgetItem, widgetItem));
+ allEntries.set(3, newTestPackage1ContentEntry);
+ mAdapter.setWidgets(allEntries);
+
+ // THEN the onItemRangeChanged is invoked for "com.google.test1 content" at index 2.
+ verify(mListener).onItemRangeChanged(eq(2), eq(1), isNull());
+ }
+
+ @Test
+ public void setWidgets_hodgepodge_shouldInvokeExpectedDataObserverCallbacks() {
+ // GIVEN a widgets entry list:
+ // Index: 0| 1 | 2| 3 | 4| 5 | 6| 7 | 8| 9 |
+ // [A, A content, B, B content, C, C content, D, D content, E, E content]
+ List<WidgetsListBaseEntry> allAppsWithWidgets = generateSampleMap(5);
+ // GIVEN the current widgets list consist of [A, A content, B, B content, E, E content].
+ // GIVEN the visible widgets list consist of [A, B, E]
+ List<WidgetsListBaseEntry> currentList = List.of(
+ // A & A content
+ allAppsWithWidgets.get(0), allAppsWithWidgets.get(1),
+ // B & B content
+ allAppsWithWidgets.get(2), allAppsWithWidgets.get(3),
+ // E & E content
+ allAppsWithWidgets.get(8), allAppsWithWidgets.get(9));
+ mAdapter.setWidgets(currentList);
+
+ // WHEN the widgets list is updated to [A, A content, C, C content, D, D content].
+ // WHEN the visible widgets list is updated to [A, C, D].
+ List<WidgetsListBaseEntry> newList = List.of(
+ // A & A content
+ allAppsWithWidgets.get(0), allAppsWithWidgets.get(1),
+ // C & C content
+ allAppsWithWidgets.get(4), allAppsWithWidgets.get(5),
+ // D & D content
+ allAppsWithWidgets.get(6), allAppsWithWidgets.get(7));
+ mAdapter.setWidgets(newList);
+
+ // Computation logic | [Intermediate list during computation]
+ // THEN B <> C < 0, removed B from index 1 | [A, E]
+ verify(mListener).onItemRangeRemoved(/* positionStart= */ 1, /* itemCount= */ 1);
+ // THEN E <> C > 0, C inserted to index 1 | [A, C, E]
+ verify(mListener).onItemRangeInserted(/* positionStart= */ 1, /* itemCount= */ 1);
+ // THEN E <> D > 0, D inserted to index 2 | [A, C, D, E]
+ verify(mListener).onItemRangeInserted(/* positionStart= */ 2, /* itemCount= */ 1);
+ // THEN E <> null = -1, E deleted from index 3 | [A, C, D]
+ verify(mListener).onItemRangeRemoved(/* positionStart= */ 3, /* itemCount= */ 1);
+ }
+
+ /**
+ * Generates a list of sample widget entries.
+ *
+ * <p>Each sample app has 1 widget only. An app is represented by 2 entries,
+ * {@link WidgetsListHeaderEntry} & {@link WidgetsListContentEntry}. Only
+ * {@link WidgetsListHeaderEntry} is always visible in the {@link WidgetsListAdapter}.
+ * {@link WidgetsListContentEntry} is only shown upon clicking the corresponding app's
+ * {@link WidgetsListHeaderEntry}. Only at most one {@link WidgetsListContentEntry} is shown at
+ * a time.
+ *
+ * @param num the number of apps that have widgets.
+ */
+ private ArrayList<WidgetsListBaseEntry> generateSampleMap(int num) {
+ ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
+ if (num <= 0) return result;
+
+ for (int i = 0; i < num; i++) {
+ String packageName = TEST_PACKAGE_PLACEHOLDER + i;
+
+ List<WidgetItem> widgetItems = generateWidgetItems(packageName, /* numOfWidgets= */ 1);
+
+ PackageItemInfo pInfo = new PackageItemInfo(packageName);
+ pInfo.title = pInfo.packageName;
+ pInfo.user = widgetItems.get(0).user;
+ pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+
+ result.add(new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems));
+ result.add(new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems));
+ }
+
+ return result;
+ }
+
+ private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+ for (int i = 0; i < numOfWidgets; i++) {
+ ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
+ AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+ widgetInfo.provider = cn;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(cn));
+
+ widgetItems.add(new WidgetItem(
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+ mTestProfile, mIconCache));
+ }
+ return widgetItems;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
new file mode 100644
index 0000000..848630e
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.testing.TestActivity;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.picker.WidgetsListHeaderViewHolderBinder.OnHeaderClickListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsListHeaderViewHolderBinderTest {
+ private static final String TEST_PACKAGE = "com.google.test";
+ private static final String APP_NAME = "Test app";
+
+ private Context mContext;
+ private WidgetsListHeaderViewHolderBinder mViewHolderBinder;
+ private InvariantDeviceProfile mTestProfile;
+ // Replace ActivityController with ActivityScenario, which is the recommended way for activity
+ // testing.
+ private ActivityController<TestActivity> mActivityController;
+ private TestActivity mTestActivity;
+ private FakeOnHeaderClickListener mFakeOnHeaderClickListener = new FakeOnHeaderClickListener();
+
+ @Mock
+ private IconCache mIconCache;
+ @Mock
+ private DeviceProfile mDeviceProfile;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+
+ mActivityController = Robolectric.buildActivity(TestActivity.class);
+ mTestActivity = mActivityController.setup().get();
+ mTestActivity.setDeviceProfile(mDeviceProfile);
+
+ doAnswer(invocation -> {
+ ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ return componentWithLabel.getComponent().getShortClassName();
+ }).when(mIconCache).getTitleNoCache(any());
+
+ mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
+ LayoutInflater.from(mTestActivity),
+ mFakeOnHeaderClickListener);
+ }
+
+ @After
+ public void tearDown() {
+ mActivityController.destroy();
+ }
+
+ @Test
+ public void bindViewHolder_appWith3Widgets_shouldShowTheCorrectAppNameAndSubtitle() {
+ WidgetsListHeaderHolder viewHolder = mViewHolderBinder.newViewHolder(
+ new FrameLayout(mTestActivity));
+ WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+ WidgetsListHeaderEntry entry = generateSampleAppHeader(
+ APP_NAME,
+ TEST_PACKAGE,
+ /* numOfWidgets= */ 3);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry);
+
+ TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
+ TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
+ assertThat(appTitle.getText()).isEqualTo(APP_NAME);
+ assertThat(appSubtitle.getText()).isEqualTo("3 widgets");
+ }
+
+ private WidgetsListHeaderEntry generateSampleAppHeader(String appName, String packageName,
+ int numOfWidgets) {
+ PackageItemInfo appInfo = new PackageItemInfo(packageName);
+ appInfo.title = appName;
+ appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+
+ return new WidgetsListHeaderEntry(appInfo,
+ /* titleSectionName= */ "",
+ generateWidgetItems(packageName, numOfWidgets));
+ }
+
+ private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+ for (int i = 0; i < numOfWidgets; i++) {
+ ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
+ AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+ widgetInfo.provider = cn;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(cn));
+
+ widgetItems.add(new WidgetItem(
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+ mTestProfile, mIconCache));
+ }
+ return widgetItems;
+ }
+
+ private void assertWidgetCellWithLabel(View view, String label) {
+ assertThat(view).isInstanceOf(WidgetCell.class);
+ TextView widgetLabel = (TextView) view.findViewById(R.id.widget_name);
+ assertThat(widgetLabel.getText()).isEqualTo(label);
+ }
+
+ private final class FakeOnHeaderClickListener implements OnHeaderClickListener {
+
+ boolean mShowWidgets = false;
+ @Nullable String mHeaderClickedPackage = null;
+
+ @Override
+ public void onHeaderClicked(boolean showWidgets, String packageName) {
+ mShowWidgets = showWidgets;
+ mHeaderClickedPackage = packageName;
+ }
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
new file mode 100644
index 0000000..8a0cf34
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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 android.os.Looper.getMainLooper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.FrameLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.testing.TestActivity;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsListTableViewHolderBinderTest {
+ private static final String TEST_PACKAGE = "com.google.test";
+ private static final String APP_NAME = "Test app";
+
+ private Context mContext;
+ private WidgetsListTableViewHolderBinder mViewHolderBinder;
+ private InvariantDeviceProfile mTestProfile;
+ // Replace ActivityController with ActivityScenario, which is the recommended way for activity
+ // testing.
+ private ActivityController<TestActivity> mActivityController;
+ private TestActivity mTestActivity;
+
+ @Mock
+ private OnLongClickListener mOnLongClickListener;
+ @Mock
+ private OnClickListener mOnIconClickListener;
+ @Mock
+ private IconCache mIconCache;
+ @Mock
+ private WidgetPreviewLoader mWidgetPreviewLoader;
+ @Mock
+ private DeviceProfile mDeviceProfile;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+
+ mActivityController = Robolectric.buildActivity(TestActivity.class);
+ mTestActivity = mActivityController.setup().get();
+ mTestActivity.setDeviceProfile(mDeviceProfile);
+
+ doAnswer(invocation -> {
+ ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ return componentWithLabel.getComponent().getShortClassName();
+ }).when(mIconCache).getTitleNoCache(any());
+
+ mViewHolderBinder = new WidgetsListTableViewHolderBinder(
+ mContext,
+ LayoutInflater.from(mTestActivity),
+ mOnIconClickListener,
+ mOnLongClickListener,
+ mWidgetPreviewLoader);
+ }
+
+ @After
+ public void tearDown() {
+ mActivityController.destroy();
+ }
+
+ @Test
+ public void bindViewHolder_appWith3Widgets_shouldHave3Widgets() {
+ WidgetsRowViewHolder viewHolder = mViewHolderBinder.newViewHolder(
+ new FrameLayout(mTestActivity));
+ WidgetsListContentEntry entry = generateSampleAppWithWidgets(
+ APP_NAME,
+ TEST_PACKAGE,
+ /* numOfWidgets= */ 3);
+ mViewHolderBinder.bindViewHolder(viewHolder, entry);
+ shadowOf(getMainLooper()).idle();
+
+ // THEN the table container has one row, which contains 3 widgets.
+ // View: .SampleWidget0 | .SampleWidget1 | .SampleWidget2
+ assertThat(viewHolder.mTableContainer.getChildCount()).isEqualTo(1);
+ TableRow row = (TableRow) viewHolder.mTableContainer.getChildAt(0);
+ assertThat(row.getChildCount()).isEqualTo(3);
+ // Widget 0 label is .SampleWidget0.
+ assertWidgetCellWithLabel(row.getChildAt(0), ".SampleWidget0");
+ // Widget 1 label is .SampleWidget1.
+ assertWidgetCellWithLabel(row.getChildAt(1), ".SampleWidget1");
+ // Widget 2 label is .SampleWidget2.
+ assertWidgetCellWithLabel(row.getChildAt(2), ".SampleWidget2");
+ }
+
+ private WidgetsListContentEntry generateSampleAppWithWidgets(String appName, String packageName,
+ int numOfWidgets) {
+ PackageItemInfo appInfo = new PackageItemInfo(packageName);
+ appInfo.title = appName;
+ appInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+
+ return new WidgetsListContentEntry(appInfo,
+ /* titleSectionName= */ "",
+ generateWidgetItems(packageName, numOfWidgets));
+ }
+
+ private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+ for (int i = 0; i < numOfWidgets; i++) {
+ ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
+ AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+ widgetInfo.provider = cn;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(cn));
+
+ widgetItems.add(new WidgetItem(
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+ mTestProfile, mIconCache));
+ }
+ return widgetItems;
+ }
+
+ private void assertWidgetCellWithLabel(View view, String label) {
+ assertThat(view).isInstanceOf(WidgetCell.class);
+ TextView widgetLabel = (TextView) view.findViewById(R.id.widget_name);
+ assertThat(widgetLabel.getText()).isEqualTo(label);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
new file mode 100644
index 0000000..2d22c45
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/model/WidgetsListContentEntryTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.model;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsListContentEntryTest {
+ private static final String PACKAGE_NAME = "com.google.test";
+ private final PackageItemInfo mPackageItemInfo = new PackageItemInfo(PACKAGE_NAME);
+ private final ComponentName mWidget1 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget1");
+ private final ComponentName mWidget2 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget2");
+ private final ComponentName mWidget3 = ComponentName.createRelative(PACKAGE_NAME, ".mWidget3");
+ private final Map<ComponentName, String> mWidgetsToLabels = new HashMap();
+
+ @Mock private IconCache mIconCache;
+
+ private Context mContext;
+ private InvariantDeviceProfile mTestProfile;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mWidgetsToLabels.put(mWidget1, "Cat");
+ mWidgetsToLabels.put(mWidget2, "Dog");
+ mWidgetsToLabels.put(mWidget3, "Bird");
+
+ mContext = RuntimeEnvironment.application;
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+
+ doAnswer(invocation -> {
+ ComponentWithLabel componentWithLabel = (ComponentWithLabel) invocation.getArgument(0);
+ return mWidgetsToLabels.get(componentWithLabel.getComponent());
+ }).when(mIconCache).getTitleNoCache(any());
+ }
+
+ @Test
+ public void unsortedWidgets_diffLabels_shouldSortWidgetItems() {
+ // GIVEN a list of widgets in unsorted order.
+ // Cat 2x3
+ WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 3);
+ // Dog 2x3
+ WidgetItem widgetItem2 = createWidgetItem(mWidget2, /* spanX= */ 2, /* spanY= */ 3);
+ // Bird 2x3
+ WidgetItem widgetItem3 = createWidgetItem(mWidget3, /* spanX= */ 2, /* spanY= */ 3);
+
+ // WHEN creates a WidgetsListRowEntry with the unsorted widgets.
+ WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(mPackageItemInfo,
+ /* titleSectionName= */ "T",
+ List.of(widgetItem1, widgetItem2, widgetItem3));
+
+ // THEN the widgets list is sorted by their labels alphabetically: [Bird, Cat, Dog].
+ assertThat(widgetsListRowEntry.mWidgets)
+ .containsExactly(widgetItem3, widgetItem1, widgetItem2)
+ .inOrder();
+ assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
+ assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(mPackageItemInfo);
+ }
+
+ @Test
+ public void unsortedWidgets_sameLabels_differentSize_shouldSortWidgetItems() {
+ // GIVEN a list of widgets in unsorted order.
+ // Cat 3x3
+ WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 3, /* spanY= */ 3);
+ // Cat 1x2
+ WidgetItem widgetItem2 = createWidgetItem(mWidget1, /* spanX= */ 1, /* spanY= */ 2);
+ // Cat 2x2
+ WidgetItem widgetItem3 = createWidgetItem(mWidget1, /* spanX= */ 2, /* spanY= */ 2);
+
+ // WHEN creates a WidgetsListRowEntry with the unsorted widgets.
+ WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(mPackageItemInfo,
+ /* titleSectionName= */ "T",
+ List.of(widgetItem1, widgetItem2, widgetItem3));
+
+ // THEN the widgets list is sorted by their gird sizes in an ascending order:
+ // [1x2, 2x2, 3x3].
+ assertThat(widgetsListRowEntry.mWidgets)
+ .containsExactly(widgetItem2, widgetItem3, widgetItem1)
+ .inOrder();
+ assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
+ assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(mPackageItemInfo);
+ }
+
+ @Test
+ public void unsortedWidgets_hodgepodge_shouldSortWidgetItems() {
+ // GIVEN a list of widgets in unsorted order.
+ // Cat 3x3
+ WidgetItem widgetItem1 = createWidgetItem(mWidget1, /* spanX= */ 3, /* spanY= */ 3);
+ // Cat 1x2
+ WidgetItem widgetItem2 = createWidgetItem(mWidget1, /* spanX= */ 1, /* spanY= */ 2);
+ // Dog 2x2
+ WidgetItem widgetItem3 = createWidgetItem(mWidget2, /* spanX= */ 2, /* spanY= */ 2);
+ // Bird 2x2
+ WidgetItem widgetItem4 = createWidgetItem(mWidget3, /* spanX= */ 2, /* spanY= */ 2);
+
+ // WHEN creates a WidgetsListRowEntry with the unsorted widgets.
+ WidgetsListContentEntry widgetsListRowEntry = new WidgetsListContentEntry(mPackageItemInfo,
+ /* titleSectionName= */ "T",
+ List.of(widgetItem1, widgetItem2, widgetItem3, widgetItem4));
+
+ // THEN the widgets list is first sorted by labels alphabetically. Then, for widgets with
+ // same labels, they are sorted by their gird sizes in an ascending order:
+ // [Bird 2x2, Cat 1x2, Cat 3x3, Dog 2x2]
+ assertThat(widgetsListRowEntry.mWidgets)
+ .containsExactly(widgetItem4, widgetItem2, widgetItem1, widgetItem3)
+ .inOrder();
+ assertThat(widgetsListRowEntry.mTitleSectionName).isEqualTo("T");
+ assertThat(widgetsListRowEntry.mPkgItem).isEqualTo(mPackageItemInfo);
+ }
+
+ private WidgetItem createWidgetItem(ComponentName componentName, int spanX, int spanY) {
+ String label = mWidgetsToLabels.get(componentName);
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+ widgetInfo.provider = componentName;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(componentName));
+
+ LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
+ launcherAppWidgetProviderInfo.spanX = spanX;
+ launcherAppWidgetProviderInfo.spanY = spanY;
+ launcherAppWidgetProviderInfo.label = label;
+
+ return new WidgetItem(launcherAppWidgetProviderInfo, mTestProfile, mIconCache);
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
new file mode 100644
index 0000000..c2bf1ae
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithmTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.search;
+
+import static android.os.Looper.getMainLooper;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.matches;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+
+import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@RunWith(RobolectricTestRunner.class)
+public class SimpleWidgetsSearchAlgorithmTest {
+
+ private SimpleWidgetsSearchAlgorithm mSimpleWidgetsSearchAlgorithm;
+ @Mock
+ private WidgetsPickerSearchPipeline mSearchPipeline;
+ @Mock
+ private SearchCallback<WidgetsListBaseEntry> mSearchCallback;
+ @Captor
+ private ArgumentCaptor<Consumer<List<WidgetsListBaseEntry>>> mConsumerCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mSimpleWidgetsSearchAlgorithm = new SimpleWidgetsSearchAlgorithm(mSearchPipeline);
+ }
+
+ @Test
+ public void doSearch_shouldQueryPipeline() {
+ mSimpleWidgetsSearchAlgorithm.doSearch("abc", mSearchCallback);
+
+ verify(mSearchPipeline).query(eq("abc"), any());
+ }
+
+ @Test
+ public void doSearch_shouldInformSearchCallbackOnQueryResult() {
+ ArrayList<WidgetsListBaseEntry> baseEntries = new ArrayList<>();
+
+ mSimpleWidgetsSearchAlgorithm.doSearch("abc", mSearchCallback);
+
+ verify(mSearchPipeline).query(eq("abc"), mConsumerCaptor.capture());
+ mConsumerCaptor.getValue().accept(baseEntries);
+ shadowOf(getMainLooper()).idle();
+ // Verify SearchCallback#onSearchResult receives a query token along with the search
+ // results. The query token is the original query string concatenated with the query
+ // timestamp.
+ verify(mSearchCallback).onSearchResult(matches("abc\t\\d*"), eq(baseEntries));
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipelineTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipelineTest.java
new file mode 100644
index 0000000..8aebf12
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipelineTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.search;
+
+import static android.os.Looper.getMainLooper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class SimpleWidgetsSearchPipelineTest {
+ private static final SimpleWidgetsSearchPipeline.StringMatcher MATCHER =
+ SimpleWidgetsSearchPipeline.StringMatcher.getInstance();
+
+ @Mock private IconCache mIconCache;
+
+ private InvariantDeviceProfile mTestProfile;
+ private WidgetsListHeaderEntry mCalendarHeaderEntry;
+ private WidgetsListContentEntry mCalendarContentEntry;
+ private WidgetsListHeaderEntry mCameraHeaderEntry;
+ private WidgetsListContentEntry mCameraContentEntry;
+ private WidgetsListHeaderEntry mClockHeaderEntry;
+ private WidgetsListContentEntry mClockContentEntry;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+ .getComponent().getPackageName())
+ .when(mIconCache).getTitleNoCache(any());
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+ mContext = RuntimeEnvironment.application;
+
+ mCalendarHeaderEntry =
+ createWidgetsHeaderEntry("com.example.android.Calendar", "Calendar", 2);
+ mCalendarContentEntry =
+ createWidgetsContentEntry("com.example.android.Calendar", "Calendar", 2);
+ mCameraHeaderEntry = createWidgetsHeaderEntry("com.example.android.Camera", "Camera", 5);
+ mCameraContentEntry = createWidgetsContentEntry("com.example.android.Camera", "Camera", 5);
+ mClockHeaderEntry = createWidgetsHeaderEntry("com.example.android.Clock", "Clock", 3);
+ mClockContentEntry = createWidgetsContentEntry("com.example.android.Clock", "Clock", 3);
+ }
+
+ @Test
+ public void query_shouldInformCallbackWithResultsMatchedOnAppName() {
+ SimpleWidgetsSearchPipeline pipeline = new SimpleWidgetsSearchPipeline(
+ List.of(mCalendarHeaderEntry, mCalendarContentEntry, mCameraHeaderEntry,
+ mCameraContentEntry, mClockHeaderEntry, mClockContentEntry));
+
+ pipeline.query("Ca", results ->
+ assertEquals(results, List.of(mCalendarHeaderEntry, mCalendarContentEntry,
+ mCameraHeaderEntry, mCameraContentEntry)));
+ shadowOf(getMainLooper()).idle();
+ }
+
+ @Test
+ public void testMatches() {
+ assertTrue(MATCHER.matches("q", "Q"));
+ assertTrue(MATCHER.matches("q", " Q"));
+ assertTrue(MATCHER.matches("e", "elephant"));
+ assertTrue(MATCHER.matches("eL", "Elephant"));
+ assertTrue(MATCHER.matches("elephant ", "elephant"));
+ assertTrue(MATCHER.matches("whitec", "white cow"));
+ assertTrue(MATCHER.matches("white c", "white cow"));
+ assertTrue(MATCHER.matches("white ", "white cow"));
+ assertTrue(MATCHER.matches("white c", "white cow"));
+ assertTrue(MATCHER.matches("电", "电子邮件"));
+ assertTrue(MATCHER.matches("电子", "电子邮件"));
+ assertTrue(MATCHER.matches("다", "다운로드"));
+ assertTrue(MATCHER.matches("드", "드라이브"));
+ assertTrue(MATCHER.matches("åbç", "abc"));
+ assertTrue(MATCHER.matches("ål", "Alpha"));
+
+ assertFalse(MATCHER.matches("phant", "elephant"));
+ assertFalse(MATCHER.matches("elephants", "elephant"));
+ assertFalse(MATCHER.matches("cow", "white cow"));
+ assertFalse(MATCHER.matches("cow", "whiteCow"));
+ assertFalse(MATCHER.matches("dog", "cats&Dogs"));
+ assertFalse(MATCHER.matches("ba", "Bot"));
+ assertFalse(MATCHER.matches("ba", "bot"));
+ assertFalse(MATCHER.matches("子", "电子邮件"));
+ assertFalse(MATCHER.matches("邮件", "电子邮件"));
+ assertFalse(MATCHER.matches("ㄷ", "다운로드 드라이브"));
+ assertFalse(MATCHER.matches("ㄷㄷ", "다운로드 드라이브"));
+ assertFalse(MATCHER.matches("åç", "abc"));
+ }
+
+ private WidgetsListHeaderEntry createWidgetsHeaderEntry(String packageName, String appName,
+ int numOfWidgets) {
+ List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+ PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+ widgetItems.get(0).user);
+
+ return new WidgetsListHeaderEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+ }
+
+ private WidgetsListContentEntry createWidgetsContentEntry(String packageName, String appName,
+ int numOfWidgets) {
+ List<WidgetItem> widgetItems = generateWidgetItems(packageName, numOfWidgets);
+ PackageItemInfo pInfo = createPackageItemInfo(packageName, appName,
+ widgetItems.get(0).user);
+
+ return new WidgetsListContentEntry(pInfo, /* titleSectionName= */ "", widgetItems);
+ }
+
+ private PackageItemInfo createPackageItemInfo(String packageName, String appName,
+ UserHandle userHandle) {
+ PackageItemInfo pInfo = new PackageItemInfo(packageName);
+ pInfo.title = appName;
+ pInfo.user = userHandle;
+ pInfo.bitmap = BitmapInfo.of(Bitmap.createBitmap(10, 10, Bitmap.Config.ALPHA_8), 0);
+ return pInfo;
+ }
+
+ private List<WidgetItem> generateWidgetItems(String packageName, int numOfWidgets) {
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+ for (int i = 0; i < numOfWidgets; i++) {
+ ComponentName cn = ComponentName.createRelative(packageName, ".SampleWidget" + i);
+ AppWidgetProviderInfo widgetInfo = new AppWidgetProviderInfo();
+ widgetInfo.provider = cn;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(cn));
+
+ WidgetItem widgetItem = new WidgetItem(
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo),
+ mTestProfile, mIconCache);
+ widgetItems.add(widgetItem);
+ }
+ return widgetItems;
+ }
+}
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
new file mode 100644
index 0000000..e68edd3
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/util/WidgetsTableUtilsTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.icons.ComponentWithLabel;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.pm.ShortcutConfigActivityInfo;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadows.ShadowPackageManager;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public final class WidgetsTableUtilsTest {
+ private static final String TEST_PACKAGE = "com.google.test";
+
+ @Mock
+ private IconCache mIconCache;
+
+ private Context mContext;
+ private InvariantDeviceProfile mTestProfile;
+ private WidgetItem mWidget1x1;
+ private WidgetItem mWidget2x2;
+ private WidgetItem mWidget2x3;
+ private WidgetItem mWidget2x4;
+ private WidgetItem mWidget4x4;
+
+ private WidgetItem mShortcut1;
+ private WidgetItem mShortcut2;
+ private WidgetItem mShortcut3;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = RuntimeEnvironment.application;
+
+ mTestProfile = new InvariantDeviceProfile();
+ mTestProfile.numRows = 5;
+ mTestProfile.numColumns = 5;
+
+ initTestWidgets();
+ initTestShortcuts();
+
+ doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))
+ .getComponent().getPackageName())
+ .when(mIconCache).getTitleNoCache(any());
+ }
+
+
+ @Test
+ public void groupWidgetItemsIntoTable_widgetsOnly_maxSpansPerRow5_shouldGroupWidgetsInTable() {
+ List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
+ mWidget2x2);
+
+ List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
+ widgetItems, /* maxSpansPerRow= */ 5);
+
+ // Row 0: 1x1, 2x2, 2x3
+ // Row 1: 2x4
+ // Row 2: 4x4
+ assertThat(widgetItemInTable).hasSize(3);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2, mWidget2x3);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x4);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ }
+
+ @Test
+ public void groupWidgetItemsIntoTable_widgetsOnly_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+ List<WidgetItem> widgetItems = List.of(mWidget4x4, mWidget2x3, mWidget1x1, mWidget2x4,
+ mWidget2x2);
+
+ List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
+ widgetItems, /* maxSpansPerRow= */ 4);
+
+ // Row 0: 1x1, 2x2
+ // Row 1: 2x3, 2x4
+ // Row 2: 4x4
+ assertThat(widgetItemInTable).hasSize(3);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget2x4);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ }
+
+ @Test
+ public void groupWidgetItemsIntoTable_mixItems_maxSpansPerRow4_shouldGroupWidgetsInTable() {
+ List<WidgetItem> widgetItems = List.of(mWidget4x4, mShortcut3, mWidget2x3, mShortcut1,
+ mWidget1x1, mShortcut2, mWidget2x4, mWidget2x2);
+
+ List<ArrayList<WidgetItem>> widgetItemInTable = WidgetsTableUtils.groupWidgetItemsIntoTable(
+ widgetItems, /* maxSpansPerRow= */ 4);
+
+ // Row 0: 1x1, 2x2
+ // Row 1: 2x3, 2x4
+ // Row 2: 4x4
+ // Row 3: shortcut3, shortcut1, shortcut2
+ assertThat(widgetItemInTable).hasSize(4);
+ assertThat(widgetItemInTable.get(0)).containsExactly(mWidget1x1, mWidget2x2);
+ assertThat(widgetItemInTable.get(1)).containsExactly(mWidget2x3, mWidget2x4);
+ assertThat(widgetItemInTable.get(2)).containsExactly(mWidget4x4);
+ assertThat(widgetItemInTable.get(3)).containsExactly(mShortcut3, mShortcut2, mShortcut1);
+ }
+
+ private void initTestWidgets() {
+ List<Point> widgetSizes = List.of(new Point(1, 1), new Point(2, 2), new Point(2, 3),
+ new Point(2, 4), new Point(4, 4));
+
+ ArrayList<WidgetItem> widgetItems = new ArrayList<>();
+ widgetSizes.stream().forEach(
+ widgetSize -> {
+ ShadowPackageManager packageManager = shadowOf(mContext.getPackageManager());
+ AppWidgetProviderInfo info = new AppWidgetProviderInfo();
+ info.provider = ComponentName.createRelative(TEST_PACKAGE,
+ ".WidgetProvider_" + widgetSize.x + "x" + widgetSize.y);
+ LauncherAppWidgetProviderInfo widgetInfo =
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+ widgetInfo.spanX = widgetSize.x;
+ widgetInfo.spanY = widgetSize.y;
+ ReflectionHelpers.setField(widgetInfo, "providerInfo",
+ packageManager.addReceiverIfNotPresent(widgetInfo.provider));
+ widgetItems.add(new WidgetItem(widgetInfo, mTestProfile, mIconCache));
+ }
+ );
+ mWidget1x1 = widgetItems.get(0);
+ mWidget2x2 = widgetItems.get(1);
+ mWidget2x3 = widgetItems.get(2);
+ mWidget2x4 = widgetItems.get(3);
+ mWidget4x4 = widgetItems.get(4);
+ }
+
+ private void initTestShortcuts() {
+ PackageManager packageManager = mContext.getPackageManager();
+ mShortcut1 = new WidgetItem(new TestShortcutConfigActivityInfo(
+ ComponentName.createRelative(TEST_PACKAGE, ".shortcut1"), UserHandle.CURRENT),
+ mIconCache, packageManager);
+ mShortcut2 = new WidgetItem(new TestShortcutConfigActivityInfo(
+ ComponentName.createRelative(TEST_PACKAGE, ".shortcut2"), UserHandle.CURRENT),
+ mIconCache, packageManager);
+ mShortcut3 = new WidgetItem(new TestShortcutConfigActivityInfo(
+ ComponentName.createRelative(TEST_PACKAGE, ".shortcut3"), UserHandle.CURRENT),
+ mIconCache, packageManager);
+
+ }
+
+ private final class TestShortcutConfigActivityInfo extends ShortcutConfigActivityInfo {
+
+ TestShortcutConfigActivityInfo(ComponentName componentName, UserHandle user) {
+ super(componentName, user);
+ }
+
+ @Override
+ public Drawable getFullResIcon(IconCache cache) {
+ return null;
+ }
+
+ @Override
+ public CharSequence getLabel(PackageManager pm) {
+ return null;
+ }
+ }
+}
diff --git a/robolectric_tests/unstaged/SettingsCacheTest.java b/robolectric_tests/unstaged/SettingsCacheTest.java
new file mode 100644
index 0000000..fbf4c63
--- /dev/null
+++ b/robolectric_tests/unstaged/SettingsCacheTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.net.Uri;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+import java.util.Collections;
+
+@RunWith(RobolectricTestRunner.class)
+public class SettingsCacheTest {
+
+ public static final Uri KEY_SYSTEM_URI_TEST1 = Uri.parse("content://settings/system/test1");
+ public static final Uri KEY_SYSTEM_URI_TEST2 = Uri.parse("content://settings/system/test2");;
+
+ private SettingsCache.OnChangeListener mChangeListener;
+ private SettingsCache mSettingsCache;
+
+ @Before
+ public void setup() {
+ mChangeListener = mock(SettingsCache.OnChangeListener.class);
+ Context targetContext = RuntimeEnvironment.application;
+ mSettingsCache = SettingsCache.INSTANCE.get(targetContext);
+ mSettingsCache.register(KEY_SYSTEM_URI_TEST1, mChangeListener);
+ }
+
+ @Test
+ public void listenerCalledOnChange() {
+ mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
+ verify(mChangeListener, times(1)).onSettingsChanged(true);
+ }
+
+ @Test
+ public void getValueRespectsDefaultValue() {
+ // Case of key not found
+ boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
+ assertFalse(val);
+ }
+
+ @Test
+ public void getValueHitsCache() {
+ mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true));
+ boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
+ assertTrue(val);
+ }
+
+ @Test
+ public void getValueUpdatedCache() {
+ // First ensure there's nothing in cache
+ boolean val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
+ assertFalse(val);
+
+ mSettingsCache.setKeyCache(Collections.singletonMap(KEY_SYSTEM_URI_TEST1, true));
+ val = mSettingsCache.getValue(KEY_SYSTEM_URI_TEST1, 0);
+ assertTrue(val);
+ }
+
+ @Test
+ public void multipleListenersSingleKey() {
+ SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
+ mSettingsCache.register(KEY_SYSTEM_URI_TEST1, secondListener);
+
+ mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
+ verify(mChangeListener, times(1)).onSettingsChanged(true);
+ verify(secondListener, times(1)).onSettingsChanged(true);
+ }
+
+ @Test
+ public void singleListenerMultipleKeys() {
+ SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
+ mSettingsCache.register(KEY_SYSTEM_URI_TEST2, secondListener);
+
+ mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
+ mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2);
+ verify(mChangeListener, times(1)).onSettingsChanged(true);
+ verify(secondListener, times(1)).onSettingsChanged(true);
+ }
+
+ @Test
+ public void sameListenerMultipleKeys() {
+ SettingsCache.OnChangeListener secondListener = mock(SettingsCache.OnChangeListener.class);
+ mSettingsCache.register(KEY_SYSTEM_URI_TEST2, mChangeListener);
+
+ mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST1);
+ mSettingsCache.onChange(true, KEY_SYSTEM_URI_TEST2);
+ verify(mChangeListener, times(2)).onSettingsChanged(true);
+ verify(secondListener, times(0)).onSettingsChanged(true);
+ }
+}
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index ce37a30..95cdbdd 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
@@ -35,8 +36,6 @@
import androidx.annotation.IntDef;
import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -60,7 +59,7 @@
TYPE_SNACKBAR,
TYPE_LISTENER,
TYPE_ALL_APPS_EDU,
-
+ TYPE_DRAG_DROP_POPUP,
TYPE_TASK_MENU,
TYPE_OPTIONS_POPUP,
TYPE_ICON_SURFACE
@@ -77,17 +76,18 @@
public static final int TYPE_SNACKBAR = 1 << 7;
public static final int TYPE_LISTENER = 1 << 8;
public static final int TYPE_ALL_APPS_EDU = 1 << 9;
+ public static final int TYPE_DRAG_DROP_POPUP = 1 << 10;
// Popups related to quickstep UI
- public static final int TYPE_TASK_MENU = 1 << 10;
- public static final int TYPE_OPTIONS_POPUP = 1 << 11;
- public static final int TYPE_ICON_SURFACE = 1 << 12;
+ public static final int TYPE_TASK_MENU = 1 << 11;
+ public static final int TYPE_OPTIONS_POPUP = 1 << 12;
+ public static final int TYPE_ICON_SURFACE = 1 << 13;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
- | TYPE_ICON_SURFACE;
+ | TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
@@ -104,7 +104,7 @@
// These view all have particular operation associated with swipe down interaction.
public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
TYPE_WIDGETS_FULL_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_ON_BOARD_POPUP |
- TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU ;
+ TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU | TYPE_DRAG_DROP_POPUP;
protected boolean mIsOpen;
@@ -126,10 +126,9 @@
}
public final void close(boolean animate) {
- animate &= Utilities.areAnimationsEnabled(getContext());
+ animate &= areAnimatorsEnabled();
if (mIsOpen) {
- BaseActivity.fromContext(getContext()).getUserEventDispatcher()
- .resetElapsedContainerMillis("container closed");
+ // Add to WW logging
}
handleClose(animate);
mIsOpen = false;
@@ -144,12 +143,6 @@
public void addHintCloseAnim(
float distanceToMove, Interpolator interpolator, PendingAnimation target) { }
- public abstract void logActionCommand(int command);
-
- public int getLogContainerType() {
- return ContainerType.DEFAULT_CONTAINERTYPE;
- }
-
public final boolean isOpen() {
return mIsOpen;
}
@@ -158,7 +151,6 @@
/** @return Whether the back is consumed. If false, Launcher will handle the back as well. */
public boolean onBackPressed() {
- logActionCommand(Action.Command.BACK);
close(true);
return true;
}
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index 9b6166f..3db456c 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -3,15 +3,25 @@
import android.content.ComponentName;
import android.content.Context;
-import com.android.launcher3.util.ResourceBasedOverride;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.stream.Collectors;
-public class AppFilter implements ResourceBasedOverride {
+/**
+ * Utility class to filter out components from various lists
+ */
+public class AppFilter {
- public static AppFilter newInstance(Context context) {
- return Overrides.getObject(AppFilter.class, context, R.string.app_filter_class);
+ private final Set<ComponentName> mFilteredComponents;
+
+ public AppFilter(Context context) {
+ mFilteredComponents = Arrays.stream(
+ context.getResources().getStringArray(R.array.filtered_components))
+ .map(ComponentName::unflattenFromString)
+ .collect(Collectors.toSet());
}
public boolean shouldShowApp(ComponentName app) {
- return true;
+ return !mFilteredComponents.contains(app);
}
}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 6f7b684..9d6af9f 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -2,6 +2,7 @@
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
+import static com.android.launcher3.Utilities.ATLEAST_S;
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_X;
import static com.android.launcher3.views.BaseDragLayer.LAYOUT_Y;
@@ -13,17 +14,21 @@
import android.content.Context;
import android.graphics.Point;
import android.graphics.Rect;
+import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.SizeF;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.util.FocusLogic;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.util.ArrayList;
import java.util.List;
@@ -36,13 +41,21 @@
private static final Rect sTmpRect = new Rect();
// Represents the cell size on the grid in the two orientations.
- private static final MainThreadInitializedObject<Point[]> CELL_SIZE =
+ public static final MainThreadInitializedObject<Point[]> CELL_SIZE =
new MainThreadInitializedObject<>(c -> {
InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
return new Point[] {inv.landscapeProfile.getCellSize(),
inv.portraitProfile.getCellSize()};
});
+ // Represents the border spacing size on the grid in the two orientations.
+ public static final MainThreadInitializedObject<int[]> BORDER_SPACING_SIZE =
+ new MainThreadInitializedObject<>(c -> {
+ InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
+ return new int[] {inv.landscapeProfile.cellLayoutBorderSpacingPx,
+ inv.portraitProfile.cellLayoutBorderSpacingPx};
+ });
+
private static final int HANDLE_COUNT = 4;
private static final int INDEX_LEFT = 0;
private static final int INDEX_TOP = 1;
@@ -279,8 +292,9 @@
* Based on the current deltas, we determine if and how to resize the widget.
*/
private void resizeWidgetIfNeeded(boolean onDismiss) {
- float xThreshold = mCellLayout.getCellWidth();
- float yThreshold = mCellLayout.getCellHeight();
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ float xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacingPx;
+ float yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacingPx;
int hSpanInc = getSpanIncrement((mDeltaX + mDeltaXAddOn) / xThreshold - mRunningHInc);
int vSpanInc = getSpanIncrement((mDeltaY + mDeltaYAddOn) / yThreshold - mRunningVInc);
@@ -351,28 +365,80 @@
}
public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
- int spanX, int spanY) {
- getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
- widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
- sTmpRect.right, sTmpRect.bottom);
+ int spanX, int spanY) {
+ List<SizeF> sizes = getWidgetSizes(launcher, spanX, spanY);
+ if (ATLEAST_S) {
+ widgetView.updateAppWidgetSize(new Bundle(), sizes);
+ } else {
+ Rect bounds = getMinMaxSizes(sizes, null /* outRect */);
+ widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
+ bounds.bottom);
+ }
}
- public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
- if (rect == null) {
- rect = new Rect();
- }
+ private static SizeF getWidgetSize(Context context, Point cellSize, int spanX, int spanY,
+ int borderSpacing) {
final float density = context.getResources().getDisplayMetrics().density;
+ final float hBorderSpacing = (spanX - 1) * borderSpacing;
+ final float vBorderSpacing = (spanY - 1) * borderSpacing;
+
+ return new SizeF(((spanX * cellSize.x) + hBorderSpacing) / density,
+ ((spanY * cellSize.y) + vBorderSpacing) / density);
+ }
+
+ /** Returns the list of sizes for a widget of given span, in dp. */
+ public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) {
final Point[] cellSize = CELL_SIZE.get(context);
+ final int[] borderSpacing = BORDER_SPACING_SIZE.get(context);
- // Compute landscape size
- int landWidth = (int) ((spanX * cellSize[0].x) / density);
- int landHeight = (int) ((spanY * cellSize[0].y) / density);
+ SizeF landSize = getWidgetSize(context, cellSize[0], spanX, spanY, borderSpacing[0]);
+ SizeF portSize = getWidgetSize(context, cellSize[1], spanX, spanY, borderSpacing[1]);
- // Compute portrait size
- int portWidth = (int) ((spanX * cellSize[1].x) / density);
- int portHeight = (int) ((spanY * cellSize[1].y) / density);
- rect.set(portWidth, landHeight, landWidth, portHeight);
- return rect;
+ ArrayList<SizeF> sizes = new ArrayList<>(2);
+ sizes.add(landSize);
+ sizes.add(portSize);
+ return sizes;
+ }
+
+ /**
+ * Returns the min and max widths and heights given a list of sizes, in dp.
+ *
+ * @param sizes List of sizes to get the min/max from.
+ * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If
+ * null, a new rectangle will be allocated.
+ * @return A rectangle with the left (resp. top) is used for the min width (resp. height) and
+ * the right (resp. bottom) for the max. The returned rectangle is set with 0s if the list is
+ * empty.
+ */
+ public static Rect getMinMaxSizes(List<SizeF> sizes, @Nullable Rect outRect) {
+ if (outRect == null) {
+ outRect = new Rect();
+ }
+ if (sizes.isEmpty()) {
+ outRect.set(0, 0, 0, 0);
+ } else {
+ SizeF first = sizes.get(0);
+ outRect.set((int) first.getWidth(), (int) first.getHeight(), (int) first.getWidth(),
+ (int) first.getHeight());
+ for (int i = 1; i < sizes.size(); i++) {
+ outRect.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
+ }
+ }
+ return outRect;
+ }
+
+ /**
+ * Returns the range of sizes a widget may be displayed, given its span.
+ *
+ * @param context Context in which the View is rendered.
+ * @param spanX Width of the widget, in cells.
+ * @param spanY Height of the widget, in cells.
+ * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If
+ * null, a new rectangle will be allocated.
+ */
+ public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY,
+ @Nullable Rect outRect) {
+ return getMinMaxSizes(getWidgetSizes(context, spanX, spanY), outRect);
}
@Override
@@ -384,8 +450,9 @@
}
private void onTouchUp() {
- int xThreshold = mCellLayout.getCellWidth();
- int yThreshold = mCellLayout.getCellHeight();
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ int xThreshold = mCellLayout.getCellWidth() + dp.cellLayoutBorderSpacingPx;
+ int yThreshold = mCellLayout.getCellHeight() + dp.cellLayoutBorderSpacingPx;
mDeltaXAddOn = mRunningHInc * xThreshold;
mDeltaYAddOn = mRunningVInc * yThreshold;
@@ -476,7 +543,7 @@
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// Clear the frame and give focus to the widget host view when a directional key is pressed.
- if (FocusLogic.shouldConsume(keyCode)) {
+ if (shouldConsume(keyCode)) {
close(false);
mWidgetView.requestFocus();
return true;
@@ -537,11 +604,6 @@
}
@Override
- public void logActionCommand(int command) {
- // TODO: Log this case.
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_WIDGET_RESIZE_FRAME) != 0;
}
@@ -604,4 +666,14 @@
return moveEnd ? out.size() - size() : size() - out.size();
}
}
+
+ /**
+ * Returns true only if this utility class handles the key code.
+ */
+ public static boolean shouldConsume(int keyCode) {
+ return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+ || keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN
+ || keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END
+ || keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN);
+ }
}
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index a55c90d..75e89b2 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -21,6 +21,7 @@
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -86,13 +87,12 @@
long mainProfileId = UserCache.INSTANCE.get(context)
.getSerialNumberForUser(myUserHandle());
String oldWidgetId = Integer.toString(oldWidgetIds[i]);
- int result = new ContentWriter(context, new ContentWriter.CommitParams(
- "appWidgetId=? and (restored & 1) = 1 and profileId=?",
- new String[] { oldWidgetId, Long.toString(mainProfileId) }))
+ final String where = "appWidgetId=? and (restored & 1) = 1 and profileId=?";
+ final String[] args = new String[] { oldWidgetId, Long.toString(mainProfileId) };
+ int result = new ContentWriter(context, new ContentWriter.CommitParams(where, args))
.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
.put(LauncherSettings.Favorites.RESTORED, state)
.commit();
-
if (result == 0) {
Cursor cursor = cr.query(Favorites.CONTENT_URI,
new String[] {Favorites.APPWIDGET_ID},
@@ -106,6 +106,12 @@
cursor.close();
}
}
+ // attempt to update widget id in backup table as well
+ new ContentWriter(context, ContentWriter.CommitParams.backupCommitParams(
+ "appWidgetId=? and profileId=?", args))
+ .put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i])
+ .put(LauncherSettings.Favorites.RESTORED, state)
+ .commit();
}
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 432073e..f61bc05 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -658,7 +658,7 @@
}
}
- protected static void beginDocument(XmlPullParser parser, String firstElementName)
+ public static void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 310c306..062ab71 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -31,14 +31,11 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
-import android.view.ContextThemeWrapper;
import androidx.annotation.IntDef;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.ActivityContext;
@@ -82,7 +79,6 @@
new ArrayList<>();
protected DeviceProfile mDeviceProfile;
- protected UserEventDispatcher mUserEventDispatcher;
protected StatsLogManager mStatsLogManager;
protected SystemUiController mSystemUiController;
@@ -144,22 +140,16 @@
return mDeviceProfile;
}
- public void modifyUserEvent(LauncherLogProto.LauncherEvent event) {}
-
- public final StatsLogManager getStatsLogManager() {
+ /**
+ * Returns {@link StatsLogManager} for user event logging.
+ */
+ public StatsLogManager getStatsLogManager() {
if (mStatsLogManager == null) {
mStatsLogManager = StatsLogManager.newInstance(this);
}
return mStatsLogManager;
}
- public final UserEventDispatcher getUserEventDispatcher() {
- if (mUserEventDispatcher == null) {
- mUserEventDispatcher = UserEventDispatcher.newInstance(this);
- }
- return mUserEventDispatcher;
- }
-
public SystemUiController getSystemUiController() {
if (mSystemUiController == null) {
mSystemUiController = new SystemUiController(getWindow());
@@ -342,7 +332,7 @@
public static <T extends BaseActivity> T fromContext(Context context) {
if (context instanceof BaseActivity) {
return (T) context;
- } else if (context instanceof ContextThemeWrapper) {
+ } else if (context instanceof ContextWrapper) {
return fromContext(((ContextWrapper) context).getBaseContext());
} else {
throw new IllegalArgumentException("Cannot find BaseActivity in parent tree");
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index f2a5c65..5bfde15 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -17,7 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
-import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
+import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ROTATION;
import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
@@ -43,16 +43,18 @@
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.DefaultDisplay.DisplayInfoChangeListener;
-import com.android.launcher3.util.DefaultDisplay.Info;
+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.PackageManagerHelper;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TraceHelper;
@@ -84,7 +86,7 @@
mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode",
() -> getPackageManager().isSafeMode());
- DefaultDisplay.INSTANCE.get(this).addChangeListener(this);
+ DisplayController.getDefaultDisplay(this).addChangeListener(this);
// Update theme
WallpaperColorInfo.INSTANCE.get(this).addOnChangeListener(this);
@@ -154,8 +156,7 @@
public abstract ActivityOptions getActivityLaunchOptions(View v);
- public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
- @Nullable String sourceContainer) {
+ public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item) {
if (mIsSafeModeEnabled && !PackageManagerHelper.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return false;
@@ -176,25 +177,20 @@
&& !((WorkspaceItemInfo) item).isPromise();
if (isShortcut) {
// Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
+ startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
- AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
- Process.myUserHandle(), sourceContainer);
} else {
getSystemService(LauncherApps.class).startMainActivity(
intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
- AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
- sourceContainer);
}
- getUserEventDispatcher().logAppLaunch(v, intent, user);
if (item != null) {
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
logAppLaunch(item, instanceId);
}
return true;
- } catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
}
@@ -206,8 +202,7 @@
.log(LAUNCHER_APP_LAUNCH_TAP);
}
- private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
- @Nullable String sourceContainer) {
+ private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
try {
StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
try {
@@ -221,8 +216,6 @@
String id = ((WorkspaceItemInfo) info).getDeepShortcutId();
String packageName = intent.getPackage();
startShortcut(packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
- sourceContainer);
} else {
// Could be launching some bookkeeping activity
startActivity(intent, optsBundle);
@@ -255,7 +248,7 @@
protected void onDestroy() {
super.onDestroy();
WallpaperColorInfo.INSTANCE.get(this).removeOnChangeListener(this);
- DefaultDisplay.INSTANCE.get(this).removeChangeListener(this);
+ DisplayController.getDefaultDisplay(this).removeChangeListener(this);
}
public void runOnceOnStart(Runnable action) {
@@ -300,4 +293,12 @@
display.getSize(mwSize);
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
+ */
+ public SearchAdapterProvider createSearchAdapterProvider(AllAppsContainerView allapps) {
+ return new DefaultSearchAdapterProvider(this);
+ }
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 8eceec0..9369bdc 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -16,21 +16,17 @@
package com.android.launcher3;
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
-
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -194,13 +190,20 @@
if (isLayoutSuppressed()) info.setScrollable(false);
}
- @Override
- public void setLayoutFrozen(boolean frozen) {
- final boolean changing = frozen != isLayoutSuppressed();
- super.setLayoutFrozen(frozen);
- if (changing) {
- ActivityContext.lookupContext(getContext()).getDragLayer()
- .sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
+ /**
+ * Scrolls this recycler view to the top.
+ */
+ public void scrollToTop() {
+ if (mScrollbar != null) {
+ mScrollbar.reattachThumbToScroll();
}
+ if (getLayoutManager() instanceof LinearLayoutManager) {
+ LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+ if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
+ // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
+ return;
+ }
+ }
+ scrollToPosition(0);
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 198f13d..e0be6de 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.FastBitmapDrawable.newIcon;
+import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
@@ -26,13 +27,16 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
+import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Process;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Property;
@@ -43,22 +47,28 @@
import android.view.ViewDebug;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.IconShape;
+import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.views.ActivityContext;
@@ -77,14 +87,25 @@
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
private static final int DISPLAY_FOLDER = 2;
+ private static final int DISPLAY_HERO_APP = 5;
+ protected static final int DISPLAY_TASKBAR = 6;
- private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
+ private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
+ private static final float HIGHLIGHT_SCALE = 1.16f;
+
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
+ private static final int ICON_UPDATE_ANIMATION_DURATION = 375;
+
private float mScaleForReorderBounce = 1f;
+ protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Path mHighlightPath = new Path();
+ protected int mHighlightColor = Color.TRANSPARENT;
+ private final BlurMaskFilter mHighlightShadowFilter;
+
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
@Override
@@ -116,7 +137,7 @@
private Drawable mIcon;
private boolean mCenterVertically;
- private final int mDisplay;
+ protected final int mDisplay;
private final CheckLongPressHelper mLongPressHelper;
@@ -145,7 +166,9 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
- private IconLoadRequest mIconLoadRequest;
+ private HandlerRunnable mIconLoadRequest;
+
+ private boolean mEnableIconUpdateAnimation = false;
public BubbleTextView(Context context) {
this(context, null, 0);
@@ -170,6 +193,7 @@
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
+ setCenterVertically(grid.isScalableGrid);
} else if (mDisplay == DISPLAY_ALL_APPS) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
@@ -178,6 +202,10 @@
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
+ } else if (mDisplay == DISPLAY_HERO_APP) {
+ defaultIconSize = grid.allAppsIconSizePx;
+ } else if (mDisplay == DISPLAY_TASKBAR) {
+ defaultIconSize = grid.iconSizePx;
} else {
// widget_selection or shortcut_popup
defaultIconSize = grid.iconSizePx;
@@ -196,6 +224,10 @@
setEllipsize(TruncateAt.END);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
setTextAlpha(1f);
+
+ int shadowSize = context.getResources().getDimensionPixelSize(
+ R.dimen.blur_size_click_shadow);
+ mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
}
@Override
@@ -235,6 +267,7 @@
mDotScaleAnim.start();
}
+ @UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
applyFromWorkspaceItem(info, false);
}
@@ -251,16 +284,16 @@
}
}
+ @UiThread
public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
applyIconAndLabel(info);
setTag(info);
- if (promiseStateChanged || (info.hasPromiseIconUi())) {
- applyPromiseState(promiseStateChanged);
- }
-
+ applyLoadingState(promiseStateChanged);
applyDotState(info, false /* animate */);
+ setDownloadStateContentDescription(info, info.getProgressLevel());
}
+ @UiThread
public void applyFromApplicationInfo(AppInfo info) {
applyIconAndLabel(info);
@@ -270,27 +303,48 @@
// Verify high res immediately
verifyHighRes();
- if (info instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
- applyProgressLevel(promiseAppInfo.level);
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+ applyProgressLevel();
}
applyDotState(info, false /* animate */);
+ setDownloadStateContentDescription(info, info.getProgressLevel());
}
- public void applyFromPackageItemInfo(PackageItemInfo info) {
+ /**
+ * Apply label and tag using a generic {@link ItemInfoWithIcon}
+ */
+ @UiThread
+ public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
applyIconAndLabel(info);
// We don't need to check the info since it's not a WorkspaceItemInfo
super.setTag(info);
// Verify high res immediately
verifyHighRes();
+
+ setDownloadStateContentDescription(info, info.getProgressLevel());
}
- private void applyIconAndLabel(ItemInfoWithIcon info) {
+ /**
+ * Apply label and tag using a {@link SearchActionItemInfo}
+ */
+ @UiThread
+ public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
+ applyIconAndLabel(searchActionItemInfo);
+ setTag(searchActionItemInfo);
+ }
+
+ @UiThread
+ protected void applyIconAndLabel(ItemInfoWithIcon info) {
FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
setIcon(iconDrawable);
+ applyLabel(info);
+ }
+
+ @UiThread
+ private void applyLabel(ItemInfoWithIcon info) {
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.isDisabled()
@@ -300,6 +354,16 @@
}
/**
+ * Directly set the icon and label.
+ */
+ @UiThread
+ public void applyIconAndLabel(Drawable icon, CharSequence label) {
+ setIcon(icon);
+ setText(label);
+ setContentDescription(label);
+ }
+
+ /**
* Overrides the default long press timeout.
*/
public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
@@ -398,18 +462,55 @@
@Override
public void onDraw(Canvas canvas) {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
+ int count = canvas.save();
+ drawFocusHighlight(canvas);
+ canvas.restoreToCount(count);
+ }
super.onDraw(canvas);
drawDotIfNecessary(canvas);
}
+ protected void drawFocusHighlight(Canvas canvas) {
+ boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
+ ((ItemInfo) getTag()).user);
+ float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
+ canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
+ float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
+ mHighlightPath.reset();
+ mHighlightPaint.reset();
+ getIconBounds(mDotParams.iconBounds);
+ getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
+ outlineSize / 2);
+ if (isBadged) {
+ float borderSize = outlineSize - getIconSize();
+ float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
+ float badgeInset = outlineSize - badgeSize;
+ getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
+ mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
+ }
+ mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
+ mHighlightPaint.setColor(mDotParams.color);
+ canvas.drawPath(mHighlightPath, mHighlightPaint);
+ mHighlightPaint.setMaskFilter(null);
+ mHighlightPaint.setColor(mHighlightColor);
+ canvas.drawPath(mHighlightPath, mHighlightPaint);
+ }
+
/**
* Draws the notification dot in the top right corner of the icon bounds.
+ *
* @param canvas The canvas to draw to.
*/
protected void drawDotIfNecessary(Canvas canvas) {
+ if (mActivity instanceof Launcher && ((Launcher) mActivity).isViewInTaskbar(this)) {
+ // TODO: support notification dots in Taskbar
+ return;
+ }
if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
getIconBounds(mDotParams.iconBounds);
- Utilities.scaleRectAboutCenter(mDotParams.iconBounds, IconShape.getNormalizationScale());
+ Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
+ IconShape.getNormalizationScale());
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
@@ -448,6 +549,14 @@
outBounds.set(left, top, right, bottom);
}
+
+ /**
+ * Sets whether to vertically center the content.
+ */
+ public void setCenterVertically(boolean centerVertically) {
+ mCenterVertically = centerVertically;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mCenterVertically) {
@@ -504,6 +613,7 @@
/**
* Creates an animator to fade the text in or out.
+ *
* @param fadeIn Whether the text should fade in or fade out.
*/
public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
@@ -517,51 +627,86 @@
mLongPressHelper.cancelLongPress();
}
- public void applyPromiseState(boolean promiseStateChanged) {
- if (getTag() instanceof WorkspaceItemInfo) {
+ /**
+ * Applies the loading progress value to the progress bar.
+ *
+ * If this app is installing, the progress bar will be updated with the installation progress.
+ * If this app is installed and downloading incrementally, the progress bar will be updated
+ * with the total download progress.
+ */
+ public void applyLoadingState(boolean promiseStateChanged) {
+ if (getTag() instanceof ItemInfoWithIcon) {
WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
- final boolean isPromise = info.hasPromiseIconUi();
- final int progressLevel = isPromise ?
- ((info.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
- info.getInstallProgress() : 0)) : 100;
-
- PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
- if (preloadDrawable != null && promiseStateChanged) {
- preloadDrawable.maybePerformFinishedAnimation();
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE)
+ != 0) {
+ updateProgressBarUi(info.getProgressLevel() == 100);
+ } else if (info.hasPromiseIconUi() || (info.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ updateProgressBarUi(promiseStateChanged);
}
}
}
- public PreloadIconDrawable applyProgressLevel(int progressLevel) {
- if (getTag() instanceof ItemInfoWithIcon) {
- ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
- if (progressLevel >= 100) {
- setContentDescription(info.contentDescription != null
- ? info.contentDescription : "");
- } else if (progressLevel > 0) {
- setContentDescription(getContext()
- .getString(R.string.app_downloading_title, info.title,
- NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+ private void updateProgressBarUi(boolean maybePerformFinishedAnimation) {
+ PreloadIconDrawable preloadDrawable = applyProgressLevel();
+ if (preloadDrawable != null && maybePerformFinishedAnimation) {
+ preloadDrawable.maybePerformFinishedAnimation();
+ }
+ }
+
+ /** Applies the given progress level to the this icon's progress bar. */
+ @Nullable
+ public PreloadIconDrawable applyProgressLevel() {
+ if (!(getTag() instanceof ItemInfoWithIcon)) {
+ return null;
+ }
+
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ int progressLevel = info.getProgressLevel();
+ if (progressLevel >= 100) {
+ setContentDescription(info.contentDescription != null
+ ? info.contentDescription : "");
+ } else if (progressLevel > 0) {
+ setDownloadStateContentDescription(info, progressLevel);
+ } else {
+ setContentDescription(getContext()
+ .getString(R.string.app_waiting_download_title, info.title));
+ }
+ if (mIcon != null) {
+ PreloadIconDrawable preloadIconDrawable;
+ if (mIcon instanceof PreloadIconDrawable) {
+ preloadIconDrawable = (PreloadIconDrawable) mIcon;
+ preloadIconDrawable.setLevel(progressLevel);
+ preloadIconDrawable.setIsDisabled(!info.isAppStartable());
} else {
- setContentDescription(getContext()
- .getString(R.string.app_waiting_download_title, info.title));
+ preloadIconDrawable = makePreloadIcon();
+ setIcon(preloadIconDrawable);
}
- if (mIcon != null) {
- final PreloadIconDrawable preloadDrawable;
- if (mIcon instanceof PreloadIconDrawable) {
- preloadDrawable = (PreloadIconDrawable) mIcon;
- preloadDrawable.setLevel(progressLevel);
- } else {
- preloadDrawable = newPendingIcon(getContext(), info);
- preloadDrawable.setLevel(progressLevel);
- setIcon(preloadDrawable);
- }
- return preloadDrawable;
- }
+ return preloadIconDrawable;
}
return null;
}
+ /**
+ * Creates a PreloadIconDrawable with the appropriate progress level without mutating this
+ * object.
+ */
+ @Nullable
+ public PreloadIconDrawable makePreloadIcon() {
+ if (!(getTag() instanceof ItemInfoWithIcon)) {
+ return null;
+ }
+
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ int progressLevel = info.getProgressLevel();
+ final PreloadIconDrawable preloadDrawable = newPendingIcon(getContext(), info);
+
+ preloadDrawable.setLevel(progressLevel);
+ preloadDrawable.setIsDisabled(!info.isAppStartable());
+
+ return preloadDrawable;
+ }
+
public void applyDotState(ItemInfo itemInfo, boolean animate) {
if (mIcon instanceof FastBitmapDrawable) {
boolean wasDotted = mDotInfo != null;
@@ -598,10 +743,28 @@
}
}
+ private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
+ != 0) {
+ String percentageString = NumberFormat.getPercentInstance()
+ .format(progressLevel * 0.01);
+ if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ setContentDescription(getContext()
+ .getString(
+ R.string.app_installing_title, info.title, percentageString));
+ } else if ((info.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+ setContentDescription(getContext()
+ .getString(
+ R.string.app_downloading_title, info.title, percentageString));
+ }
+ }
+ }
+
/**
* Sets the icon for this view based on the layout direction.
*/
- private void setIcon(Drawable icon) {
+ protected void setIcon(Drawable icon) {
if (mIsIconVisible) {
applyCompoundDrawables(icon);
}
@@ -621,17 +784,26 @@
applyCompoundDrawables(icon);
}
+ protected boolean iconUpdateAnimationEnabled() {
+ return mEnableIconUpdateAnimation;
+ }
+
protected void applyCompoundDrawables(Drawable icon) {
// If we had already set an icon before, disable relayout as the icon size is the
// same as before.
mDisableRelayout = mIcon != null;
icon.setBounds(0, 0, mIconSize, mIconSize);
- if (mLayoutHorizontal) {
- setCompoundDrawablesRelative(icon, null, null, null);
- } else {
- setCompoundDrawables(null, icon, null, null);
+
+ updateIcon(icon);
+
+ // If the current icon is a placeholder color, animate its update.
+ if (mIcon != null
+ && mIcon instanceof PlaceHolderIconDrawable
+ && iconUpdateAnimationEnabled()) {
+ ((PlaceHolderIconDrawable) mIcon).animateIconUpdate(icon);
}
+
mDisableRelayout = false;
}
@@ -650,6 +822,7 @@
if (getTag() == info) {
mIconLoadRequest = null;
mDisableRelayout = true;
+ mEnableIconUpdateAnimation = true;
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
info.bitmap.icon.prepareToDraw();
@@ -660,10 +833,13 @@
applyFromWorkspaceItem((WorkspaceItemInfo) info);
mActivity.invalidateParent(info);
} else if (info instanceof PackageItemInfo) {
- applyFromPackageItemInfo((PackageItemInfo) info);
+ applyFromItemInfoWithIcon((PackageItemInfo) info);
+ } else if (info instanceof SearchActionItemInfo) {
+ applyFromSearchActionItemInfo((SearchActionItemInfo) info);
}
mDisableRelayout = false;
+ mEnableIconUpdateAnimation = false;
}
}
@@ -756,9 +932,11 @@
@Override
public SafeCloseable prepareDrawDragView() {
+ int highlightColor = mHighlightColor;
+ mHighlightColor = Color.TRANSPARENT;
resetIconScale();
setForceHideDot(true);
- return () -> { };
+ return () -> mHighlightColor = highlightColor;
}
private void resetIconScale() {
@@ -766,4 +944,12 @@
((FastBitmapDrawable) mIcon).setScale(1f);
}
}
+
+ private void updateIcon(Drawable newIcon) {
+ if (mLayoutHorizontal) {
+ setCompoundDrawablesRelative(newIcon, null, null, null);
+ } else {
+ setCompoundDrawables(null, newIcon, null, null);
+ }
+ }
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 09827d6..459b9a8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -47,7 +47,6 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -241,7 +240,7 @@
@Override
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- mActive = supportsDrop(dragObject.dragInfo);
+ mActive = !options.isKeyboardDrag && supportsDrop(dragObject.dragInfo);
mDrawable.setColorFilter(null);
if (mCurrentColorAnim != null) {
mCurrentColorAnim.cancel();
@@ -395,6 +394,4 @@
TextUtils.TruncateAt.END);
return !mText.equals(displayedText);
}
-
- public abstract Target getDropTargetForLogging();
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 89d768c..b8833cf 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -16,7 +16,10 @@
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.ENABLE_FOUR_COLUMNS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -50,6 +53,7 @@
import android.view.accessibility.AccessibilityEvent;
import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -86,6 +90,8 @@
@Thunk int mCellHeight;
private int mFixedCellWidth;
private int mFixedCellHeight;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private final int mBorderSpacing;
@ViewDebug.ExportedProperty(category = "launcher")
private int mCountX;
@@ -206,6 +212,7 @@
DeviceProfile grid = mActivity.getDeviceProfile();
+ mBorderSpacing = grid.cellLayoutBorderSpacingPx;
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
@@ -286,7 +293,8 @@
}
mShortcutsAndWidgets = new ShortcutAndWidgetContainer(context, mContainerType);
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+ mBorderSpacing);
addView(mShortcutsAndWidgets);
}
@@ -303,6 +311,8 @@
setImportantForAccessibility(accessibilityFlag);
getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
+ // ExploreByTouchHelper sets focusability. Clear it when the delegate is cleared.
+ setFocusable(delegate != null);
// Invalidate the accessibility hierarchy
if (getParent() != null) {
getParent().notifySubtreeAccessibilityStateChanged(
@@ -310,6 +320,13 @@
}
}
+ /**
+ * Returns the currently set accessibility delegate
+ */
+ public DragAndDropAccessibilityDelegate getDragAndDropAccessibilityDelegate() {
+ return mTouchHelper;
+ }
+
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// Always attempt to dispatch hover events to accessibility first.
@@ -321,11 +338,8 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- if (mTouchHelper != null
- || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev))) {
- return true;
- }
- return false;
+ return mTouchHelper != null
+ || (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev));
}
public void enableHardwareLayer(boolean hasLayer) {
@@ -339,7 +353,8 @@
public void setCellDimensions(int width, int height) {
mFixedCellWidth = mCellWidth = width;
mFixedCellHeight = mCellHeight = height;
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+ mBorderSpacing);
}
public void setGridSize(int x, int y) {
@@ -348,7 +363,8 @@
mOccupied = new GridOccupancy(mCountX, mCountY);
mTmpOccupied = new GridOccupancy(mCountX, mCountY);
mTempRectStack.clear();
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+ mBorderSpacing);
requestLayout();
}
@@ -469,8 +485,8 @@
for (int j = 0; j < mCountY; j++) {
canvas.save();
- int transX = i * mCellWidth;
- int transY = j * mCellHeight;
+ int transX = i * mCellWidth + (i * mBorderSpacing);
+ int transY = j * mCellHeight + (j * mBorderSpacing);
canvas.translate(getPaddingLeft() + transX, getPaddingTop() + transY);
@@ -585,6 +601,9 @@
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
+ if (ENABLE_FOUR_COLUMNS.get()) {
+ bubbleChild.setCenterVertically(mContainerType != HOTSEAT);
+ }
}
child.setScaleX(mChildScale);
@@ -700,11 +719,9 @@
* @param result Array of 2 ints to hold the x and y coordinate of the point
*/
void cellToPoint(int cellX, int cellY, int[] result) {
- final int hStartPadding = getPaddingLeft();
- final int vStartPadding = getPaddingTop();
-
- result[0] = hStartPadding + cellX * mCellWidth;
- result[1] = vStartPadding + cellY * mCellHeight;
+ cellToRect(cellX, cellY, 1, 1, mTempRect);
+ result[0] = mTempRect.left;
+ result[1] = mTempRect.top;
}
/**
@@ -728,25 +745,9 @@
* @param result Array of 2 ints to hold the x and y coordinate of the point
*/
void regionToCenterPoint(int cellX, int cellY, int spanX, int spanY, int[] result) {
- final int hStartPadding = getPaddingLeft();
- final int vStartPadding = getPaddingTop();
- result[0] = hStartPadding + cellX * mCellWidth + (spanX * mCellWidth) / 2;
- result[1] = vStartPadding + cellY * mCellHeight + (spanY * mCellHeight) / 2;
- }
-
- /**
- * Given a cell coordinate and span fills out a corresponding pixel rect
- *
- * @param cellX X coordinate of the cell
- * @param cellY Y coordinate of the cell
- * @param result Rect in which to write the result
- */
- void regionToRect(int cellX, int cellY, int spanX, int spanY, Rect result) {
- final int hStartPadding = getPaddingLeft();
- final int vStartPadding = getPaddingTop();
- final int left = hStartPadding + cellX * mCellWidth;
- final int top = vStartPadding + cellY * mCellHeight;
- result.set(left, top, left + (spanX * mCellWidth), top + (spanY * mCellHeight));
+ cellToRect(cellX, cellY, spanX, spanY, mTempRect);
+ result[0] = mTempRect.centerX();
+ result[1] = mTempRect.centerY();
}
public float getDistanceFromCell(float x, float y, int[] cell) {
@@ -777,12 +778,15 @@
int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
- int cw = DeviceProfile.calculateCellWidth(childWidthSize, mCountX);
- int ch = DeviceProfile.calculateCellHeight(childHeightSize, mCountY);
+ int cw = DeviceProfile.calculateCellWidth(childWidthSize, mBorderSpacing,
+ mCountX);
+ int ch = DeviceProfile.calculateCellHeight(childHeightSize, mBorderSpacing,
+ mCountY);
if (cw != mCellWidth || ch != mCellHeight) {
mCellWidth = cw;
mCellHeight = ch;
- mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
+ mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY,
+ mBorderSpacing);
}
}
@@ -832,10 +836,11 @@
/**
* Returns the amount of space left over after subtracting padding and cells. This space will be
* very small, a few pixels at most, and is a result of rounding down when calculating the cell
- * width in {@link DeviceProfile#calculateCellWidth(int, int)}.
+ * width in {@link DeviceProfile#calculateCellWidth(int, int, int)}.
*/
public int getUnusedHorizontalSpace() {
- return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
+ return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth)
+ - ((mCountX - 1) * mBorderSpacing);
}
public Drawable getScrimBackground() {
@@ -851,8 +856,8 @@
return mShortcutsAndWidgets;
}
- public View getChildAt(int x, int y) {
- return mShortcutsAndWidgets.getChildAt(x, y);
+ public View getChildAt(int cellX, int cellY) {
+ return mShortcutsAndWidgets.getChildAt(cellX, cellY);
}
public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
@@ -983,11 +988,11 @@
}
// Center horizontaly
- left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
+ left += (r.width() - dragOutline.getWidth()) / 2;
if (v != null && v.getViewType() == DraggableView.DRAGGABLE_WIDGET) {
// Center vertically
- top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
+ top += (r.height() - dragOutline.getHeight()) / 2;
} else if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
int cHeight = getShortcutsAndWidgets().getCellContentHeight();
int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
@@ -2009,7 +2014,7 @@
// 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 (Utilities.areAnimationsEnabled(getContext())) {
+ if (areAnimatorsEnabled()) {
va.setRepeatMode(ValueAnimator.REVERSE);
va.setRepeatCount(ValueAnimator.INFINITE);
}
@@ -2147,7 +2152,7 @@
findNearestArea(dragViewCenterX, dragViewCenterY, spanX, spanY, targetDestination);
Rect dragRect = new Rect();
- regionToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
+ cellToRect(targetDestination[0], targetDestination[1], spanX, spanY, dragRect);
dragRect.offset(dragViewCenterX - dragRect.centerX(), dragViewCenterY - dragRect.centerY());
Rect dropRegionRect = new Rect();
@@ -2157,7 +2162,7 @@
int dropRegionSpanX = dropRegionRect.width();
int dropRegionSpanY = dropRegionRect.height();
- regionToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
+ cellToRect(dropRegionRect.left, dropRegionRect.top, dropRegionRect.width(),
dropRegionRect.height(), dropRegionRect);
int deltaX = (dropRegionRect.centerX() - dragViewCenterX) / spanX;
@@ -2515,10 +2520,11 @@
final int hStartPadding = getPaddingLeft();
final int vStartPadding = getPaddingTop();
- int width = cellHSpan * cellWidth;
- int height = cellVSpan * cellHeight;
- int x = hStartPadding + cellX * cellWidth;
- int y = vStartPadding + cellY * cellHeight;
+ int x = hStartPadding + (cellX * mBorderSpacing) + (cellX * cellWidth);
+ int y = vStartPadding + (cellY * mBorderSpacing) + (cellY * cellHeight);
+
+ int width = cellHSpan * cellWidth + ((cellHSpan - 1) * mBorderSpacing);
+ int height = cellVSpan * cellHeight + ((cellVSpan - 1) * mBorderSpacing);
resultRect.set(x, y, x + width, y + height);
}
@@ -2536,11 +2542,13 @@
}
public int getDesiredWidth() {
- return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth);
+ return getPaddingLeft() + getPaddingRight() + (mCountX * mCellWidth)
+ + ((mCountX - 1) * mBorderSpacing);
}
public int getDesiredHeight() {
- return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight);
+ return getPaddingTop() + getPaddingBottom() + (mCountY * mCellHeight)
+ + ((mCountY - 1) * mBorderSpacing);
}
public boolean isOccupied(int x, int y) {
@@ -2655,19 +2663,22 @@
this.cellVSpan = cellVSpan;
}
- public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount) {
- setup(cellWidth, cellHeight, invertHorizontally, colCount, 1.0f, 1.0f);
+ public void setup(int cellWidth, int cellHeight, boolean invertHorizontally, int colCount,
+ int rowCount, int borderSpacing, @Nullable Rect inset) {
+ setup(cellWidth, cellHeight, invertHorizontally, colCount, rowCount, 1.0f, 1.0f,
+ borderSpacing, inset);
}
/**
- * Use this method, as opposed to {@link #setup(int, int, boolean, int)}, if the view needs
- * to be scaled.
+ * Use this method, as opposed to {@link #setup(int, int, boolean, int, int, int, 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,
- float cellScaleX, float cellScaleY) {
+ int rowCount, float cellScaleX, float cellScaleY, int borderSpacing,
+ @Nullable Rect inset) {
if (isLockedToGrid) {
final int myCellHSpan = cellHSpan;
final int myCellVSpan = cellVSpan;
@@ -2678,17 +2689,30 @@
myCellX = colCount - myCellX - cellHSpan;
}
- width = (int) (myCellHSpan * cellWidth / cellScaleX - leftMargin - rightMargin);
- height = (int) (myCellVSpan * cellHeight / cellScaleY - topMargin - bottomMargin);
- x = (myCellX * cellWidth + leftMargin);
- y = (myCellY * cellHeight + topMargin);
+ int hBorderSpacing = (myCellHSpan - 1) * borderSpacing;
+ int vBorderSpacing = (myCellVSpan - 1) * borderSpacing;
+
+ 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 * borderSpacing);
+ y = topMargin + (myCellY * cellHeight) + (myCellY * borderSpacing);
+
+ 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 setXY(Point point) {
+ public void setCellXY(Point point) {
cellX = point.x;
cellY = point.y;
}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 2857497..cc119c9 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -18,8 +18,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_CANCEL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROPPED_ON_REMOVE;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNDO;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNDO;
import android.content.Context;
import android.text.TextUtils;
@@ -28,22 +27,19 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.Snackbar;
public class DeleteDropTarget extends ButtonDropTarget {
private final StatsLogManager mStatsLogManager;
- private int mControlType = ControlType.DEFAULT_CONTROLTYPE;
+ private StatsLogManager.LauncherEvent mLauncherEvent;
public DeleteDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -115,8 +111,8 @@
* Set mControlType depending on the drag item.
*/
private void setControlTypeBasedOnDragSource(ItemInfo item) {
- mControlType = item.id != ItemInfo.NO_ID ? ControlType.REMOVE_TARGET
- : ControlType.CANCEL_TARGET;
+ mLauncherEvent = item.id != ItemInfo.NO_ID ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
+ : LAUNCHER_ITEM_DROPPED_ON_CANCEL;
}
@Override
@@ -127,8 +123,7 @@
}
super.onDrop(d, options);
mStatsLogManager.logger().withInstanceId(d.logInstanceId)
- .log(mControlType == ControlType.REMOVE_TARGET ? LAUNCHER_ITEM_DROPPED_ON_REMOVE
- : LAUNCHER_ITEM_DROPPED_ON_CANCEL);
+ .log(mLauncherEvent);
}
@Override
@@ -141,7 +136,7 @@
Runnable onUndoClicked = () -> {
mLauncher.setPageToBindSynchronously(itemPage);
modelWriter.abortDelete();
- mLauncher.getUserEventDispatcher().logActionOnControl(TAP, UNDO);
+ mLauncher.getStatsLogManager().logger().log(LAUNCHER_UNDO);
};
Snackbar.show(mLauncher, R.string.item_removed, R.string.undo,
modelWriter::commitDelete, onUndoClicked);
@@ -161,11 +156,4 @@
mLauncher.getDragLayer()
.announceForAccessibility(getContext().getString(R.string.item_removed));
}
-
- @Override
- public Target getDropTargetForLogging() {
- Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
- t.controlType = mControlType;
- return t;
- }
}
diff --git a/src/com/android/launcher3/DevicePaddings.java b/src/com/android/launcher3/DevicePaddings.java
new file mode 100644
index 0000000..4827f36
--- /dev/null
+++ b/src/com/android/launcher3/DevicePaddings.java
@@ -0,0 +1,202 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Workspace items have a fixed height, so we need a way to distribute any unused workspace height.
+ *
+ * The unused or "extra" height is allocated to three different variable heights:
+ * - The space above the workspace
+ * - The space between the workspace and hotseat
+ * - The espace below the hotseat
+ */
+public class DevicePaddings {
+
+ private static final String DEVICE_PADDING = "device-paddings";
+ private static final String DEVICE_PADDINGS = "device-padding";
+
+ private static final String WORKSPACE_TOP_PADDING = "workspaceTopPadding";
+ private static final String WORKSPACE_BOTTOM_PADDING = "workspaceBottomPadding";
+ private static final String HOTSEAT_BOTTOM_PADDING = "hotseatBottomPadding";
+
+ private static final String TAG = DevicePaddings.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ ArrayList<DevicePadding> mDevicePaddings = new ArrayList<>();
+
+ public DevicePaddings(Context context) {
+ try (XmlResourceParser parser = context.getResources().getXml(R.xml.size_limits)) {
+ final int depth = parser.getDepth();
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if ((type == XmlPullParser.START_TAG) && DEVICE_PADDING.equals(parser.getName())) {
+ final int displayDepth = parser.getDepth();
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > displayDepth)
+ && type != XmlPullParser.END_DOCUMENT) {
+ if ((type == XmlPullParser.START_TAG)
+ && DEVICE_PADDINGS.equals(parser.getName())) {
+ TypedArray a = context.obtainStyledAttributes(
+ Xml.asAttributeSet(parser), R.styleable.DevicePadding);
+ int maxWidthPx = a.getDimensionPixelSize(
+ R.styleable.DevicePadding_maxEmptySpace, 0);
+ a.recycle();
+
+ PaddingFormula workspaceTopPadding = null;
+ PaddingFormula workspaceBottomPadding = null;
+ PaddingFormula hotseatBottomPadding = null;
+
+ final int limitDepth = parser.getDepth();
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > limitDepth)
+ && type != XmlPullParser.END_DOCUMENT) {
+ AttributeSet attr = Xml.asAttributeSet(parser);
+ if ((type == XmlPullParser.START_TAG)) {
+ if (WORKSPACE_TOP_PADDING.equals(parser.getName())) {
+ workspaceTopPadding = new PaddingFormula(context, attr);
+ } else if (WORKSPACE_BOTTOM_PADDING.equals(parser.getName())) {
+ workspaceBottomPadding = new PaddingFormula(context, attr);
+ } else if (HOTSEAT_BOTTOM_PADDING.equals(parser.getName())) {
+ hotseatBottomPadding = new PaddingFormula(context, attr);
+ }
+ }
+ }
+
+ if (workspaceTopPadding == null
+ || workspaceBottomPadding == null
+ || hotseatBottomPadding == null) {
+ throw new RuntimeException("DevicePadding missing padding.");
+ }
+
+ mDevicePaddings.add(new DevicePadding(maxWidthPx, workspaceTopPadding,
+ workspaceBottomPadding, hotseatBottomPadding));
+ }
+ }
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Sort ascending by maxEmptySpacePx
+ mDevicePaddings.sort((sl1, sl2) -> Integer.compare(sl1.maxEmptySpacePx,
+ sl2.maxEmptySpacePx));
+ }
+
+ public DevicePadding getDevicePadding(int extraSpacePx) {
+ for (DevicePadding limit : mDevicePaddings) {
+ if (extraSpacePx <= limit.maxEmptySpacePx) {
+ return limit;
+ }
+ }
+
+ return mDevicePaddings.get(mDevicePaddings.size() - 1);
+ }
+
+ /**
+ * Holds all the formulas to calculate the padding for a particular device based on the
+ * amount of extra space.
+ */
+ public static final class DevicePadding {
+
+ private final int maxEmptySpacePx;
+ private final PaddingFormula workspaceTopPadding;
+ private final PaddingFormula workspaceBottomPadding;
+ private final PaddingFormula hotseatBottomPadding;
+
+ public DevicePadding(int maxEmptySpacePx,
+ PaddingFormula workspaceTopPadding,
+ PaddingFormula workspaceBottomPadding,
+ PaddingFormula hotseatBottomPadding) {
+ this.maxEmptySpacePx = maxEmptySpacePx;
+ this.workspaceTopPadding = workspaceTopPadding;
+ this.workspaceBottomPadding = workspaceBottomPadding;
+ this.hotseatBottomPadding = hotseatBottomPadding;
+ }
+
+ public int getWorkspaceTopPadding(int extraSpacePx) {
+ return workspaceTopPadding.calculate(extraSpacePx);
+ }
+
+ public int getWorkspaceBottomPadding(int extraSpacePx) {
+ return workspaceBottomPadding.calculate(extraSpacePx);
+ }
+
+ public int getHotseatBottomPadding(int extraSpacePx) {
+ return hotseatBottomPadding.calculate(extraSpacePx);
+ }
+ }
+
+ /**
+ * Used to calculate a padding based on three variables: a, b, and c.
+ *
+ * Calculation: a * (extraSpace - c) + b
+ */
+ private static final class PaddingFormula {
+
+ private final float a;
+ private final float b;
+ private final float c;
+
+ public PaddingFormula(Context context, AttributeSet attrs) {
+ TypedArray t = context.obtainStyledAttributes(attrs,
+ R.styleable.DevicePaddingFormula);
+
+ a = getValue(t, R.styleable.DevicePaddingFormula_a);
+ b = getValue(t, R.styleable.DevicePaddingFormula_b);
+ c = getValue(t, R.styleable.DevicePaddingFormula_c);
+
+ t.recycle();
+ }
+
+ public int calculate(int extraSpacePx) {
+ if (DEBUG) {
+ Log.d(TAG, "a=" + a + " * (" + extraSpacePx + " - " + c + ") + b=" + b);
+ }
+ return Math.round(a * (extraSpacePx - c) + b);
+ }
+
+ private static float getValue(TypedArray a, int index) {
+ if (a.getType(index) == TypedValue.TYPE_DIMENSION) {
+ return a.getDimensionPixelSize(index, 0);
+ } else if (a.getType(index) == TypedValue.TYPE_FLOAT) {
+ return a.getFloat(index, 0);
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "a=" + a + ", b=" + b + ", c=" + c;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 49caa93..2440854 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,6 +16,8 @@
package com.android.launcher3;
+import static com.android.launcher3.ResourceUtils.pxFromDp;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -23,12 +25,17 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.view.Surface;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.DevicePaddings.DevicePadding;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.WindowBounds;
public class DeviceProfile {
@@ -38,7 +45,7 @@
public final InvariantDeviceProfile inv;
- private final DefaultDisplay.Info mInfo;
+ private final Info mInfo;
// Device properties
public final boolean isTablet;
@@ -59,6 +66,8 @@
public final float aspectRatio;
+ public final boolean isScalableGrid;
+
/**
* The maximum amount of left/right workspace padding as a percentage of the screen width.
* To be clear, this means that up to 7% of the screen width can be used as left padding, and
@@ -72,13 +81,19 @@
private static final int PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER = 4;
// Workspace
- public final int desiredWorkspaceLeftRightMarginPx;
+ public final int desiredWorkspaceLeftRightOriginalPx;
+ public int desiredWorkspaceLeftRightMarginPx;
+ public final int cellLayoutBorderSpacingOriginalPx;
+ public int cellLayoutBorderSpacingPx;
public final int cellLayoutPaddingLeftRightPx;
public final int cellLayoutBottomPaddingPx;
public final int edgeMarginPx;
public float workspaceSpringLoadShrinkFactor;
public final int workspaceSpringLoadedBottomSpace;
+ public int workspaceTopPadding;
+ public int workspaceBottomPadding;
+
// Workspace page indicator
public final int workspacePageIndicatorHeight;
private final int mWorkspacePageIndicatorOverlapWorkspace;
@@ -93,10 +108,19 @@
public int cellHeightPx;
public int workspaceCellPaddingXPx;
+ public int cellYPaddingPx;
+ public int cellYPaddingOriginalPx;
+
// Folder
+ public float folderLabelTextScale;
+ public int folderLabelTextSizePx;
public int folderIconSizePx;
public int folderIconOffsetYPx;
+ // Folder content
+ public int folderContentPaddingLeftRight;
+ public int folderContentPaddingTop;
+
// Folder cell
public int folderCellWidthPx;
public int folderCellHeightPx;
@@ -140,7 +164,11 @@
public DotRenderer mDotRendererWorkSpace;
public DotRenderer mDotRendererAllApps;
- DeviceProfile(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info,
+ // Taskbar
+ public boolean isTaskbarPresent;
+ public int taskbarSize;
+
+ DeviceProfile(Context context, InvariantDeviceProfile inv, Info info,
Point minSize, Point maxSize, int width, int height, boolean isLandscape,
boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
Point windowPosition) {
@@ -148,18 +176,22 @@
this.inv = inv;
this.isLandscape = isLandscape;
this.isMultiWindowMode = isMultiWindowMode;
+ this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
windowX = windowPosition.x;
windowY = windowPosition.y;
+ isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
+
// Determine sizes.
widthPx = width;
heightPx = height;
+ int nonFinalAvailableHeightPx;
if (isLandscape) {
availableWidthPx = maxSize.x;
- availableHeightPx = minSize.y;
+ nonFinalAvailableHeightPx = minSize.y;
} else {
availableWidthPx = minSize.x;
- availableHeightPx = maxSize.y;
+ nonFinalAvailableHeightPx = maxSize.y;
}
mInfo = info;
@@ -167,26 +199,56 @@
// Constants from resources
float swDPs = Utilities.dpiFromPx(
Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
- isTablet = swDPs >= TABLET_MIN_DPS;
- isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS;
+ boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation);
+ // Tablet UI is built with assumption that simulated landscape is disabled.
+ isTablet = allowRotation && swDPs >= TABLET_MIN_DPS;
+ isLargeTablet = isTablet && swDPs >= LARGE_TABLET_MIN_DPS;
isPhone = !isTablet && !isLargeTablet;
aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
// Some more constants
- this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
-
context = getContext(context, info, isVerticalBarLayout()
? Configuration.ORIENTATION_LANDSCAPE
: Configuration.ORIENTATION_PORTRAIT);
final Resources res = context.getResources();
+ isTaskbarPresent = isTablet && FeatureFlags.ENABLE_TASKBAR.get();
+ if (isTaskbarPresent) {
+ // Taskbar will be added later, but provides bottom insets that we should subtract
+ // from availableHeightPx.
+ taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size);
+ WindowInsets windowInsets = DisplayController.INSTANCE.get(context).getHolder(mInfo.id)
+ .getDisplayContext().getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics().getWindowInsets();
+ int nonOverlappingTaskbarInset =
+ taskbarSize - windowInsets.getSystemWindowInsetBottom();
+ if (nonOverlappingTaskbarInset > 0) {
+ nonFinalAvailableHeightPx -= nonOverlappingTaskbarInset;
+ }
+ }
+ availableHeightPx = nonFinalAvailableHeightPx;
+
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
- desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
+
+ desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : isScalableGrid
+ ? res.getDimensionPixelSize(R.dimen.scalable_grid_left_right_margin)
+ : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
+ desiredWorkspaceLeftRightOriginalPx = desiredWorkspaceLeftRightMarginPx;
+
+ 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);
+
+ setCellLayoutBorderSpacing(pxFromDp(inv.borderSpacing, mInfo.metrics, 1f));
+ cellLayoutBorderSpacingOriginalPx = cellLayoutBorderSpacingPx;
int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet
? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;
- int cellLayoutPadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
+ int cellLayoutPadding = isScalableGrid
+ ? 0
+ : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
if (isLandscape) {
cellLayoutPaddingLeftRightPx = 0;
cellLayoutBottomPaddingPx = cellLayoutPadding;
@@ -217,22 +279,31 @@
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;
- hotseatBarSizePx = ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics)
+ int hotseatExtraVerticalSize =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
+ hotseatBarSizePx = pxFromDp(inv.iconSize, mInfo.metrics, 1f)
+ (isVerticalBarLayout()
? (hotseatBarSidePaddingStartPx + hotseatBarSidePaddingEndPx)
- : (res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size)
- + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx));
+ : (hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx
+ + (isScalableGrid ? 0 : hotseatExtraVerticalSize)));
// Calculate all of the remaining variables.
- updateAvailableDimensions(res);
-
+ int extraSpace = updateAvailableDimensions(res);
// Now that we have all of the variables calculated, we can tune certain sizes.
- if (!isVerticalBarLayout() && isPhone && isTallDevice) {
+ if (isScalableGrid) {
+ DevicePadding padding = inv.devicePaddings.getDevicePadding(extraSpace);
+ workspaceTopPadding = padding.getWorkspaceTopPadding(extraSpace);
+ workspaceBottomPadding = padding.getWorkspaceBottomPadding(extraSpace);
+
+ float hotseatBarBottomPadding = padding.getHotseatBottomPadding(extraSpace);
+ hotseatBarSizePx += hotseatBarBottomPadding;
+ hotseatBarBottomPaddingPx += hotseatBarBottomPadding;
+ } else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
// We increase the hotseat size when there is extra space.
// 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.
- int extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
+ extraSpace = getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2
- workspacePageIndicatorHeight;
hotseatBarSizePx += extraSpace;
hotseatBarBottomPaddingPx += extraSpace;
@@ -250,6 +321,30 @@
IconShape.DEFAULT_PATH_SIZE);
}
+ private void setCellLayoutBorderSpacing(int borderSpacing) {
+ if (isScalableGrid) {
+ cellLayoutBorderSpacingPx = borderSpacing;
+ folderContentPaddingLeftRight = borderSpacing;
+ folderContentPaddingTop = borderSpacing;
+ } else {
+ cellLayoutBorderSpacingPx = 0;
+ }
+ }
+
+ /**
+ * We inset the widget padding added by the system and instead rely on the border spacing
+ * between cells to create reliable consistency between widgets
+ */
+ public boolean shouldInsetWidgets() {
+ Rect widgetPadding = inv.defaultWidgetPadding;
+
+ // Check all sides to ensure that the widget won't overlap into another cell.
+ return cellLayoutBorderSpacingPx > widgetPadding.left
+ && cellLayoutBorderSpacingPx > widgetPadding.top
+ && cellLayoutBorderSpacingPx > widgetPadding.right
+ && cellLayoutBorderSpacingPx > widgetPadding.bottom;
+ }
+
public Builder toBuilder(Context context) {
Point size = new Point(availableWidthPx, availableHeightPx);
return new Builder(context, inv, mInfo)
@@ -325,17 +420,46 @@
+ topBottomPadding * 2;
}
- private void updateAvailableDimensions(Resources res) {
+ /**
+ * Returns the amount of extra (or unused) vertical space.
+ */
+ private int updateAvailableDimensions(Resources res) {
updateIconSize(1f, res);
- // Check to see if the icons fit within the available height. If not, then scale down.
- float usedHeight = (cellHeightPx * inv.numRows);
- int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
- if (usedHeight > maxHeight) {
- float scale = maxHeight / usedHeight;
- updateIconSize(scale, res);
+ Point workspacePadding = getTotalWorkspacePadding();
+
+ // Check to see if the icons fit within the available height.
+ float usedHeight = getCellLayoutHeight();
+ final int maxHeight = availableHeightPx - workspacePadding.y;
+ float extraHeight = Math.max(0, maxHeight - usedHeight);
+ float scaleY = maxHeight / usedHeight;
+ boolean shouldScale = scaleY < 1f;
+
+ float scaleX = 1f;
+ if (isScalableGrid) {
+ // We scale to fit the cellWidth and cellHeight in the available space.
+ // The benefit of scalable grids is that we can get consistent aspect ratios between
+ // devices.
+ float usedWidth = (cellWidthPx * inv.numColumns)
+ + (cellLayoutBorderSpacingPx * (inv.numColumns - 1))
+ + (desiredWorkspaceLeftRightMarginPx * 2);
+ // We do not subtract padding here, as we also scale the workspace padding if needed.
+ scaleX = availableWidthPx / usedWidth;
+ shouldScale = true;
}
+
+ if (shouldScale) {
+ float scale = Math.min(scaleX, scaleY);
+ updateIconSize(scale, res);
+ extraHeight = Math.max(0, maxHeight - getCellLayoutHeight());
+ }
+
updateAvailableFolderCellDimensions(res);
+ return Math.round(extraHeight);
+ }
+
+ private int getCellLayoutHeight() {
+ return (cellHeightPx * inv.numRows) + (cellLayoutBorderSpacingPx * (inv.numRows - 1));
}
/**
@@ -343,31 +467,41 @@
* iconTextSizePx, iconDrawablePaddingPx, cellWidth/Height, allApps* variants,
* hotseat sizes, workspaceSpringLoadedShrinkFactor, folderIconSizePx, and folderIconOffsetYPx.
*/
- private void updateIconSize(float scale, Resources res) {
+ public void updateIconSize(float scale, Resources res) {
// Workspace
final boolean isVerticalLayout = isVerticalBarLayout();
float invIconSizeDp = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
- iconSizePx = Math.max(1, (int) (ResourceUtils.pxFromDp(invIconSizeDp, mInfo.metrics)
- * scale));
+ iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mInfo.metrics, scale));
iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, mInfo.metrics) * scale);
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
- cellHeightPx = iconSizePx + iconDrawablePaddingPx
- + Utilities.calculateTextHeight(iconTextSizePx);
- int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
- if (iconDrawablePaddingPx > cellYPadding && !isVerticalLayout
- && !isMultiWindowMode) {
- // Ensures that the label is closer to its corresponding icon. This is not an issue
- // with vertical bar layout or multi-window mode since the issue is handled separately
- // with their calls to {@link #adjustToHideWorkspaceLabels}.
- cellHeightPx -= (iconDrawablePaddingPx - cellYPadding);
- iconDrawablePaddingPx = cellYPadding;
+ setCellLayoutBorderSpacing((int) (cellLayoutBorderSpacingOriginalPx * scale));
+
+ if (isScalableGrid) {
+ cellWidthPx = pxFromDp(inv.minCellWidth, mInfo.metrics, scale);
+ cellHeightPx = pxFromDp(inv.minCellHeight, mInfo.metrics, scale);
+ int cellContentHeight = iconSizePx + iconDrawablePaddingPx
+ + Utilities.calculateTextHeight(iconTextSizePx);
+ cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
+ desiredWorkspaceLeftRightMarginPx = (int) (desiredWorkspaceLeftRightOriginalPx * scale);
+ } else {
+ cellWidthPx = iconSizePx + iconDrawablePaddingPx;
+ cellHeightPx = iconSizePx + iconDrawablePaddingPx
+ + Utilities.calculateTextHeight(iconTextSizePx);
+ int cellPaddingY = (getCellSize().y - cellHeightPx) / 2;
+ if (iconDrawablePaddingPx > cellPaddingY && !isVerticalLayout
+ && !isMultiWindowMode) {
+ // Ensures that the label is closer to its corresponding icon. This is not an issue
+ // with vertical bar layout or multi-window mode since the issue is handled
+ // separately with their calls to {@link #adjustToHideWorkspaceLabels}.
+ cellHeightPx -= (iconDrawablePaddingPx - cellPaddingY);
+ iconDrawablePaddingPx = cellPaddingY;
+ }
}
- cellWidthPx = iconSizePx + iconDrawablePaddingPx;
// All apps
if (allAppsHasDifferentNumColumns()) {
- allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, mInfo.metrics);
+ allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mInfo.metrics);
allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mInfo.metrics);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
// We use 4 below to ensure labels are closer to their corresponding icon.
@@ -411,25 +545,26 @@
}
private void updateAvailableFolderCellDimensions(Resources res) {
- int folderBottomPanelSize = res.getDimensionPixelSize(R.dimen.folder_label_padding_top)
- + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
- + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
-
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;
+ float contentUsedHeight = folderCellHeightPx * inv.numFolderRows
+ + ((inv.numFolderRows - 1) * cellLayoutBorderSpacingPx);
int contentMaxHeight = availableHeightPx - totalWorkspacePadding.y - folderBottomPanelSize
- - folderMargin;
+ - folderMargin - folderContentPaddingTop;
float scaleY = contentMaxHeight / contentUsedHeight;
// Check if the icons fit within the available width.
- float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns;
- int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin;
+ float contentUsedWidth = folderCellWidthPx * inv.numFolderColumns
+ + ((inv.numFolderColumns - 1) * cellLayoutBorderSpacingPx);
+ int contentMaxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin
+ - folderContentPaddingLeftRight * 2;
float scaleX = contentMaxWidth / contentUsedWidth;
float scale = Math.min(scaleX, scaleY);
@@ -439,9 +574,10 @@
}
private void updateFolderCellSize(float scale, Resources res) {
- folderChildIconSizePx = (int) (ResourceUtils.pxFromDp(inv.iconSize, mInfo.metrics) * scale);
- folderChildTextSizePx =
- (int) (res.getDimensionPixelSize(R.dimen.folder_child_text_size) * scale);
+ float invIconSizeDp = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+ folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mInfo.metrics, scale));
+ folderChildTextSizePx = pxFromDp(inv.iconTextSize, mInfo.metrics, scale);
+ folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
int cellPaddingX = (int) (res.getDimensionPixelSize(R.dimen.folder_cell_x_padding) * scale);
@@ -476,9 +612,9 @@
// not matter.
Point padding = getTotalWorkspacePadding();
result.x = calculateCellWidth(availableWidthPx - padding.x
- - cellLayoutPaddingLeftRightPx * 2, numColumns);
+ - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, numColumns);
result.y = calculateCellHeight(availableHeightPx - padding.y
- - cellLayoutBottomPaddingPx, numRows);
+ - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, numRows);
return result;
}
@@ -505,8 +641,9 @@
padding.right = hotseatBarSizePx;
}
} else {
- int paddingBottom = hotseatBarSizePx + workspacePageIndicatorHeight
- - mWorkspacePageIndicatorOverlapWorkspace;
+ int hotseatTop = isTaskbarPresent ? taskbarSize : hotseatBarSizePx;
+ int paddingBottom = hotseatTop + workspacePageIndicatorHeight
+ + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
@@ -515,15 +652,16 @@
((inv.numColumns - 1) * cellWidthPx)));
availablePaddingX = (int) Math.min(availablePaddingX,
widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
+ int hotseatVerticalPadding = isTaskbarPresent ? 0
+ : hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom
- - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
- - hotseatBarBottomPaddingPx);
+ - (2 * inv.numRows * cellHeightPx) - hotseatVerticalPadding);
padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
} else {
// Pad the top and bottom of the workspace with search/hotseat bar sizes
padding.set(desiredWorkspaceLeftRightMarginPx,
- edgeMarginPx,
+ workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx),
desiredWorkspaceLeftRightMarginPx,
paddingBottom);
}
@@ -578,11 +716,11 @@
}
}
- public static int calculateCellWidth(int width, int countX) {
- return width / countX;
+ public static int calculateCellWidth(int width, int borderSpacing, int countX) {
+ return (width - ((countX - 1) * borderSpacing)) / countX;
}
- public static int calculateCellHeight(int height, int countY) {
- return height / countY;
+ public static int calculateCellHeight(int height, int borderSpacing, int countY) {
+ return (height - ((countY - 1) * borderSpacing)) / countY;
}
/**
@@ -606,8 +744,14 @@
*/
public boolean updateIsSeascape(Context context) {
if (isVerticalBarLayout()) {
- boolean isSeascape = DefaultDisplay.INSTANCE.get(context).getInfo().rotation
- == Surface.ROTATION_270;
+ // Check an up-to-date info.
+ DisplayController.Info displayInfo = DisplayController.getDefaultDisplay(context)
+ .createInfoForContext(context);
+ if (displayInfo == null) {
+ return false;
+ }
+
+ boolean isSeascape = displayInfo.rotation == Surface.ROTATION_270;
if (mIsSeascape != isSeascape) {
mIsSeascape = isSeascape;
return true;
@@ -638,7 +782,7 @@
}
}
- private static Context getContext(Context c, DefaultDisplay.Info info, int orientation) {
+ private static Context getContext(Context c, Info info, int orientation) {
Configuration config = new Configuration(c.getResources().getConfiguration());
config.orientation = orientation;
config.densityDpi = info.metrics.densityDpi;
@@ -662,7 +806,7 @@
public static class Builder {
private Context mContext;
private InvariantDeviceProfile mInv;
- private DefaultDisplay.Info mInfo;
+ private Info mInfo;
private final Point mWindowPosition = new Point();
private Point mMinSize, mMaxSize;
@@ -672,7 +816,7 @@
private boolean mIsMultiWindowMode = false;
private boolean mTransposeLayoutWithOrientation;
- public Builder(Context context, InvariantDeviceProfile inv, DefaultDisplay.Info info) {
+ public Builder(Context context, InvariantDeviceProfile inv, Info info) {
mContext = context;
mInv = inv;
mInfo = info;
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index d4d7b99..ba227d4 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -19,12 +19,11 @@
import android.view.View;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
/**
* Interface defining an object that can originate a drag.
*/
-public interface DragSource extends LogContainerProvider {
+public interface DragSource {
/**
* A callback made back to the source after an item from this source has been dropped on a
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index b27abc4..70d8476 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -79,9 +79,7 @@
public DraggableView originalView = null;
/** Used for matching DROP event with its corresponding DRAG event on the server side. */
- public final InstanceId logInstanceId =
- new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
- .newInstanceId();
+ public final InstanceId logInstanceId = new InstanceIdSequence().newInstanceId();
public DragObject(Context context) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
@@ -113,18 +111,6 @@
return res;
}
-
-
- /**
- * This is used to determine if an object is dropped at a different location than it was
- * dragged from
- */
- public boolean isMoved() {
- return dragInfo.cellX != originalDragInfo.cellX
- || dragInfo.cellY != originalDragInfo.cellY
- || dragInfo.screenId != originalDragInfo.screenId
- || dragInfo.container != originalDragInfo.container;
- }
}
/**
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index ca001a3..c768493 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -131,7 +131,10 @@
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
- if (mIsVertical) {
+ int visibleCount = getVisibleButtonsCount();
+ if (visibleCount == 0) {
+ // do nothing
+ } else if (mIsVertical) {
int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
@@ -142,7 +145,6 @@
}
}
} else {
- int visibleCount = getVisibleButtonsCount();
int availableWidth = width / visibleCount;
boolean textVisible = true;
for (ButtonDropTarget buttons : mDropTargets) {
@@ -165,7 +167,10 @@
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mIsVertical) {
+ int visibleCount = getVisibleButtonsCount();
+ if (visibleCount == 0) {
+ // do nothing
+ } else if (mIsVertical) {
int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
int start = gap;
int end;
@@ -178,7 +183,6 @@
}
}
} else {
- int visibleCount = getVisibleButtonsCount();
int frameSize = (right - left) / visibleCount;
int start = frameSize / 2;
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index d64967b..02c6162 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -24,6 +24,7 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.UiThreadHelper;
@@ -130,6 +131,10 @@
public void reset() {
if (!TextUtils.isEmpty(getText())) {
setText("");
+ } else {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ return;
+ }
}
if (isFocused()) {
View nextFocus = focusSearch(View.FOCUS_DOWN);
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index d3b86de..b1fe4a2 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -33,6 +33,8 @@
import android.graphics.drawable.Drawable;
import android.util.Property;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -54,6 +56,8 @@
protected Bitmap mBitmap;
protected final int mIconColor;
+ @Nullable private ColorFilter mColorFilter;
+
private boolean mIsPressed;
private boolean mIsDisabled;
private float mDisabledAlpha = 1f;
@@ -115,7 +119,8 @@
@Override
public void setColorFilter(ColorFilter cf) {
- // No op
+ mColorFilter = cf;
+ updateFilter();
}
@Override
@@ -265,21 +270,21 @@
* Updates the paint to reflect the current brightness and saturation.
*/
protected void updateFilter() {
- mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null);
+ mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter);
invalidateSelf();
}
@Override
public ConstantState getConstantState() {
- return new MyConstantState(mBitmap, mIconColor, mIsDisabled);
+ return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled);
}
- protected static class MyConstantState extends ConstantState {
+ protected static class FastBitmapConstantState extends ConstantState {
protected final Bitmap mBitmap;
protected final int mIconColor;
protected final boolean mIsDisabled;
- public MyConstantState(Bitmap bitmap, int color, boolean isDisabled) {
+ public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) {
mBitmap = bitmap;
mIconColor = color;
mIsDisabled = isDisabled;
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index 6c5bc40..a199a57 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -15,7 +15,7 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
deleted file mode 100644
index e5aecf7..0000000
--- a/src/com/android/launcher3/FocusHelper.java
+++ /dev/null
@@ -1,567 +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;
-
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.SoundEffectConstants;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderPagedView;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.util.FocusLogic;
-import com.android.launcher3.util.Thunk;
-
-/**
- * A keyboard listener we set on all the workspace icons.
- */
-class IconKeyEventListener implements View.OnKeyListener {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- return FocusHelper.handleIconKeyEvent(v, keyCode, event);
- }
-}
-
-/**
- * A keyboard listener we set on all the hotseat buttons.
- */
-class HotseatIconKeyEventListener implements View.OnKeyListener {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- return FocusHelper.handleHotseatButtonKeyEvent(v, keyCode, event);
- }
-}
-
-/**
- * A keyboard listener we set on full screen pages (e.g. custom content).
- */
-class FullscreenKeyEventListener implements View.OnKeyListener {
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
- || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_UP) {
- // Handle the key event just like a workspace icon would in these cases. In this case,
- // it will basically act as if there is a single icon in the top left (so you could
- // think of the fullscreen page as a focusable fullscreen widget).
- return FocusHelper.handleIconKeyEvent(v, keyCode, event);
- }
- return false;
- }
-}
-
-/**
- * TODO: Reevaluate if this is still required
- */
-public class FocusHelper {
-
- private static final String TAG = "FocusHelper";
- private static final boolean DEBUG = false;
-
- /**
- * Handles key events in paged folder.
- */
- public static class PagedFolderKeyEventListener implements View.OnKeyListener {
-
- private final Folder mFolder;
-
- public PagedFolderKeyEventListener(Folder folder) {
- mFolder = folder;
- }
-
- @Override
- public boolean onKey(View v, int keyCode, KeyEvent e) {
- boolean consume = FocusLogic.shouldConsume(keyCode);
- if (e.getAction() == KeyEvent.ACTION_UP) {
- return consume;
- }
- if (DEBUG) {
- Log.v(TAG, String.format("Handle ALL Folders keyevent=[%s].",
- KeyEvent.keyCodeToString(keyCode)));
- }
-
- if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (FeatureFlags.IS_STUDIO_BUILD) {
- throw new IllegalStateException("Parent of the focused item is not supported.");
- } else {
- return false;
- }
- }
-
- // Initialize variables.
- final ShortcutAndWidgetContainer itemContainer = (ShortcutAndWidgetContainer) v.getParent();
- final CellLayout cellLayout = (CellLayout) itemContainer.getParent();
-
- final int iconIndex = itemContainer.indexOfChild(v);
- final FolderPagedView pagedView = (FolderPagedView) cellLayout.getParent();
-
- final int pageIndex = pagedView.indexOfChild(cellLayout);
- final int pageCount = pagedView.getPageCount();
- final boolean isLayoutRtl = Utilities.isRtl(v.getResources());
-
- int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
- // Process focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
- pageCount, isLayoutRtl);
- if (newIconIndex == FocusLogic.NOOP) {
- handleNoopKey(keyCode, v);
- return consume;
- }
- ShortcutAndWidgetContainer newParent = null;
- View child = null;
-
- switch (newIconIndex) {
- case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
- case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
- newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
- if (newParent != null) {
- int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
- pagedView.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(
- ((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN)
- ^ newParent.invertLayoutHorizontally()) ? 0 : matrix.length - 1,
- row);
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
- if (newParent != null) {
- pagedView.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(0, 0);
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
- if (newParent != null) {
- pagedView.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(matrix.length - 1, matrix[0].length - 1);
- }
- break;
- case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
- if (newParent != null) {
- pagedView.snapToPage(pageIndex + 1);
- child = newParent.getChildAt(0, 0);
- }
- break;
- case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
- case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
- newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
- if (newParent != null) {
- pagedView.snapToPage(pageIndex + 1);
- child = FocusLogic.getAdjacentChildInNextFolderPage(
- newParent, v, newIconIndex);
- }
- break;
- case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
- child = cellLayout.getChildAt(0, 0);
- break;
- case FocusLogic.CURRENT_PAGE_LAST_ITEM:
- child = pagedView.getLastItem();
- break;
- default: // Go to some item on the current page.
- child = itemContainer.getChildAt(newIconIndex);
- break;
- }
- if (child != null) {
- child.requestFocus();
- playSoundEffect(keyCode, v);
- } else {
- handleNoopKey(keyCode, v);
- }
- return consume;
- }
-
- public void handleNoopKey(int keyCode, View v) {
- if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
- mFolder.mFolderName.requestFocus();
- playSoundEffect(keyCode, v);
- }
- }
- }
-
- /**
- * Handles key events in the workspace hotseat (bottom of the screen).
- * <p>Currently we don't special case for the phone UI in different orientations, even though
- * the hotseat is on the side in landscape mode. This is to ensure that accessibility
- * consistency is maintained across rotations.
- */
- static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e) {
- boolean consume = FocusLogic.shouldConsume(keyCode);
- if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
- return consume;
- }
-
- final Launcher launcher = Launcher.getLauncher(v.getContext());
- final DeviceProfile profile = launcher.getDeviceProfile();
-
- if (DEBUG) {
- Log.v(TAG, String.format(
- "Handle HOTSEAT BUTTONS keyevent=[%s] on hotseat buttons, isVertical=%s",
- KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
- }
-
- // Initialize the variables.
- final Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
- final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
- final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
-
- final ItemInfo itemInfo = (ItemInfo) v.getTag();
- int pageIndex = workspace.getNextPage();
- int pageCount = workspace.getChildCount();
- int iconIndex = hotseatParent.indexOfChild(v);
- int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
- .getChildAt(iconIndex).getLayoutParams()).cellX;
-
- final CellLayout iconLayout = (CellLayout) workspace.getChildAt(pageIndex);
- if (iconLayout == null) {
- // This check is to guard against cases where key strokes rushes in when workspace
- // child creation/deletion is still in flux. (e.g., during drop or fling
- // animation.)
- return consume;
- }
- final ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
-
- ViewGroup parent = null;
- int[][] matrix = null;
-
- if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
- !profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
- iconIndex += iconParent.getChildCount();
- parent = iconParent;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
- profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
- iconIndex += iconParent.getChildCount();
- parent = iconParent;
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
- profile.isVerticalBarLayout()) {
- keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
- } else {
- // For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
- // matrix extended with hotseat.
- matrix = FocusLogic.createSparseMatrix(hotseatLayout);
- parent = hotseatParent;
- }
-
- // Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
- pageCount, Utilities.isRtl(v.getResources()));
-
- View newIcon = null;
- switch (newIconIndex) {
- case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
- newIcon = parent.getChildAt(0);
- // TODO(hyunyoungs): handle cases where the child is not an icon but
- // a folder or a widget.
- workspace.snapToPage(pageIndex + 1);
- break;
- case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
- newIcon = parent.getChildAt(0);
- // TODO(hyunyoungs): handle cases where the child is not an icon but
- // a folder or a widget.
- workspace.snapToPage(pageIndex - 1);
- break;
- case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
- newIcon = parent.getChildAt(parent.getChildCount() - 1);
- // TODO(hyunyoungs): handle cases where the child is not an icon but
- // a folder or a widget.
- workspace.snapToPage(pageIndex - 1);
- break;
- case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
- case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
- // Go to the previous page but keep the focus on the same hotseat icon.
- workspace.snapToPage(pageIndex - 1);
- break;
- case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
- case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
- // Go to the next page but keep the focus on the same hotseat icon.
- workspace.snapToPage(pageIndex + 1);
- break;
- }
- if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
- newIconIndex -= iconParent.getChildCount();
- }
- if (parent != null) {
- if (newIcon == null && newIconIndex >= 0) {
- newIcon = parent.getChildAt(newIconIndex);
- }
- if (newIcon != null) {
- newIcon.requestFocus();
- playSoundEffect(keyCode, v);
- }
- }
- return consume;
- }
-
- /**
- * Handles key events in a workspace containing icons.
- */
- static boolean handleIconKeyEvent(View v, int keyCode, KeyEvent e) {
- boolean consume = FocusLogic.shouldConsume(keyCode);
- if (e.getAction() == KeyEvent.ACTION_UP || !consume) {
- return consume;
- }
-
- Launcher launcher = Launcher.getLauncher(v.getContext());
- DeviceProfile profile = launcher.getDeviceProfile();
-
- if (DEBUG) {
- Log.v(TAG, String.format("Handle WORKSPACE ICONS keyevent=[%s] isVerticalBar=%s",
- KeyEvent.keyCodeToString(keyCode), profile.isVerticalBarLayout()));
- }
-
- // Initialize the variables.
- ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
- CellLayout iconLayout = (CellLayout) parent.getParent();
- final Workspace workspace = (Workspace) iconLayout.getParent();
- final ViewGroup dragLayer = (ViewGroup) workspace.getParent();
- final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.drop_target_bar);
- final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
-
- final ItemInfo itemInfo = (ItemInfo) v.getTag();
- final int iconIndex = parent.indexOfChild(v);
- final int pageIndex = workspace.indexOfChild(iconLayout);
- final int pageCount = workspace.getChildCount();
-
- CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
- ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
- int[][] matrix;
-
- // KEYCODE_DPAD_DOWN in portrait (KEYCODE_DPAD_RIGHT in landscape) is the only key allowed
- // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
- // with the hotseat.
- if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
- } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
- profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
- } else {
- matrix = FocusLogic.createSparseMatrix(iconLayout);
- }
-
- // Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
- pageCount, Utilities.isRtl(v.getResources()));
- boolean isRtl = Utilities.isRtl(v.getResources());
- View newIcon = null;
- CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex);
- switch (newIconIndex) {
- case FocusLogic.NOOP:
- if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
- newIcon = tabs;
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
- case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
- int newPageIndex = pageIndex - 1;
- if (newIconIndex == FocusLogic.NEXT_PAGE_RIGHT_COLUMN) {
- newPageIndex = pageIndex + 1;
- }
- int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
- parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
- if (parent != null) {
- iconLayout = (CellLayout) parent.getParent();
- matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout,
- iconLayout.getCountX(), row);
- newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
- newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
- if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
- newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
- isRtl);
- } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
- newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
- isRtl);
- } else {
- newIcon = parent.getChildAt(newIconIndex);
- }
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
- newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
- if (newIcon == null) {
- // Check the hotseat if no focusable item was found on the workspace.
- newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
- workspace.snapToPage(pageIndex - 1);
- }
- break;
- case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, isRtl);
- break;
- case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, isRtl);
- break;
- case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
- case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
- newPageIndex = pageIndex + 1;
- if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
- newPageIndex = pageIndex - 1;
- }
- row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
- parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
- if (parent != null) {
- iconLayout = (CellLayout) parent.getParent();
- matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout, -1, row);
- newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
- newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
- if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
- newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
- isRtl);
- } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
- newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
- isRtl);
- } else {
- newIcon = parent.getChildAt(newIconIndex);
- }
- }
- break;
- case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
- newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
- if (newIcon == null) {
- // Check the hotseat if no focusable item was found on the workspace.
- newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
- }
- break;
- case FocusLogic.CURRENT_PAGE_LAST_ITEM:
- newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
- if (newIcon == null) {
- // Check the hotseat if no focusable item was found on the workspace.
- newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout, isRtl);
- }
- break;
- default:
- // current page, some item.
- if (0 <= newIconIndex && newIconIndex < parent.getChildCount()) {
- newIcon = parent.getChildAt(newIconIndex);
- } else if (parent.getChildCount() <= newIconIndex &&
- newIconIndex < parent.getChildCount() + hotseatParent.getChildCount()) {
- newIcon = hotseatParent.getChildAt(newIconIndex - parent.getChildCount());
- }
- break;
- }
- if (newIcon != null) {
- newIcon.requestFocus();
- playSoundEffect(keyCode, v);
- }
- return consume;
- }
-
- //
- // Helper methods.
- //
-
- /**
- * Private helper method to get the CellLayoutChildren given a CellLayout index.
- */
- @Thunk static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
- ViewGroup container, int i) {
- CellLayout parent = (CellLayout) container.getChildAt(i);
- return parent.getShortcutsAndWidgets();
- }
-
- /**
- * Helper method to be used for playing sound effects.
- */
- @Thunk static void playSoundEffect(int keyCode, View v) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_PAGE_DOWN:
- case KeyEvent.KEYCODE_MOVE_END:
- v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_PAGE_UP:
- case KeyEvent.KEYCODE_MOVE_HOME:
- v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
- break;
- default:
- break;
- }
- }
-
- private static View handlePreviousPageLastItem(Workspace workspace, CellLayout hotseatLayout,
- int pageIndex, boolean isRtl) {
- if (pageIndex - 1 < 0) {
- return null;
- }
- CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
- View newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
- if (newIcon == null) {
- // Check the hotseat if no focusable item was found on the workspace.
- newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout,isRtl);
- workspace.snapToPage(pageIndex - 1);
- }
- return newIcon;
- }
-
- private static View handleNextPageFirstItem(Workspace workspace, CellLayout hotseatLayout,
- int pageIndex, boolean isRtl) {
- if (pageIndex + 1 >= workspace.getPageCount()) {
- return null;
- }
- CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex + 1);
- View newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
- if (newIcon == null) {
- // Check the hotseat if no focusable item was found on the workspace.
- newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
- workspace.snapToPage(pageIndex + 1);
- }
- return newIcon;
- }
-
- private static View getFirstFocusableIconInReadingOrder(CellLayout cellLayout, boolean isRtl) {
- View icon;
- int countX = cellLayout.getCountX();
- for (int y = 0; y < cellLayout.getCountY(); y++) {
- int increment = isRtl ? -1 : 1;
- for (int x = isRtl ? countX - 1 : 0; 0 <= x && x < countX; x += increment) {
- if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
- return icon;
- }
- }
- }
- return null;
- }
-
- private static View getFirstFocusableIconInReverseReadingOrder(CellLayout cellLayout,
- boolean isRtl) {
- View icon;
- int countX = cellLayout.getCountX();
- for (int y = cellLayout.getCountY() - 1; y >= 0; y--) {
- int increment = isRtl ? 1 : -1;
- for (int x = isRtl ? 0 : countX - 1; 0 <= x && x < countX; x += increment) {
- if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
- return icon;
- }
- }
- }
- return null;
- }
-}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 51f3819..b2112ad 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,30 +16,33 @@
package com.android.launcher3;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import androidx.annotation.Nullable;
-import java.util.ArrayList;
+import com.android.launcher3.config.FeatureFlags;
-public class Hotseat extends CellLayout implements LogContainerProvider, Insettable {
+import java.util.function.Consumer;
+
+/**
+ * View class that represents the bottom row of the home screen.
+ */
+public class Hotseat extends CellLayout implements Insettable {
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mHasVerticalHotseat;
private Workspace mWorkspace;
private boolean mSendTouchToWorkspace;
+ @Nullable
+ private Consumer<Boolean> mOnVisibilityAggregatedCallback;
public Hotseat(Context context) {
this(context, null);
@@ -74,17 +77,9 @@
if (hasVerticalHotseat) {
setGridSize(1, idp.numHotseatIcons);
} else {
- setGridSize(idp.numHotseatIcons, 1);
+ setGridSize(idp.numHotseatIcons, FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 2 : 1);
}
- }
-
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- child.rank = childInfo.rank;
- child.gridX = childInfo.cellX;
- child.gridY = childInfo.cellY;
- parents.add(newContainerTarget(LauncherLogProto.ContainerType.HOTSEAT));
+ showInlineQsb();
}
@Override
@@ -104,10 +99,20 @@
} else {
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
- lp.height = grid.hotseatBarSizePx + insets.bottom;
+ lp.height = grid.isTaskbarPresent
+ ? grid.taskbarSize
+ : grid.hotseatBarSizePx + insets.bottom;
}
- Rect padding = grid.getHotseatLayoutPadding();
- setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ if (!grid.isTaskbarPresent) {
+ // When taskbar is present, we set the padding separately to ensure a seamless visual
+ // handoff between taskbar and hotseat during drag and drop.
+ Rect padding = grid.getHotseatLayoutPadding();
+ int paddingBottom = padding.bottom;
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !grid.isVerticalBarLayout()) {
+ paddingBottom -= grid.hotseatBarBottomPaddingPx;
+ }
+ setPadding(padding.left, padding.top, padding.right, paddingBottom);
+ }
setLayoutParams(lp);
InsettableFrameLayout.dispatchInsets(this, insets);
@@ -144,4 +149,29 @@
}
return event.getY() > getCellHeight();
}
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+
+ if (mOnVisibilityAggregatedCallback != null) {
+ mOnVisibilityAggregatedCallback.accept(isVisible);
+ }
+ }
+
+ /** Sets a callback to be called onVisibilityAggregated */
+ public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
+ mOnVisibilityAggregatedCallback = callback;
+ }
+
+ protected void showInlineQsb() {
+ //Does nothing
+ }
+
+ /**
+ * Returns the first View for which the given itemOperator returns true, or null.
+ */
+ public View getFirstItemMatch(Workspace.ItemOperator itemOperator) {
+ return mWorkspace.getFirstMatch(new CellLayout[] { this }, itemOperator);
+ }
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
deleted file mode 100644
index 62c9b4d..0000000
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ /dev/null
@@ -1,672 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.Executors.MODEL_EXECUTOR;
-
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.Parcelable;
-import android.os.Process;
-import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.Base64;
-import android.util.Log;
-import android.util.Pair;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.GraphicsUtils;
-import com.android.launcher3.icons.LauncherIcons;
-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.pm.UserCache;
-import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.PackageManagerHelper;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.Thunk;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-import org.json.JSONStringer;
-
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-public class InstallShortcutReceiver extends BroadcastReceiver {
-
- public static final int FLAG_ACTIVITY_PAUSED = 1;
- public static final int FLAG_LOADER_RUNNING = 2;
- public static final int FLAG_DRAG_AND_DROP = 4;
-
- // Determines whether to defer installing shortcuts immediately until
- // processAllPendingInstalls() is called.
- private static int sInstallQueueDisabledFlags = 0;
-
- private static final String TAG = "InstallShortcutReceiver";
- private static final boolean DBG = false;
-
- private static final String ACTION_INSTALL_SHORTCUT =
- "com.android.launcher.action.INSTALL_SHORTCUT";
-
- private static final String LAUNCH_INTENT_KEY = "intent.launch";
- private static final String NAME_KEY = "name";
- private static final String ICON_KEY = "icon";
- private static final String ICON_RESOURCE_NAME_KEY = "iconResource";
- private static final String ICON_RESOURCE_PACKAGE_NAME_KEY = "iconResourcePackage";
-
- private static final String APP_SHORTCUT_TYPE_KEY = "isAppShortcut";
- private static final String DEEPSHORTCUT_TYPE_KEY = "isDeepShortcut";
- private static final String APP_WIDGET_TYPE_KEY = "isAppWidget";
- private static final String USER_HANDLE_KEY = "userHandle";
-
- // The set of shortcuts that are pending install
- private static final String APPS_PENDING_INSTALL = "apps_to_install";
-
- public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
- public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
-
- @WorkerThread
- private static void addToQueue(Context context, PendingInstallShortcutInfo info) {
- String encoded = info.encodeToString();
- SharedPreferences prefs = Utilities.getPrefs(context);
- Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
- strings = (strings != null) ? new HashSet<>(strings) : new HashSet<>(1);
- strings.add(encoded);
- prefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
- }
-
- @WorkerThread
- private static void flushQueueInBackground(Context context) {
- if (Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null) {
- // Launcher not loaded
- return;
- }
-
- ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
- SharedPreferences prefs = Utilities.getPrefs(context);
- Set<String> strings = prefs.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) Log.d(TAG, "Getting and clearing APPS_PENDING_INSTALL: " + strings);
- if (strings == null) {
- return;
- }
-
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- for (String encoded : strings) {
- PendingInstallShortcutInfo info = decode(encoded, context);
- if (info == null) {
- continue;
- }
-
- String pkg = getIntentPackage(info.launchIntent);
- if (!TextUtils.isEmpty(pkg)
- && !launcherApps.isPackageEnabled(pkg, info.user)
- && !info.isActivity) {
- if (DBG) {
- Log.d(TAG, "Ignoring shortcut for absent package: " + info.launchIntent);
- }
- continue;
- }
-
- // Generate a shortcut info to add into the model
- installQueue.add(info.getItemInfo());
- }
- prefs.edit().remove(APPS_PENDING_INSTALL).apply();
- if (!installQueue.isEmpty()) {
- LauncherAppState.getInstance(context).getModel()
- .addAndBindAddedWorkspaceItems(installQueue);
- }
- }
-
- public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
- UserHandle user) {
- if (packageNames.isEmpty()) {
- return;
- }
- Preconditions.assertWorkerThread();
-
- SharedPreferences sp = Utilities.getPrefs(context);
- Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
- if (DBG) {
- Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
- + ", removing packages: " + packageNames);
- }
- if (strings == null || ((Collection) strings).isEmpty()) {
- return;
- }
- Set<String> newStrings = new HashSet<>(strings);
- Iterator<String> newStringsIter = newStrings.iterator();
- while (newStringsIter.hasNext()) {
- String encoded = newStringsIter.next();
- try {
- Decoder decoder = new Decoder(encoded, context);
- if (packageNames.contains(getIntentPackage(decoder.launcherIntent)) &&
- user.equals(decoder.user)) {
- newStringsIter.remove();
- }
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
- newStringsIter.remove();
- }
- }
- sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
- }
-
- public void onReceive(Context context, Intent data) {
- if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
- return;
- }
- PendingInstallShortcutInfo info = createPendingInfo(context, data);
- if (info != null) {
- if (!info.isLauncherActivity()) {
- // Since its a custom shortcut, verify that it is safe to launch.
- if (!new PackageManagerHelper(context).hasPermissionForActivity(
- info.launchIntent, null)) {
- // Target cannot be launched, or requires some special permission to launch
- Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
- return;
- }
- }
- queuePendingShortcutInfo(info, context);
- }
- }
-
- /**
- * @return true is 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);
- }
-
- /**
- * Verifies the intent and creates a {@link PendingInstallShortcutInfo}
- */
- private static PendingInstallShortcutInfo createPendingInfo(Context context, Intent data) {
- if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class) ||
- !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
- Intent.ShortcutIconResource.class)) ||
- !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) {
-
- if (DBG) Log.e(TAG, "Invalid install shortcut intent");
- return null;
- }
-
- PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(
- data, Process.myUserHandle(), context);
- if (info.launchIntent == null || info.label == null) {
- if (DBG) Log.e(TAG, "Invalid install shortcut intent");
- return null;
- }
-
- return convertToLauncherActivityIfPossible(info);
- }
-
- public static WorkspaceItemInfo fromShortcutIntent(Context context, Intent data) {
- PendingInstallShortcutInfo info = createPendingInfo(context, data);
- return info == null ? null : (WorkspaceItemInfo) info.getItemInfo().first;
- }
-
- public static void queueShortcut(ShortcutInfo info, Context context) {
- queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
- }
-
- public static void queueWidget(AppWidgetProviderInfo info, int widgetId, Context context) {
- queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId, context), context);
- }
-
- public static void queueApplication(Intent data, UserHandle user, Context context) {
- queuePendingShortcutInfo(new PendingInstallShortcutInfo(data, context, user),
- context);
- }
-
- public static HashSet<ShortcutKey> getPendingShortcuts(Context context) {
- HashSet<ShortcutKey> result = new HashSet<>();
-
- Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
- if (strings == null || ((Collection) strings).isEmpty()) {
- return result;
- }
-
- for (String encoded : strings) {
- try {
- Decoder decoder = new Decoder(encoded, context);
- if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
- result.add(ShortcutKey.fromIntent(decoder.launcherIntent, decoder.user));
- }
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
- }
- }
- return result;
- }
-
- private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
- // Queue the item up for adding if launcher has not loaded properly yet
- MODEL_EXECUTOR.post(() -> addToQueue(context, info));
- flushInstallQueue(context);
- }
-
- public static void enableInstallQueue(int flag) {
- sInstallQueueDisabledFlags |= flag;
- }
- public static void disableAndFlushInstallQueue(int flag, Context context) {
- sInstallQueueDisabledFlags &= ~flag;
- flushInstallQueue(context);
- }
-
- static void flushInstallQueue(Context context) {
- if (sInstallQueueDisabledFlags != 0) {
- return;
- }
- MODEL_EXECUTOR.post(() -> flushQueueInBackground(context));
- }
-
- /**
- * Ensures that we have a valid, non-null name. If the provided name is null, we will return
- * the application name instead.
- */
- @Thunk static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) {
- if (name == null) {
- try {
- PackageManager pm = context.getPackageManager();
- ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0);
- name = info.loadLabel(pm);
- } catch (PackageManager.NameNotFoundException nnfe) {
- return "";
- }
- }
- return name;
- }
-
- private static class PendingInstallShortcutInfo {
-
- final boolean isActivity;
- @Nullable final ShortcutInfo shortcutInfo;
- @Nullable final AppWidgetProviderInfo providerInfo;
-
- @Nullable final Intent data;
- final Context mContext;
- final Intent launchIntent;
- final String label;
- final UserHandle user;
-
- /**
- * Initializes a PendingInstallShortcutInfo received from a different app.
- */
- public PendingInstallShortcutInfo(Intent data, UserHandle user, Context context) {
- isActivity = false;
- shortcutInfo = null;
- providerInfo = null;
-
- this.data = data;
- this.user = user;
- mContext = context;
-
- launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
- label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
- }
-
- /**
- * Initializes a PendingInstallShortcutInfo to represent a launcher target.
- */
- public PendingInstallShortcutInfo(LauncherActivityInfo info, Context context) {
- isActivity = true;
- shortcutInfo = null;
- providerInfo = null;
-
- String packageName = info.getComponentName().getPackageName();
- data = new Intent();
- data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
- new ComponentName(packageName, "")).setPackage(packageName));
- data.putExtra(Intent.EXTRA_SHORTCUT_NAME, info.getLabel());
-
- user = info.getUser();
- mContext = context;
-
- launchIntent = AppInfo.makeLaunchIntent(info);
- label = info.getLabel().toString();
- }
-
- /**
- * Initializes a PendingInstallShortcutInfo to represent a launcher target.
- */
- public PendingInstallShortcutInfo(Intent data, Context context, UserHandle user) {
- isActivity = true;
- shortcutInfo = null;
- providerInfo = null;
-
- this.data = data;
- this.user = user;
- mContext = context;
-
- launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
- label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
- }
-
- /**
- * Initializes a PendingInstallShortcutInfo to represent a launcher target.
- */
- public PendingInstallShortcutInfo(ShortcutInfo info, Context context) {
- isActivity = false;
- shortcutInfo = info;
- providerInfo = null;
-
- data = null;
- mContext = context;
- user = info.getUserHandle();
-
- launchIntent = ShortcutKey.makeIntent(info);
- label = info.getShortLabel().toString();
- }
-
- /**
- * Initializes a PendingInstallShortcutInfo to represent a launcher target.
- */
- public PendingInstallShortcutInfo(
- AppWidgetProviderInfo info, int widgetId, Context context) {
- isActivity = false;
- shortcutInfo = null;
- providerInfo = info;
-
- data = null;
- mContext = context;
- user = info.getProfile();
-
- launchIntent = new Intent().setComponent(info.provider)
- .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
- label = info.label;
- }
-
- public String encodeToString() {
- try {
- if (shortcutInfo != null) {
- // If it a launcher target, we only need component name, and user to
- // recreate this.
- return new JSONStringer()
- .object()
- .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
- .key(DEEPSHORTCUT_TYPE_KEY).value(true)
- .key(USER_HANDLE_KEY).value(UserCache.INSTANCE.get(mContext)
- .getSerialNumberForUser(user))
- .endObject().toString();
- } else if (providerInfo != null) {
- // If it a launcher target, we only need component name, and user to
- // recreate this.
- return new JSONStringer()
- .object()
- .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
- .key(APP_WIDGET_TYPE_KEY).value(true)
- .key(USER_HANDLE_KEY).value(UserCache.INSTANCE.get(mContext)
- .getSerialNumberForUser(user))
- .endObject().toString();
- }
-
- if (launchIntent.getAction() == null) {
- launchIntent.setAction(Intent.ACTION_VIEW);
- } else if (launchIntent.getAction().equals(Intent.ACTION_MAIN) &&
- launchIntent.getCategories() != null &&
- launchIntent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
- launchIntent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
-
- // This name is only used for comparisons and notifications, so fall back to activity
- // name if not supplied
- String name = ensureValidName(mContext, launchIntent, label).toString();
- Bitmap icon = data == null ? null
- : data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
- Intent.ShortcutIconResource iconResource = data == null ? null
- : data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
-
- // Only encode the parameters which are supported by the API.
- JSONStringer json = new JSONStringer()
- .object()
- .key(LAUNCH_INTENT_KEY).value(launchIntent.toUri(0))
- .key(NAME_KEY).value(name)
- .key(USER_HANDLE_KEY).value(
- UserCache.INSTANCE.get(mContext).getSerialNumberForUser(user))
- .key(APP_SHORTCUT_TYPE_KEY).value(isActivity);
- if (icon != null) {
- byte[] iconByteArray = GraphicsUtils.flattenBitmap(icon);
- if (iconByteArray != null) {
- json = json.key(ICON_KEY).value(
- Base64.encodeToString(
- iconByteArray, 0, iconByteArray.length, Base64.DEFAULT));
- }
- }
- if (iconResource != null) {
- json = json.key(ICON_RESOURCE_NAME_KEY).value(iconResource.resourceName);
- json = json.key(ICON_RESOURCE_PACKAGE_NAME_KEY)
- .value(iconResource.packageName);
- }
- return json.endObject().toString();
- } catch (JSONException e) {
- Log.d(TAG, "Exception when adding shortcut: " + e);
- return null;
- }
- }
-
- public Pair<ItemInfo, Object> getItemInfo() {
- if (isActivity) {
- WorkspaceItemInfo si = createWorkspaceItemInfo(data, user,
- LauncherAppState.getInstance(mContext));
- si.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
- si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
- return Pair.create(si, null);
- } else if (shortcutInfo != null) {
- WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, mContext);
- LauncherAppState.getInstance(mContext).getIconCache().getShortcutIcon(
- itemInfo, shortcutInfo);
- return Pair.create(itemInfo, shortcutInfo);
- } else if (providerInfo != null) {
- LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
- .fromProviderInfo(mContext, providerInfo);
- LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
- launchIntent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
- info.provider);
- InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
- widgetInfo.minSpanX = info.minSpanX;
- widgetInfo.minSpanY = info.minSpanY;
- widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
- widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
- return Pair.create(widgetInfo, providerInfo);
- } else {
- WorkspaceItemInfo itemInfo =
- createWorkspaceItemInfo(data, user, LauncherAppState.getInstance(mContext));
- return Pair.create(itemInfo, null);
- }
- }
-
- public boolean isLauncherActivity() {
- return isActivity;
- }
- }
-
- private static String getIntentPackage(Intent intent) {
- return intent.getComponent() == null
- ? intent.getPackage() : intent.getComponent().getPackageName();
- }
-
- private static PendingInstallShortcutInfo decode(String encoded, Context context) {
- try {
- Decoder decoder = new Decoder(encoded, context);
- if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
- LauncherActivityInfo info = context.getSystemService(LauncherApps.class)
- .resolveActivity(decoder.launcherIntent, decoder.user);
- if (info != null) {
- return new PendingInstallShortcutInfo(info, context);
- }
- } else if (decoder.optBoolean(DEEPSHORTCUT_TYPE_KEY)) {
- List<ShortcutInfo> si = ShortcutKey.fromIntent(decoder.launcherIntent, decoder.user)
- .buildRequest(context)
- .query(ShortcutRequest.ALL);
- if (si.isEmpty()) {
- return null;
- } else {
- return new PendingInstallShortcutInfo(si.get(0), context);
- }
- } else if (decoder.optBoolean(APP_WIDGET_TYPE_KEY)) {
- int widgetId = decoder.launcherIntent
- .getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
- AppWidgetProviderInfo info = AppWidgetManager.getInstance(context)
- .getAppWidgetInfo(widgetId);
- if (info == null || !info.provider.equals(decoder.launcherIntent.getComponent()) ||
- !info.getProfile().equals(decoder.user)) {
- return null;
- }
- return new PendingInstallShortcutInfo(info, widgetId, context);
- }
-
- Intent data = new Intent();
- data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, decoder.launcherIntent);
- data.putExtra(Intent.EXTRA_SHORTCUT_NAME, decoder.getString(NAME_KEY));
-
- String iconBase64 = decoder.optString(ICON_KEY);
- String iconResourceName = decoder.optString(ICON_RESOURCE_NAME_KEY);
- String iconResourcePackageName = decoder.optString(ICON_RESOURCE_PACKAGE_NAME_KEY);
- if (iconBase64 != null && !iconBase64.isEmpty()) {
- byte[] iconArray = Base64.decode(iconBase64, Base64.DEFAULT);
- Bitmap b = BitmapFactory.decodeByteArray(iconArray, 0, iconArray.length);
- data.putExtra(Intent.EXTRA_SHORTCUT_ICON, b);
- } else if (iconResourceName != null && !iconResourceName.isEmpty()) {
- Intent.ShortcutIconResource iconResource =
- new Intent.ShortcutIconResource();
- iconResource.resourceName = iconResourceName;
- iconResource.packageName = iconResourcePackageName;
- data.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
- }
-
- if (decoder.optBoolean(APP_SHORTCUT_TYPE_KEY)) {
- return new PendingInstallShortcutInfo(data, context, decoder.user);
- } else {
- return new PendingInstallShortcutInfo(data, decoder.user, context);
- }
- } catch (JSONException | URISyntaxException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
- }
- return null;
- }
-
- private static class Decoder extends JSONObject {
- public final Intent launcherIntent;
- public final UserHandle user;
-
- private Decoder(String encoded, Context context) throws JSONException, URISyntaxException {
- super(encoded);
- launcherIntent = Intent.parseUri(getString(LAUNCH_INTENT_KEY), 0);
- user = has(USER_HANDLE_KEY) ? UserCache.INSTANCE.get(context)
- .getUserForSerialNumber(getLong(USER_HANDLE_KEY))
- : Process.myUserHandle();
- if (user == null) {
- throw new JSONException("Invalid user");
- }
- }
- }
-
- /**
- * Tries to create a new PendingInstallShortcutInfo which represents the same target,
- * but is an app target and not a shortcut.
- * @return the newly created info or the original one.
- */
- private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible(
- PendingInstallShortcutInfo original) {
- if (original.isLauncherActivity()) {
- // Already an activity target
- return original;
- }
- if (!PackageManagerHelper.isLauncherAppTarget(original.launchIntent)) {
- return original;
- }
-
- LauncherActivityInfo info = original.mContext.getSystemService(LauncherApps.class)
- .resolveActivity(original.launchIntent, original.user);
- if (info == null) {
- return original;
- }
- // Ignore any conflicts in the label name, as that can change based on locale.
- return new PendingInstallShortcutInfo(info, original.mContext);
- }
-
- private static WorkspaceItemInfo createWorkspaceItemInfo(Intent data, UserHandle user,
- LauncherAppState app) {
- if (data == null) {
- Log.e(TAG, "Can't construct WorkspaceItemInfo with null data");
- return null;
- }
-
- Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
- String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
- Parcelable bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
-
- if (intent == null) {
- // If the intent is null, return null as we can't construct a valid WorkspaceItemInfo
- Log.e(TAG, "Can't construct WorkspaceItemInfo with null intent");
- return null;
- }
-
- final WorkspaceItemInfo info = new WorkspaceItemInfo();
- info.user = user;
-
- BitmapInfo iconInfo = null;
- LauncherIcons li = LauncherIcons.obtain(app.getContext());
- if (bitmap instanceof Bitmap) {
- iconInfo = li.createIconBitmap((Bitmap) bitmap);
- } else {
- Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
- if (extra instanceof Intent.ShortcutIconResource) {
- info.iconResource = (Intent.ShortcutIconResource) extra;
- iconInfo = li.createIconBitmap(info.iconResource);
- }
- }
- li.recycle();
-
- if (iconInfo == null) {
- iconInfo = app.getIconCache().getDefaultIcon(info.user);
- }
- info.bitmap = iconInfo;
-
- info.title = Utilities.trim(name);
- info.contentDescription = app.getContext().getPackageManager()
- .getUserBadgedLabel(info.title, info.user);
- info.intent = intent;
- return info;
- }
-
-}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index e39e89c..bb60557 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,7 +18,7 @@
import static com.android.launcher3.Utilities.getDevicePrefs;
import static com.android.launcher3.Utilities.getPointString;
-import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
@@ -48,8 +48,8 @@
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.util.ConfigMonitor;
-import com.android.launcher3.util.DefaultDisplay;
-import com.android.launcher3.util.DefaultDisplay.Info;
+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.Themes;
@@ -111,6 +111,10 @@
public float allAppsIconSize;
public float allAppsIconTextSize;
+ public float minCellHeight;
+ public float minCellWidth;
+ public float borderSpacing;
+
private SparseArray<TypedValue> mExtraAttrs;
/**
@@ -123,6 +127,11 @@
*/
public int numAllAppsColumns;
+ /**
+ * Do not query directly. see {@link DeviceProfile#isScalableGrid}.
+ */
+ protected boolean isScalable;
+
public String dbFile;
public int defaultLayoutId;
int demoModeLayoutId;
@@ -130,6 +139,8 @@
public DeviceProfile landscapeProfile;
public DeviceProfile portraitProfile;
+ public DevicePaddings devicePaddings;
+
public Point defaultWallpaperSize;
public Rect defaultWidgetPadding;
@@ -152,6 +163,10 @@
iconTextSize = p.iconTextSize;
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
+ isScalable = p.isScalable;
+ minCellHeight = p.minCellHeight;
+ minCellWidth = p.minCellWidth;
+ borderSpacing = p.borderSpacing;
dbFile = p.dbFile;
allAppsIconSize = p.allAppsIconSize;
allAppsIconTextSize = p.allAppsIconTextSize;
@@ -159,6 +174,7 @@
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
mOverlayMonitor = p.mOverlayMonitor;
+ devicePaddings = p.devicePaddings;
}
@TargetApi(23)
@@ -173,8 +189,7 @@
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(numColumns, numRows))
.apply();
- mConfigMonitor = new ConfigMonitor(context,
- APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
+ mConfigMonitor = new ConfigMonitor(context, this::onConfigChanged);
mOverlayMonitor = new OverlayMonitor(context);
}
@@ -198,7 +213,7 @@
// Get the display info based on default display and interpolate it to existing display
DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
- DefaultDisplay.INSTANCE.get(context).getInfo(),
+ DisplayController.getDefaultDisplay(context).getInfo(),
getPredefinedDeviceProfiles(context, gridName));
Info myInfo = new Info(context, display);
@@ -211,10 +226,18 @@
result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
result.allAppsIconSize = Math.min(
defaultDisplayOption.allAppsIconSize, myDisplayOption.allAppsIconSize);
+ result.minCellHeight = defaultDisplayOption.minCellHeight;
+ result.minCellWidth = defaultDisplayOption.minCellWidth;
+ result.borderSpacing = defaultDisplayOption.borderSpacing;
+
+ devicePaddings = new DevicePaddings(context);
initGrid(context, myInfo, result);
}
public static String getCurrentGridName(Context context) {
+ if (ENABLE_FOUR_COLUMNS.get()) {
+ return ENABLE_FOUR_COLUMNS.key;
+ }
return Utilities.isGridOptionsEnabled(context)
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
}
@@ -231,16 +254,17 @@
}
private String initGrid(Context context, String gridName) {
- DefaultDisplay.Info displayInfo = DefaultDisplay.INSTANCE.get(context).getInfo();
+ Info displayInfo = DisplayController.getDefaultDisplay(context).getInfo();
ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
+ devicePaddings = new DevicePaddings(context);
initGrid(context, displayInfo, displayOption);
return displayOption.grid.name;
}
private void initGrid(
- Context context, DefaultDisplay.Info displayInfo, DisplayOption displayOption) {
+ Context context, Info displayInfo, DisplayOption displayOption) {
GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
@@ -251,6 +275,7 @@
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
numAllAppsColumns = closestProfile.numAllAppsColumns;
+ isScalable = closestProfile.isScalable;
mExtraAttrs = closestProfile.extraAttrs;
@@ -261,6 +286,10 @@
iconTextSize = displayOption.iconTextSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
+ minCellHeight = displayOption.minCellHeight;
+ minCellWidth = displayOption.minCellWidth;
+ borderSpacing = displayOption.borderSpacing;
+
if (Utilities.isGridOptionsEnabled(context)) {
allAppsIconSize = displayOption.allAppsIconSize;
allAppsIconTextSize = displayOption.allAppsIconTextSize;
@@ -313,11 +342,6 @@
mChangeListeners.remove(listener);
}
- private void killProcess(Context context) {
- Log.e("ConfigMonitor", "restarting launcher");
- android.os.Process.killProcess(android.os.Process.myPid());
- }
-
public void verifyConfigChangedInBackground(final Context context) {
String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
// Good place to check if grid size changed in themepicker when launcher was dead.
@@ -466,7 +490,7 @@
@VisibleForTesting
static DisplayOption invDistWeightedInterpolate(
- DefaultDisplay.Info displayInfo, ArrayList<DisplayOption> points) {
+ Info displayInfo, ArrayList<DisplayOption> points) {
Point smallestSize = new Point(displayInfo.smallestSize);
Point largestSize = new Point(displayInfo.largestSize);
@@ -586,6 +610,8 @@
private final int defaultLayoutId;
private final int demoModeLayoutId;
+ private final boolean isScalable;
+
private final SparseArray<TypedValue> extraAttrs;
public GridOption(Context context, AttributeSet attrs) {
@@ -609,6 +635,9 @@
numAllAppsColumns = a.getInt(
R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
+ isScalable = a.getBoolean(
+ R.styleable.GridDisplayOption_isScalable, false);
+
a.recycle();
extraAttrs = Themes.createValueMap(context, attrs,
@@ -623,6 +652,10 @@
private final float minHeightDps;
private final boolean canBeDefault;
+ private float minCellHeight;
+ private float minCellWidth;
+ private float borderSpacing;
+
private float iconSize;
private float iconTextSize;
private float landscapeIconSize;
@@ -640,6 +673,10 @@
canBeDefault = a.getBoolean(
R.styleable.ProfileDisplayOption_canBeDefault, false);
+ minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+ minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
+ borderSpacing = a.getFloat(R.styleable.ProfileDisplayOption_borderSpacingDps, 0);
+
iconSize = a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
landscapeIconSize = a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
iconSize);
@@ -661,6 +698,9 @@
minWidthDps = 0;
minHeightDps = 0;
canBeDefault = false;
+ minCellHeight = 0;
+ minCellWidth = 0;
+ borderSpacing = 0;
}
private DisplayOption multiply(float w) {
@@ -669,6 +709,9 @@
allAppsIconSize *= w;
iconTextSize *= w;
allAppsIconTextSize *= w;
+ minCellHeight *= w;
+ minCellWidth *= w;
+ borderSpacing *= w;
return this;
}
@@ -678,6 +721,9 @@
allAppsIconSize += p.allAppsIconSize;
iconTextSize += p.iconTextSize;
allAppsIconTextSize += p.allAppsIconTextSize;
+ minCellHeight += p.minCellHeight;
+ minCellWidth += p.minCellWidth;
+ borderSpacing += p.borderSpacing;
return this;
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ae89ded..78e6f68 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -19,13 +19,13 @@
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;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
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.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
@@ -35,15 +35,20 @@
import static com.android.launcher3.LauncherState.NO_OFFSET;
import static com.android.launcher3.LauncherState.NO_SCALE;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
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.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
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_ALLAPPS_ENTRY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH;
+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.containerTypeToAtomState;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP;
+import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
@@ -70,12 +75,15 @@
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Parcelable;
import android.os.Process;
import android.os.StrictMode;
+import android.os.SystemClock;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
@@ -88,8 +96,10 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
+import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.CallSuper;
@@ -99,35 +109,38 @@
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.LauncherAction;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.allapps.search.LiveSearchManager;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.model.ModelUtils;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.PinRequestHelper;
@@ -146,9 +159,6 @@
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.ComponentKey;
@@ -161,7 +171,6 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
@@ -173,16 +182,18 @@
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.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
-import com.android.launcher3.widget.WidgetListRowEntry;
import com.android.launcher3.widget.WidgetManagerHelper;
-import com.android.launcher3.widget.WidgetsFullSheet;
import com.android.launcher3.widget.custom.CustomWidgetManager;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.shared.LauncherExterns;
@@ -263,9 +274,13 @@
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 1;
private static final int SCRIM_VIEW_ALPHA_CHANNEL_INDEX = 0;
+ private static final int THEME_CROSS_FADE_ANIMATION_DURATION = 375;
+
private LauncherAppTransitionManager mAppTransitionManager;
private Configuration mOldConfig;
+ private LiveSearchManager mLiveSearchManager;
+
@Thunk
Workspace mWorkspace;
@Thunk
@@ -341,11 +356,18 @@
private boolean mDeferOverlayCallbacks;
private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
- private long mLastTouchUpTime = -1;
+ protected long mLastTouchUpTime = -1;
private boolean mTouchInProgress;
private SafeCloseable mUserChangedCallbackCloseable;
+ // New InstanceId is assigned to mAllAppsSessionLogId for each AllApps sessions.
+ // When Launcher is not in AllApps state mAllAppsSessionLogId will be null.
+ // User actions within AllApps state are logged with this InstanceId, to recreate AllApps
+ // session on the server side.
+ protected InstanceId mAllAppsSessionLogId;
+ private LauncherState mPrevLauncherState;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
Object traceToken = TraceHelper.INSTANCE.beginSection(ON_CREATE_EVT,
@@ -377,12 +399,14 @@
idp.addOnChangeListener(this);
mSharedPrefs = Utilities.getPrefs(this);
mIconCache = app.getIconCache();
- mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
+ mAccessibilityDelegate = createAccessibilityDelegate();
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new StateManager<>(this, NORMAL);
+ mLiveSearchManager = new LiveSearchManager(this);
+
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
mAppWidgetManager = new WidgetManagerHelper(this);
@@ -392,6 +416,7 @@
inflateRootView(R.layout.launcher);
setupViews();
+ crossFadeWithPreviousAppearance();
mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
@@ -452,12 +477,12 @@
float alpha = 1f - mCurrentAssistantVisibility;
if (finalState == NORMAL) {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
- } else if (finalState == OVERVIEW || finalState == OVERVIEW_PEEK) {
+ } else if (finalState == OVERVIEW) {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
- mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ mScrimView.setAlpha(alpha);
} else {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
- mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(1f);
+ mScrimView.setAlpha(1f);
}
}
});
@@ -466,6 +491,14 @@
mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
() -> getStateManager().goToState(NORMAL));
+
+ if (Utilities.ATLEAST_R) {
+ getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
+ }
+ }
+
+ public LiveSearchManager getLiveSearchManager() {
+ return mLiveSearchManager;
}
protected LauncherOverlayManager getDefaultOverlay() {
@@ -533,7 +566,6 @@
}
private void onIdpChanged(InvariantDeviceProfile idp) {
- mUserEventDispatcher = null;
initDeviceProfile(idp);
dispatchDeviceProfileChanged();
@@ -552,9 +584,9 @@
LauncherState state = mStateManager.getState();
if (state == NORMAL) {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
- } else if (state == OVERVIEW || state == OVERVIEW_PEEK) {
+ } else if (state == OVERVIEW) {
mAppsView.getAlphaProperty(APPS_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
- mScrimView.getAlphaProperty(SCRIM_VIEW_ALPHA_CHANNEL_INDEX).setValue(alpha);
+ mScrimView.setAlpha(alpha);
}
}
@@ -818,7 +850,7 @@
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- startActivitySafely(v, intent, null, null);
+ startActivitySafely(v, intent, null);
} else {
// TODO: Show a snack bar with link to settings
Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@@ -888,8 +920,8 @@
mOverlayManager.onActivityStopped(this);
}
- logStopAndResume(Action.Command.STOP);
- mAppWidgetHost.setListenIfResumed(false);
+ logStopAndResume(false /* isResume */);
+ mAppWidgetHost.setActivityStarted(false);
NotificationListener.removeNotificationsChangedListener();
}
@@ -902,59 +934,51 @@
mOverlayManager.onActivityStarted(this);
}
- mAppWidgetHost.setListenIfResumed(true);
+ mAppWidgetHost.setActivityStarted(true);
TraceHelper.INSTANCE.endSection(traceToken);
}
@Override
@CallSuper
protected void onDeferredResumed() {
- logStopAndResume(Action.Command.RESUME);
- getUserEventDispatcher().startSession();
-
- AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
+ logStopAndResume(true /* isResume */);
// Process any items that were added while Launcher was away.
- InstallShortcutReceiver.disableAndFlushInstallQueue(
- InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
+ ItemInstallQueue.INSTANCE.get(this)
+ .resumeModelPush(FLAG_ACTIVITY_PAUSED);
// Refresh shortcuts if the permission changed.
- mModel.refreshShortcutsIfRequired();
+ mModel.validateModelDataOnResume();
// Set the notification listener and fetch updated notifications when we resume
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
DiscoveryBounce.showForHomeIfNeeded(this);
+ mAppWidgetHost.setActivityResumed(true);
}
- protected void handlePendingActivityRequest() { }
-
- private void logStopAndResume(int command) {
+ private void logStopAndResume(boolean isResume) {
if (mPendingExecutor != null) return;
int pageIndex = mWorkspace.isOverlayShown() ? -1 : mWorkspace.getCurrentPage();
- int containerType = mStateManager.getState().containerType;
+ int statsLogOrdinal = mStateManager.getState().statsLogOrdinal;
StatsLogManager.EventEnum event;
StatsLogManager.StatsLogger logger = getStatsLogManager().logger();
- if (command == Action.Command.RESUME) {
+ if (isResume) {
logger.withSrcState(LAUNCHER_STATE_BACKGROUND)
- .withDstState(containerTypeToAtomState(mStateManager.getState().containerType));
+ .withDstState(mStateManager.getState().statsLogOrdinal);
event = LAUNCHER_ONRESUME;
} else { /* command == Action.Command.STOP */
- logger.withSrcState(containerTypeToAtomState(mStateManager.getState().containerType))
+ logger.withSrcState(mStateManager.getState().statsLogOrdinal)
.withDstState(LAUNCHER_STATE_BACKGROUND);
event = LAUNCHER_ONSTOP;
}
- if (containerType == ContainerType.WORKSPACE && mWorkspace != null) {
- getUserEventDispatcher().logActionCommand(command,
- containerType, -1, pageIndex);
+ if (statsLogOrdinal == LAUNCHER_STATE_HOME && mWorkspace != null) {
logger.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder()
.setPageIndex(pageIndex)).build());
- } else {
- getUserEventDispatcher().logActionCommand(command, containerType, -1);
}
logger.log(event);
}
@@ -1011,7 +1035,7 @@
if (state == SPRING_LOADED) {
// Prevent any Un/InstallShortcutReceivers from updating the db while we are
// not on homescreen
- InstallShortcutReceiver.enableInstallQueue(FLAG_DRAG_AND_DROP);
+ ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_DRAG_AND_DROP);
getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
mWorkspace.showPageIndicatorAtCurrentScroll();
@@ -1019,12 +1043,13 @@
}
// When multiple pages are visible, show persistent page indicator
mWorkspace.getPageIndicator().setShouldAutoHide(!state.hasFlag(FLAG_MULTI_PAGE));
+ mPrevLauncherState = mStateManager.getCurrentStableState();
}
@Override
public void onStateSetEnd(LauncherState state) {
- super.onStateSetStart(state);
- getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+ super.onStateSetEnd(state);
+ getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
finishAutoCancelActionMode();
@@ -1036,11 +1061,27 @@
if (state == NORMAL) {
// Re-enable any Un/InstallShortcutReceiver and now process any queued items
- InstallShortcutReceiver.disableAndFlushInstallQueue(FLAG_DRAG_AND_DROP, this);
+ ItemInstallQueue.INSTANCE.get(this)
+ .resumeModelPush(FLAG_DRAG_AND_DROP);
// Clear any rotation locks when going to normal state
getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
}
+
+ if (ALL_APPS.equals(state)) {
+ // creates new instance ID since new all apps session is started.
+ mAllAppsSessionLogId = new InstanceIdSequence().newInstanceId();
+ getStatsLogManager()
+ .logger()
+ .log(FeatureFlags.ENABLE_DEVICE_SEARCH.get()
+ ? LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH
+ : LAUNCHER_ALLAPPS_ENTRY);
+ } else if (ALL_APPS.equals(mPrevLauncherState)
+ // Check if mLogInstanceId is not null to make sure exit event is logged only once.
+ && mAllAppsSessionLogId != null) {
+ getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_EXIT);
+ mAllAppsSessionLogId = null;
+ }
}
@Override
@@ -1070,7 +1111,7 @@
@Override
protected void onPause() {
// Ensure that items added to Launcher are queued until Launcher returns
- InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
+ ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
super.onPause();
mDragController.cancelDrag();
@@ -1080,6 +1121,7 @@
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityPaused(this);
}
+ mAppWidgetHost.setActivityResumed(false);
}
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1205,16 +1247,13 @@
int[] cellXY = mTmpAddItemCellCoordinates;
CellLayout layout = getCellLayout(container, screenId);
- WorkspaceItemInfo info = null;
- if (Utilities.ATLEAST_OREO) {
- info = PinRequestHelper.createWorkspaceItemFromPinItemRequest(
+ WorkspaceItemInfo info = PinRequestHelper.createWorkspaceItemFromPinItemRequest(
this, PinRequestHelper.getPinItemRequest(data), 0);
- }
if (info == null) {
// Legacy shortcuts are only supported for primary profile.
info = Process.myUserHandle().equals(args.user)
- ? InstallShortcutReceiver.fromShortcutIntent(this, data) : null;
+ ? ModelUtils.fromLegacyShortcutIntent(this, data) : null;
if (info == null) {
Log.e(TAG, "Unable to parse a valid custom shortcut result");
@@ -1352,6 +1391,16 @@
public Object onRetainNonConfigurationInstance() {
NonConfigInstance instance = new NonConfigInstance();
instance.config = new Configuration(mOldConfig);
+
+ int width = mDragLayer.getWidth();
+ int height = mDragLayer.getHeight();
+
+ if (FeatureFlags.ENABLE_LAUNCHER_ACTIVITY_THEME_CROSSFADE.get()
+ && width > 0
+ && height > 0) {
+ instance.snapshot =
+ BitmapRenderer.createHardwareBitmap(width, height, mDragLayer::draw);
+ }
return instance;
}
@@ -1440,8 +1489,7 @@
if (!isInState(NORMAL)) {
// Only change state, if not already the same. This prevents cancelling any
// animations running as part of resume
- mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange(),
- this::handlePendingActivityRequest);
+ mStateManager.goToState(NORMAL, mStateManager.shouldAnimateStateChange());
}
// Reset the apps view
@@ -1455,11 +1503,6 @@
}
// Handle HOME_INTENT
- UserEventDispatcher ued = getUserEventDispatcher();
- Target target = newContainerTarget(mStateManager.getState().containerType);
- target.pageIndex = mWorkspace.getCurrentPage();
- ued.logActionCommand(Action.Command.HOME_INTENT, target,
- newContainerTarget(ContainerType.WORKSPACE));
hideKeyboard();
if (mLauncherCallbacks != null) {
@@ -1468,12 +1511,17 @@
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
handleGestureContract(intent);
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
- getStateManager().goToState(ALL_APPS, alreadyOnHome);
+ showAllAppsFromIntent(alreadyOnHome);
}
TraceHelper.INSTANCE.endSection(traceToken);
}
+ protected void showAllAppsFromIntent(boolean alreadyOnHome) {
+ AbstractFloatingView.closeAllOpenViews(this);
+ getStateManager().goToState(ALL_APPS, alreadyOnHome);
+ }
+
/**
* Handles gesture nav contract
*/
@@ -1561,9 +1609,9 @@
LauncherAppState.getIDP(this).removeOnChangeListener(this);
mOverlayManager.onActivityDestroyed(this);
- mAppTransitionManager.unregisterRemoteAnimations();
+ mAppTransitionManager.onActivityDestroyed();
mUserChangedCallbackCloseable.close();
- mAllAppsController.onActivityDestroyed();
+ mLiveSearchManager.stop();
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1757,15 +1805,42 @@
return newFolder;
}
- /**
- * Called when a workspace item is converted into a folder
- */
- public void folderCreatedFromItem(Folder folder, WorkspaceItemInfo itemInfo){}
+ @Override
+ public Rect getFolderBoundingBox() {
+ // We need to bound the folder to the currently visible workspace area
+ Rect folderBoundingBox = new Rect();
+ getWorkspace().getPageAreaRelativeToDragLayer(folderBoundingBox);
+ return folderBoundingBox;
+ }
- /**
- * Called when a folder is converted into a workspace item
- */
- public void folderConvertedToItem(Folder folder, WorkspaceItemInfo itemInfo) {}
+ @Override
+ public void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) {
+ int left = inOutPosition[0];
+ int top = inOutPosition[1];
+ DeviceProfile grid = getDeviceProfile();
+ int distFromEdgeOfScreen = getWorkspace().getPaddingLeft();
+ if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) {
+ // Center the folder if it is very close to being centered anyway, by virtue of
+ // filling the majority of the viewport. ie. remove it from the uncanny valley
+ // of centeredness.
+ left = (grid.availableWidthPx - width) / 2;
+ } else if (width >= bounds.width()) {
+ // If the folder doesn't fit within the bounds, center it about the desired bounds
+ left = bounds.left + (bounds.width() - width) / 2;
+ }
+ if (height >= bounds.height()) {
+ // Folder height is greater than page height, center on page
+ top = bounds.top + (bounds.height() - height) / 2;
+ } else {
+ // Folder height is less than page height, so bound it to the absolute open folder
+ // bounds if necessary
+ Rect folderBounds = grid.getAbsoluteOpenFolderBounds();
+ left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width));
+ top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height));
+ }
+ inOutPosition[0] = left;
+ inOutPosition[1] = top;
+ }
/**
* Unbinds the view for the specified item, and removes the item and all its children.
@@ -1820,7 +1895,7 @@
mTouchInProgress = true;
break;
case MotionEvent.ACTION_UP:
- mLastTouchUpTime = System.currentTimeMillis();
+ mLastTouchUpTime = SystemClock.uptimeMillis();
// Follow through
case MotionEvent.ACTION_CANCEL:
mTouchInProgress = false;
@@ -1890,13 +1965,19 @@
}
@Override
- public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
- @Nullable String sourceContainer) {
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ if (isViewInTaskbar(v)) {
+ // Start the activity without the hacky workarounds below, which assume the View was
+ // clicked when Launcher was resumed and will be hidden until Launcher is re-resumed
+ // (this isn't the case for Taskbar).
+ return super.startActivitySafely(v, intent, item);
+ }
+
if (!hasBeenResumed()) {
// Workaround an issue where the WM launch animation is clobbered when finishing the
// recents animation into launcher. Defer launching the activity until Launcher is
// next resumed.
- addOnResumeCallback(() -> startActivitySafely(v, intent, item, sourceContainer));
+ addOnResumeCallback(() -> startActivitySafely(v, intent, item));
if (mOnDeferredActivityLaunchCallback != null) {
mOnDeferredActivityLaunchCallback.run();
mOnDeferredActivityLaunchCallback = null;
@@ -1904,7 +1985,7 @@
return true;
}
- boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
+ boolean success = super.startActivitySafely(v, intent, item);
if (success && v instanceof BubbleTextView) {
// This is set to the view that launched the activity that navigated the user away
// from launcher. Since there is no callback for when the activity has finished
@@ -1951,7 +2032,7 @@
// Populate event with a fake title based on the current state.
// TODO: When can workspace be null?
text.add(mWorkspace == null
- ? getString(R.string.all_apps_home_button_label)
+ ? getString(R.string.home_screen)
: mStateManager.getState().getDescription(this));
return result;
}
@@ -2204,9 +2285,6 @@
workspace.requestLayout();
}
- @Override
- public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
-
/**
* Add the views for a widget to the workspace.
*/
@@ -2435,8 +2513,8 @@
mPendingActivityResult = null;
}
- InstallShortcutReceiver.disableAndFlushInstallQueue(
- InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
+ ItemInstallQueue.INSTANCE.get(this)
+ .resumeModelPush(FLAG_LOADER_RUNNING);
// When undoing the removal of the last item on a page, return to that page.
// Since we are just resetting the current page without user interaction,
@@ -2456,15 +2534,15 @@
if (mDragController.isDragging()) {
return false;
} else {
- return (System.currentTimeMillis() - mLastTouchUpTime)
+ return (SystemClock.uptimeMillis() - mLastTouchUpTime)
> (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
}
}
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v)
- .setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
- bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
+ .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION);
+ bounceAnim.setStartDelay(i * ItemInstallQueue.NEW_SHORTCUT_STAGGER_DELAY);
bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
return bounceAnim;
}
@@ -2481,6 +2559,7 @@
@Override
public void bindAllApplications(AppInfo[] apps, int flags) {
mAppsView.getAppsStore().setApps(apps, flags);
+ PopupContainerWithArrow.dismissInvalidPopup(this);
}
/**
@@ -2493,8 +2572,8 @@
}
@Override
- public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
- mAppsView.getAppsStore().updatePromiseAppProgress(app);
+ public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+ mAppsView.getAppsStore().updateProgressBar(app);
}
@Override
@@ -2509,9 +2588,10 @@
* @param updated list of shortcuts which have changed.
*/
@Override
- public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) {
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
if (!updated.isEmpty()) {
mWorkspace.updateShortcuts(updated);
+ PopupContainerWithArrow.dismissInvalidPopup(this);
}
}
@@ -2536,10 +2616,11 @@
public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
mWorkspace.removeItemsByMatcher(matcher);
mDragController.onAppsRemoved(matcher);
+ PopupContainerWithArrow.dismissInvalidPopup(this);
}
@Override
- public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
+ public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) {
mPopupDataProvider.setAllWidgets(allWidgets);
}
@@ -2620,19 +2701,9 @@
shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.widget_button_text),
KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON));
}
- final View currentFocus = getCurrentFocus();
- if (currentFocus != null) {
- if (new CustomActionsPopup(this, currentFocus).canShow()) {
- shortcutInfos.add(new KeyboardShortcutInfo(getString(R.string.custom_actions),
- KeyEvent.KEYCODE_O, KeyEvent.META_CTRL_ON));
- }
- if (currentFocus.getTag() instanceof ItemInfo
- && ShortcutUtil.supportsShortcuts((ItemInfo) currentFocus.getTag())) {
+ getSupportedActions(this, getCurrentFocus()).forEach(la ->
shortcutInfos.add(new KeyboardShortcutInfo(
- getString(R.string.shortcuts_menu_with_notifications_description),
- KeyEvent.KEYCODE_S, KeyEvent.META_CTRL_ON));
- }
- }
+ la.accessibilityAction.getLabel(), la.keyCode, KeyEvent.META_CTRL_ON)));
if (!shortcutInfos.isEmpty()) {
data.add(new KeyboardShortcutGroup(getString(R.string.home_screen), shortcutInfos));
}
@@ -2650,29 +2721,18 @@
return true;
}
break;
- case KeyEvent.KEYCODE_S: {
- View focusedView = getCurrentFocus();
- if (focusedView instanceof BubbleTextView
- && focusedView.getTag() instanceof ItemInfo
- && mAccessibilityDelegate.performAction(focusedView,
- (ItemInfo) focusedView.getTag(),
- LauncherAccessibilityDelegate.DEEP_SHORTCUTS)) {
- PopupContainerWithArrow.getOpen(this).requestFocus();
- return true;
- }
- break;
- }
- case KeyEvent.KEYCODE_O:
- if (new CustomActionsPopup(this, getCurrentFocus()).show()) {
- return true;
- }
- break;
case KeyEvent.KEYCODE_W:
if (isInState(NORMAL)) {
OptionsPopupView.openWidgets(this);
return true;
}
break;
+ default:
+ for (LauncherAction la : getSupportedActions(this, getCurrentFocus())) {
+ if (la.keyCode == keyCode) {
+ return la.invokeFromKeyboard(getCurrentFocus());
+ }
+ }
}
}
return super.onKeyShortcut(keyCode, event);
@@ -2700,8 +2760,10 @@
return super.onKeyUp(keyCode, event);
}
- protected StateHandler<LauncherState>[] createStateHandlers() {
- return new StateHandler[] { getAllAppsController(), getWorkspace() };
+ @Override
+ protected void collectStateHandlers(List<StateHandler> out) {
+ out.add(getAllAppsController());
+ out.add(getWorkspace());
}
public TouchController[] createTouchControllers() {
@@ -2730,6 +2792,9 @@
return Stream.of(APP_INFO, WIDGETS, INSTALL);
}
+ protected LauncherAccessibilityDelegate createAccessibilityDelegate() {
+ return new LauncherAccessibilityDelegate(this);
+ }
/**
* @see LauncherState#getOverviewScaleAndOffset(Launcher)
@@ -2758,7 +2823,59 @@
void onLauncherResume();
}
+ /**
+ * Cross-fades the launcher's updated appearance with its previous appearance.
+ *
+ * This method is used to cross-fade UI updates on activity creation, specifically dark mode
+ * updates.
+ */
+ private void crossFadeWithPreviousAppearance() {
+ NonConfigInstance lastInstance = (NonConfigInstance) getLastNonConfigurationInstance();
+
+ if (lastInstance == null || lastInstance.snapshot == null) {
+ return;
+ }
+
+ ImageView crossFadeHelper = new ImageView(this);
+ crossFadeHelper.setImageBitmap(lastInstance.snapshot);
+ crossFadeHelper.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+
+ InsettableFrameLayout.LayoutParams layoutParams = new InsettableFrameLayout.LayoutParams(
+ InsettableFrameLayout.LayoutParams.MATCH_PARENT,
+ InsettableFrameLayout.LayoutParams.MATCH_PARENT);
+
+ layoutParams.ignoreInsets = true;
+
+ crossFadeHelper.setLayoutParams(layoutParams);
+
+ getRootView().addView(crossFadeHelper);
+
+ crossFadeHelper
+ .animate()
+ .setDuration(THEME_CROSS_FADE_ANIMATION_DURATION)
+ .alpha(0f)
+ .withEndAction(() -> getRootView().removeView(crossFadeHelper))
+ .start();
+ }
+
+ /**
+ * @return Whether the View is in the same window as the Taskbar window.
+ */
+ public boolean isViewInTaskbar(View v) {
+ return false;
+ }
+
+ public DragOptions getDefaultWorkspaceDragOptions() {
+ return new DragOptions();
+ }
+
private static class NonConfigInstance {
public Configuration config;
+ public Bitmap snapshot;
+ }
+
+ @Override
+ public StatsLogManager getStatsLogManager() {
+ return super.getStatsLogManager().withDefaultInstanceId(mAllAppsSessionLogId);
}
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index d9cf7f1..803f8d2 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
import android.util.IntProperty;
@@ -29,8 +32,8 @@
*/
public static final int SPRING_LOADED_EXIT_DELAY = 500;
- // The progress of an animation to all apps must be at least this far along to snap to all apps.
- public static final float MIN_PROGRESS_TO_ALL_APPS = 0.5f;
+ // Progress after which the transition is assumed to be a success
+ public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
public static final IntProperty<Drawable> DRAWABLE_ALPHA =
new IntProperty<Drawable>("drawableAlpha") {
@@ -131,4 +134,23 @@
return view.getAlpha();
}
};
+
+ /**
+ * Utility method to create an {@link AnimatorListener} which executes a callback on animation
+ * cancel.
+ */
+ public static AnimatorListener newCancelListener(Runnable callback) {
+ return new AnimatorListenerAdapter() {
+
+ boolean mDispatched = false;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!mDispatched) {
+ mDispatched = true;
+ callback.run();
+ }
+ }
+ };
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 53e5274..57d7600 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,8 +17,8 @@
package com.android.launcher3;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.content.ComponentName;
import android.content.Context;
@@ -33,15 +33,14 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.PredictionModel;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.InstallSessionTracker;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SafeCloseable;
-import com.android.launcher3.util.SecureSettingsObserver;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.launcher3.widget.custom.CustomWidgetManager;
@@ -58,9 +57,9 @@
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
- private final PredictionModel mPredictionModel;
+ private SettingsCache.OnChangeListener mNotificationSettingsChangedListener;
- private SecureSettingsObserver mNotificationDotsObserver;
+ private SettingsCache mSettingsCache;
private InstallSessionTracker mInstallSessionTracker;
private SimpleBroadcastReceiver mModelChangeReceiver;
private SafeCloseable mCalendarChangeTracker;
@@ -107,17 +106,14 @@
new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
- .registerInstallTracker(mModel, MODEL_EXECUTOR);
+ .registerInstallTracker(mModel);
- if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) {
- mNotificationDotsObserver = null;
- } else {
- // Register an observer to rebind the notification listener when dots are re-enabled.
- mNotificationDotsObserver =
- newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged);
- mNotificationDotsObserver.register();
- mNotificationDotsObserver.dispatchOnChange();
- }
+ // Register an observer to rebind the notification listener when dots are re-enabled.
+ mSettingsCache = SettingsCache.INSTANCE.get(mContext);
+ mNotificationSettingsChangedListener = this::onNotificationSettingsChanged;
+ mSettingsCache.register(NOTIFICATION_BADGING_URI,
+ mNotificationSettingsChangedListener);
+ mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI);
}
public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
@@ -128,8 +124,7 @@
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
- mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
- mPredictionModel = PredictionModel.newInstance(mContext);
+ mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
}
protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -157,6 +152,7 @@
* Call from Application.onTerminate(), which is not guaranteed to ever be called.
*/
public void onTerminate() {
+ mModel.destroy();
if (mModelChangeReceiver != null) {
mContext.unregisterReceiver(mModelChangeReceiver);
}
@@ -172,8 +168,9 @@
}
CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
- if (mNotificationDotsObserver != null) {
- mNotificationDotsObserver.unregister();
+ if (mSettingsCache != null) {
+ mSettingsCache.unregister(NOTIFICATION_BADGING_URI,
+ mNotificationSettingsChangedListener);
}
}
@@ -185,10 +182,6 @@
return mModel;
}
- public PredictionModel getPredictionModel() {
- return mPredictionModel;
- }
-
public WidgetPreviewLoader getWidgetCache() {
return mWidgetCache;
}
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 24e0d14..d0bf577 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -50,7 +50,7 @@
return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
}
- public boolean supportsAdaptiveIconAnimation() {
+ public boolean supportsAdaptiveIconAnimation(View clickedView) {
return false;
}
@@ -62,9 +62,30 @@
}
/**
+ * Handles clean up when activity is destroyed.
+ */
+ public void onActivityDestroyed() {
+ // Do nothing
+ }
+
+ /**
* Unregisters all remote animations.
*/
public void unregisterRemoteAnimations() {
// Do nothing
}
+
+ /**
+ * Registers remote transitions for certain system transitions.
+ */
+ public void registerRemoteTransitions() {
+ // Do nothing
+ }
+
+ /**
+ * Unregisters all remote transitions.
+ */
+ public void unregisterRemoteTransitions() {
+ // Do nothing
+ }
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f434c91..699495c 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -20,7 +20,6 @@
import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
import android.content.Context;
import android.content.Intent;
@@ -44,9 +43,12 @@
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.CacheDataUpdatedTask;
+import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDelegate;
import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.model.PackageIncrementalDownloadUpdatedTask;
import com.android.launcher3.model.PackageInstallStateChangedTask;
import com.android.launcher3.model.PackageUpdatedTask;
import com.android.launcher3.model.ShortcutsChangedTask;
@@ -58,9 +60,8 @@
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -85,7 +86,6 @@
private final LauncherAppState mApp;
private final Object mLock = new Object();
- private final LooperExecutor mMainExecutor = MAIN_EXECUTOR;
private LoaderTask mLoaderTask;
private boolean mIsLoaderTaskRunning;
@@ -112,20 +112,22 @@
*/
private final BgDataModel mBgDataModel = new BgDataModel();
+ private final ModelDelegate mModelDelegate;
+
// Runnable to check if the shortcuts permission has changed.
- private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+ private final Runnable mDataValidationCheck = new Runnable() {
@Override
public void run() {
- if (mModelLoaded && hasShortcutsPermission(mApp.getContext())
- != mBgAllAppsList.hasShortcutHostPermission()) {
- forceReload();
+ if (mModelLoaded) {
+ mModelDelegate.validateData();
}
}
};
- LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
+ LauncherModel(Context context, LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
+ mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel);
}
/**
@@ -195,6 +197,15 @@
}
@Override
+ public void onPackageLoadingProgressChanged(
+ String packageName, UserHandle user, float progress) {
+ if (Utilities.ATLEAST_S) {
+ enqueueModelUpdateTask(new PackageIncrementalDownloadUpdatedTask(
+ packageName, user, progress));
+ }
+ }
+
+ @Override
public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
UserHandle user) {
enqueueModelUpdateTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
@@ -217,6 +228,20 @@
}
}
+ /**
+ * Called when the workspace items have drastically changed
+ */
+ public void onWorkspaceUiChanged() {
+ MODEL_EXECUTOR.execute(mModelDelegate::workspaceLoadComplete);
+ }
+
+ /**
+ * Called when the model is destroyed
+ */
+ public void destroy() {
+ MODEL_EXECUTOR.execute(mModelDelegate::destroy);
+ }
+
public void onBroadcastIntent(Intent intent) {
if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
final String action = intent.getAction();
@@ -321,20 +346,21 @@
*/
public boolean startLoader() {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
- InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
+ ItemInstallQueue.INSTANCE.get(mApp.getContext())
+ .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
synchronized (mLock) {
// Don't bother to start the thread if we know it's not going to do anything
final Callbacks[] callbacksList = getCallbacks();
if (callbacksList.length > 0) {
// Clear any pending bind-runnables from the synchronized load process.
for (Callbacks cb : callbacksList) {
- mMainExecutor.execute(cb::clearPendingBinds);
+ MAIN_EXECUTOR.execute(cb::clearPendingBinds);
}
// If there is already one running, tell it to stop.
stopLoader();
LoaderResults loaderResults = new LoaderResults(
- mApp, mBgDataModel, mBgAllAppsList, callbacksList, mMainExecutor);
+ mApp, mBgDataModel, mBgAllAppsList, callbacksList);
if (mModelLoaded && !mIsLoaderTaskRunning) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
@@ -372,7 +398,8 @@
public void startLoaderForResults(LoaderResults results) {
synchronized (mLock) {
stopLoader();
- mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, mBgDataModel, results);
+ mLoaderTask = new LoaderTask(
+ mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, results);
// Always post the loader task, instead of running directly (even on same thread) so
// that we exit any nested synchronized blocks
@@ -410,7 +437,7 @@
enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- final IntSparseArrayMap<Boolean> removedIds = new IntSparseArrayMap<>();
+ final IntSet removedIds = new IntSet();
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo
@@ -418,13 +445,13 @@
&& user.equals(info.user)
&& info.getIntent() != null
&& TextUtils.equals(packageName, info.getIntent().getPackage())) {
- removedIds.put(info.id, true /* remove */);
+ removedIds.add(info.id);
}
}
}
if (!removedIds.isEmpty()) {
- deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds, false));
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedIds));
}
}
});
@@ -491,9 +518,9 @@
* Current implementation simply reloads the workspace, but it can be optimized to
* use partial updates similar to {@link UserCache}
*/
- public void refreshShortcutsIfRequired() {
- MODEL_EXECUTOR.getHandler().removeCallbacks(mShortcutPermissionCheckRunnable);
- MODEL_EXECUTOR.post(mShortcutPermissionCheckRunnable);
+ public void validateModelDataOnResume() {
+ MODEL_EXECUTOR.getHandler().removeCallbacks(mDataValidationCheck);
+ MODEL_EXECUTOR.post(mDataValidationCheck);
}
/**
@@ -520,7 +547,7 @@
}
public void enqueueModelUpdateTask(ModelUpdateTask task) {
- task.init(mApp, this, mBgDataModel, mBgAllAppsList, mMainExecutor);
+ task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
MODEL_EXECUTOR.execute(task);
}
@@ -588,7 +615,9 @@
+ "\" bitmapIcon=" + info.bitmap.icon
+ " componentName=" + info.componentName.getPackageName());
}
+ writer.println();
}
+ mModelDelegate.dump(prefix, fd, writer, args);
mBgDataModel.dump(prefix, fd, writer, args);
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 19a314f..2ec5e90 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -71,6 +71,7 @@
import com.android.launcher3.util.NoLocaleSQLiteHelper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
import org.xmlpull.v1.XmlPullParser;
@@ -188,6 +189,9 @@
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
+ final Bundle extra = new Bundle();
+ extra.putString(LauncherSettings.Settings.EXTRA_DB_NAME, mOpenHelper.getDatabaseName());
+ result.setExtras(extra);
result.setNotificationUri(getContext().getContentResolver(), uri);
return result;
@@ -913,7 +917,6 @@
* Removes widgets which are registered to the Launcher's host, but are not present
* in our model.
*/
- @TargetApi(Build.VERSION_CODES.O)
public void removeGhostWidgets(SQLiteDatabase db) {
// Get all existing widget ids.
final AppWidgetHost host = newLauncherWidgetHost();
@@ -1097,11 +1100,20 @@
* @return the max _id in the provided table.
*/
@Thunk static int getMaxId(SQLiteDatabase db, String query, Object... args) {
- int max = (int) DatabaseUtils.longForQuery(db,
- String.format(Locale.ENGLISH, query, args),
- null);
- if (max < 0) {
- throw new RuntimeException("Error: could not query max id");
+ int max = 0;
+ try (SQLiteStatement prog = db.compileStatement(
+ String.format(Locale.ENGLISH, query, args))) {
+ max = (int) DatabaseUtils.longForQuery(prog, null);
+ if (max < 0) {
+ throw new RuntimeException("Error: could not query max id");
+ }
+ } catch (IllegalArgumentException exception) {
+ String message = exception.getMessage();
+ if (message.contains("re-open") && message.contains("already-closed")) {
+ // Don't crash trying to end a transaction an an already closed DB. See b/173162852.
+ } else {
+ throw exception;
+ }
}
return max;
}
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 51504ce..76c4518 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -4,12 +4,14 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewDebug;
import android.view.WindowInsets;
+import com.android.launcher3.graphics.SysUiScrim;
import com.android.launcher3.statemanager.StatefulActivity;
import java.util.Collections;
@@ -31,6 +33,8 @@
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mForceHideBackArrow;
+ private SysUiScrim mSysUiScrim;
+
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
mActivity = StatefulActivity.fromContext(context);
@@ -89,6 +93,18 @@
}
}
+ public void setSysUiScrim(SysUiScrim scrim) {
+ mSysUiScrim = scrim;
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mSysUiScrim != null) {
+ mSysUiScrim.draw(canvas);
+ }
+ super.dispatchDraw(canvas);
+ }
+
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
@@ -119,4 +135,4 @@
void onWindowVisibilityChanged(int visibility);
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index a1ac229..22c257a 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -75,6 +75,37 @@
public static final int ITEM_TYPE_SHORTCUT = 1;
/**
+ * The favorite is a user created folder
+ */
+ public static final int ITEM_TYPE_FOLDER = 2;
+
+ /**
+ * The favorite is a widget
+ */
+ public static final int ITEM_TYPE_APPWIDGET = 4;
+
+ /**
+ * The favorite is a custom widget provided by the launcher
+ */
+ public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
+
+ /**
+ * The gesture is an application created deep shortcut
+ */
+ public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
+
+ /**
+ * 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;
+
+ /**
+ * The item is QSB
+ */
+ public static final int ITEM_TYPE_QSB = 8;
+
+ /**
* The icon package name in Intent.ShortcutIconResource
* <P>Type: TEXT</P>
*/
@@ -121,6 +152,12 @@
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
+ * The content:// style URL for "favorites_bakup" table
+ */
+ public static final Uri BACKUP_CONTENT_URI = Uri.parse("content://"
+ + LauncherProvider.AUTHORITY + "/" + BACKUP_TABLE_NAME);
+
+ /**
* The content:// style URL for "favorites_preview" table
*/
public static final Uri PREVIEW_CONTENT_URI = Uri.parse("content://"
@@ -156,6 +193,7 @@
public static final int CONTAINER_DESKTOP = -100;
public static final int CONTAINER_HOTSEAT = -101;
public static final int CONTAINER_PREDICTION = -102;
+ public static final int CONTAINER_WIDGETS_PREDICTION = -111;
public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
public static final int CONTAINER_ALL_APPS = -104;
public static final int CONTAINER_WIDGETS_TRAY = -105;
@@ -164,7 +202,10 @@
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_TASKFOREGROUND = -110;
+ 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;
public static final String containerToString(int container) {
switch (container) {
@@ -187,6 +228,8 @@
case ITEM_TYPE_APPWIDGET: return "WIDGET";
case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
case ITEM_TYPE_DEEP_SHORTCUT: return "DEEPSHORTCUT";
+ case ITEM_TYPE_TASK: return "TASK";
+ case ITEM_TYPE_QSB: return "QSB";
default: return String.valueOf(type);
}
}
@@ -232,32 +275,6 @@
public static final String PROFILE_ID = "profileId";
/**
- * The favorite is a user created folder
- */
- public static final int ITEM_TYPE_FOLDER = 2;
-
- /**
- * The favorite is a widget
- */
- public static final int ITEM_TYPE_APPWIDGET = 4;
-
- /**
- * The favorite is a custom widget provided by the launcher
- */
- public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
-
- /**
- * The gesture is an application created deep shortcut
- */
- public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
-
- /**
- * 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;
-
- /**
* The appWidgetId of the widget
*
* <P>Type: INTEGER</P>
@@ -359,6 +376,8 @@
public static final String EXTRA_VALUE = "value";
+ public static final String EXTRA_DB_NAME = "db_name";
+
public static Bundle call(ContentResolver cr, String method) {
return call(cr, method, null /* arg */);
}
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 39b0f2f..21c40ef 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -16,12 +16,12 @@
package com.android.launcher3;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
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.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
-import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_STATE_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;
@@ -29,6 +29,7 @@
import android.content.Context;
import android.view.animation.Interpolator;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.states.HintState;
@@ -36,7 +37,6 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.uioverrides.states.AllAppsState;
import com.android.launcher3.uioverrides.states.OverviewState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import java.util.Arrays;
@@ -56,7 +56,10 @@
public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
public static final int ALL_APPS_CONTENT = 1 << 4;
public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
- public static final int OVERVIEW_BUTTONS = 1 << 6;
+ public static final int OVERVIEW_ACTIONS = 1 << 6;
+ public static final int TASKBAR = 1 << 7;
+ public static final int CLEAR_ALL_BUTTON = 1 << 8;
+ public static final int WORKSPACE_PAGE_INDICATOR = 1 << 9;
/** Mask of all the items that are contained in the apps view. */
public static final int APPS_VIEW_ITEM_MASK =
@@ -98,7 +101,7 @@
* TODO: Create a separate class for NORMAL state.
*/
public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
- ContainerType.WORKSPACE,
+ LAUNCHER_STATE_HOME,
FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
FLAG_HAS_SYS_UI_SCRIM) {
@Override
@@ -117,8 +120,6 @@
public static final LauncherState HINT_STATE = new HintState(HINT_STATE_ORDINAL);
public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
- public static final LauncherState OVERVIEW_PEEK =
- OverviewState.newPeekState(OVERVIEW_PEEK_STATE_ORDINAL);
public static final LauncherState OVERVIEW_MODAL_TASK = OverviewState.newModalTaskState(
OVERVIEW_MODAL_TASK_STATE_ORDINAL);
public static final LauncherState QUICK_SWITCH =
@@ -129,9 +130,9 @@
public final int ordinal;
/**
- * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
+ * Used for {@link com.android.launcher3.logging.StatsLogManager}
*/
- public final int containerType;
+ public final int statsLogOrdinal;
/**
* True if the state has overview panel visible.
@@ -140,8 +141,8 @@
private final int mFlags;
- public LauncherState(int id, int containerType, int flags) {
- this.containerType = containerType;
+ public LauncherState(int id, int statsLogOrdinal, int flags) {
+ this.statsLogOrdinal = statsLogOrdinal;
this.mFlags = flags;
this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
this.ordinal = id;
@@ -171,9 +172,9 @@
/**
* Returns an array of two elements.
- * The first specifies the scale for the overview
- * The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
- * should be shifted horizontally.
+ * The first specifies the scale for the overview
+ * The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
+ * should be shifted horizontally.
*/
public float[] getOverviewScaleAndOffset(Launcher launcher) {
return launcher.getNormalOverviewScaleAndOffset();
@@ -188,10 +189,23 @@
}
public int getVisibleElements(Launcher launcher) {
- if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- return HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
+ DeviceProfile deviceProfile = launcher.getDeviceProfile();
+ int flags = WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
+ if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !deviceProfile.isVerticalBarLayout()) {
+ flags |= HOTSEAT_SEARCH_BOX;
}
- return HOTSEAT_ICONS | HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR;
+ if (!deviceProfile.isTaskbarPresent) {
+ flags |= HOTSEAT_ICONS;
+ }
+ return flags;
+ }
+
+ /**
+ * A shorthand for checking getVisibleElements() & elements == elements.
+ * @return Whether all of the given elements are visible.
+ */
+ public boolean areElementsVisible(Launcher launcher, int elements) {
+ return (getVisibleElements(launcher) & elements) == elements;
}
/**
@@ -220,6 +234,13 @@
}
/**
+ * For this state, whether tasks should layout as a grid rather than a list.
+ */
+ public boolean displayOverviewTasksAsGrid(Launcher launcher) {
+ return false;
+ }
+
+ /**
* The amount of blur and wallpaper zoom to apply to the background of either the app
* or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
*
@@ -232,6 +253,7 @@
/**
* Returns the amount of blur and wallpaper zoom for this state with {@param isMultiWindowMode}.
+ *
* @see #getDepth(Context).
*/
public final float getDepth(Context context, boolean isMultiWindowMode) {
@@ -258,7 +280,7 @@
return new PageAlphaProvider(ACCEL_2) {
@Override
public float getPageAlpha(int pageIndex) {
- return pageIndex != centerPage ? 0 : 1f;
+ return pageIndex != centerPage ? 0 : 1f;
}
};
}
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
index 5f6ecb5..f2a3de7 100644
--- a/src/com/android/launcher3/MainProcessInitializer.java
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -38,7 +38,6 @@
protected void init(Context context) {
FileLog.setDir(context.getApplicationContext().getFilesDir());
FeatureFlags.initialize(context);
- SessionCommitReceiver.applyDefaultUserPrefs(context);
IconShape.init(context);
if (BitmapCreationCheck.ENABLED) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e13d89f..50f1e44 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -195,9 +195,7 @@
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
- if (Utilities.ATLEAST_OREO) {
- setDefaultFocusHighlightEnabled(false);
- }
+ setDefaultFocusHighlightEnabled(false);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -219,7 +217,7 @@
/**
* Returns the index of the currently displayed page. When in free scroll mode, this is the page
* that the user was on before entering free scroll mode (e.g. the home screen page they
- * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()}
+ * long-pressed on to enter the overview). Try using {@link #getDestinationPage()}
* to get the page the user is currently scrolling over.
*/
public int getCurrentPage() {
@@ -747,6 +745,11 @@
return mOrientationHandler.getChildStart(pageAtIndex);
}
+ protected int getChildVisibleSize(int index) {
+ View layout = getPageAt(index);
+ return mOrientationHandler.getMeasuredSize(layout);
+ }
+
@Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
int page = indexToPage(indexOfChild(child));
@@ -1274,7 +1277,7 @@
if (((initialScroll >= maxScroll) && (isVelocityLeft || !isFling)) ||
((initialScroll <= minScroll) && (!isVelocityLeft || !isFling))) {
mScroller.springBack(initialScroll, minScroll, maxScroll);
- mNextPage = getPageNearestToCenterOfScreen();
+ mNextPage = getDestinationPage();
} else {
mScroller.setInterpolator(mDefaultInterpolator);
mScroller.fling(initialScroll, -velocity,
@@ -1282,11 +1285,12 @@
Math.round(getWidth() * 0.5f * OVERSCROLL_DAMP_FACTOR));
int finalPos = mScroller.getFinalPos();
- mNextPage = getPageNearestToCenterOfScreen(finalPos);
+ mNextPage = getDestinationPage(finalPos);
int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
- if (finalPos > minScroll && finalPos < maxScroll) {
+ if (snapToPageInFreeScroll() && finalPos > minScroll
+ && finalPos < maxScroll) {
// If scrolling ends in the half of the added space that is closer to
// the end, settle to the end. Otherwise snap to the nearest page.
// If flinging past one of the ends, don't change the velocity as it
@@ -1332,6 +1336,10 @@
return true;
}
+ protected boolean snapToPageInFreeScroll() {
+ return true;
+ }
+
protected boolean shouldFlingForVelocity(int velocity) {
float threshold = mAllowEasyFling ? mEasyFlingThresholdVelocity : mFlingThresholdVelocity;
return Math.abs(velocity) > threshold;
@@ -1437,6 +1445,14 @@
}
}
+ public int getDestinationPage() {
+ return getDestinationPage(mOrientationHandler.getPrimaryScroll(this));
+ }
+
+ protected int getDestinationPage(int scaledScroll) {
+ return getPageNearestToCenterOfScreen(scaledScroll);
+ }
+
public int getPageNearestToCenterOfScreen() {
return getPageNearestToCenterOfScreen(mOrientationHandler.getPrimaryScroll(this));
}
@@ -1459,8 +1475,7 @@
}
private int getDisplacementFromScreenCenter(int childIndex, int screenCenter) {
- View layout = getPageAt(childIndex);
- int childSize = mOrientationHandler.getMeasuredSize(layout);
+ int childSize = getChildVisibleSize(childIndex);
int halfChildSize = (childSize / 2);
int childCenter = getChildOffset(childIndex) + halfChildSize;
return childCenter - screenCenter;
@@ -1473,7 +1488,7 @@
}
protected void snapToDestination() {
- snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
+ snapToPage(getDestinationPage(), getPageSnapDuration());
}
protected boolean isInOverScroll() {
diff --git a/src/com/android/launcher3/ResourceUtils.java b/src/com/android/launcher3/ResourceUtils.java
index 403d779..ece123d 100644
--- a/src/com/android/launcher3/ResourceUtils.java
+++ b/src/com/android/launcher3/ResourceUtils.java
@@ -22,8 +22,11 @@
public class ResourceUtils {
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";
public static final String NAVBAR_BOTTOM_GESTURE_SIZE = "navigation_bar_gesture_height";
+ public static final String NAVBAR_BOTTOM_GESTURE_LARGER_SIZE =
+ "navigation_bar_gesture_larger_height";
public static int getNavbarSize(String resName, Resources res) {
return getDimenByName(resName, res, DEFAULT_NAVBAR_VALUE);
@@ -51,7 +54,17 @@
return val;
}
+ public static int getIntegerByName(String resName, Resources res, int defaultValue) {
+ int resId = res.getIdentifier(resName, "integer", "android");
+ return resId != 0 ? res.getInteger(resId) : defaultValue;
+ }
+
public static int pxFromDp(float size, DisplayMetrics metrics) {
- return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, size, metrics));
+ return pxFromDp(size, metrics, 1f);
+ }
+
+ 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));
}
}
diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
index 499b54f..4fd87cb 100644
--- a/src/com/android/launcher3/SecondaryDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -37,19 +37,18 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.FileLog;
-import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.logging.StatsLogManager;
-import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
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.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import java.net.URISyntaxException;
-import java.util.ArrayList;
/**
* Drop target which provides a secondary option for an item.
@@ -136,19 +135,6 @@
}
@Override
- public Target getDropTargetForLogging() {
- Target t = LoggerUtils.newTarget(Target.Type.CONTROL);
- if (mCurrentAccessibilityAction == UNINSTALL) {
- t.controlType = ControlType.UNINSTALL_TARGET;
- } else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- t.controlType = ControlType.DISMISS_PREDICTION;
- } else {
- t.controlType = ControlType.SETTINGS_BUTTON;
- }
- return t;
- }
-
- @Override
protected boolean supportsDrop(ItemInfo info) {
return supportsAccessibilityDrop(info, getViewUnderDrag(info));
}
@@ -218,13 +204,20 @@
public void onDrop(DragObject d, DragOptions options) {
// Defer onComplete
d.dragSource = new DeferredOnComplete(d.dragSource, getContext());
+
super.onDrop(d, options);
+ doLog(d.logInstanceId, d.originalDragInfo);
+ }
+
+ private void doLog(InstanceId logInstanceId, ItemInfo itemInfo) {
+ StatsLogger logger = mStatsLogManager.logger().withInstanceId(logInstanceId);
+ if (itemInfo != null) {
+ logger.withItemInfo(itemInfo);
+ }
if (mCurrentAccessibilityAction == UNINSTALL) {
- mStatsLogManager.logger().withInstanceId(d.logInstanceId)
- .log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
+ logger.log(LAUNCHER_ITEM_DROPPED_ON_UNINSTALL);
} else if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- mStatsLogManager.logger().withInstanceId(d.logInstanceId)
- .log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
+ logger.log(LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST);
}
}
@@ -283,8 +276,7 @@
return null;
}
if (mCurrentAccessibilityAction == DISMISS_PREDICTION) {
- AppLaunchTracker.INSTANCE.get(getContext()).onDismissApp(info.getTargetComponent(),
- info.user, AppLaunchTracker.CONTAINER_PREDICTIONS);
+ // We sent the log event, nothing else left to do
return null;
}
// else: mCurrentAccessibilityAction == UNINSTALL
@@ -311,6 +303,7 @@
@Override
public void onAccessibilityDrop(View view, ItemInfo item) {
+ doLog(new InstanceIdSequence().newInstanceId(), item);
performDropAction(view, item);
}
@@ -338,12 +331,6 @@
}
@Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- mOriginal.fillInLogContainerData(childInfo, child, parents);
- }
-
- @Override
public void onLauncherResume() {
// We use MATCH_UNINSTALLED_PACKAGES as the app can be on SD card as well.
if (new PackageManagerHelper(mContext).getApplicationInfo(mPackageName,
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 89f0a3d..1bbbb2b 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -16,55 +16,37 @@
package com.android.launcher3;
-import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
-
-import android.annotation.TargetApi;
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.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
import android.os.UserHandle;
-import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Log;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.Executors;
-import java.util.List;
-
/**
* BroadcastReceiver to handle session commit intent.
*/
-@TargetApi(Build.VERSION_CODES.O)
public class SessionCommitReceiver extends BroadcastReceiver {
- private static final String TAG = "SessionCommitReceiver";
-
- // The content provider for the add to home screen setting. It should be of the format:
- // <package name>.addtohomescreen
- private static final String MARKER_PROVIDER_PREFIX = ".addtohomescreen";
-
// Preference key for automatically adding icon to homescreen.
public static final String ADD_ICON_PREFERENCE_KEY = "pref_add_icon_to_home";
- public static final String ADD_ICON_PREFERENCE_INITIALIZED_KEY =
- "pref_add_icon_to_home_initialized";
@Override
public void onReceive(Context context, Intent intent) {
- if (!isEnabled(context) || !Utilities.ATLEAST_OREO) {
+ Executors.MODEL_EXECUTOR.execute(() -> processIntent(context, intent));
+ }
+
+ @WorkerThread
+ private static void processIntent(Context context, Intent intent) {
+ if (!isEnabled(context)) {
// User has decided to not add icons on homescreen.
return;
}
@@ -86,105 +68,11 @@
return;
}
- queueAppIconAddition(context, info.getAppPackageName(), user);
- }
-
- public static void queuePromiseAppIconAddition(Context context, SessionInfo sessionInfo) {
- String packageName = sessionInfo.getAppPackageName();
- if (context.getSystemService(LauncherApps.class)
- .getActivityList(packageName, getUserHandle(sessionInfo)).isEmpty()) {
- // Ensure application isn't already installed.
- queueAppIconAddition(context, packageName, sessionInfo.getAppLabel(),
- sessionInfo.getAppIcon(), getUserHandle(sessionInfo));
- }
- }
-
- public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
- List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
- .getActivityList(packageName, user);
- if (activities.isEmpty()) {
- // no activity found
- return;
- }
- queueAppIconAddition(context, packageName, activities.get(0).getLabel(), null, user);
- }
-
- private static void queueAppIconAddition(Context context, String packageName,
- CharSequence label, Bitmap icon, UserHandle user) {
- Intent data = new Intent();
- data.putExtra(Intent.EXTRA_SHORTCUT_INTENT, new Intent().setComponent(
- new ComponentName(packageName, "")).setPackage(packageName));
- data.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
- data.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
-
- InstallShortcutReceiver.queueApplication(data, user, context);
+ ItemInstallQueue.INSTANCE.get(context)
+ .queueItem(info.getAppPackageName(), user);
}
public static boolean isEnabled(Context context) {
return Utilities.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
}
-
- public static void applyDefaultUserPrefs(final Context context) {
- if (!Utilities.ATLEAST_OREO) {
- return;
- }
- SharedPreferences prefs = Utilities.getPrefs(context);
- if (prefs.getAll().isEmpty()) {
- // This logic assumes that the code is the first thing that is executed (before any
- // shared preference is written).
- // TODO: Move this logic to DB upgrade once we have proper support for db downgrade
- // If it is a fresh start, just apply the default value. We use prefs.isEmpty() to infer
- // a fresh start as put preferences always contain some values corresponding to current
- // grid.
- prefs.edit().putBoolean(ADD_ICON_PREFERENCE_KEY, true).apply();
- } else if (!prefs.contains(ADD_ICON_PREFERENCE_INITIALIZED_KEY)) {
- new PrefInitTask(context).executeOnExecutor(Executors.THREAD_POOL_EXECUTOR);
- }
- }
-
- private static class PrefInitTask extends AsyncTask<Void, Void, Void> {
- private final Context mContext;
-
- PrefInitTask(Context context) {
- mContext = context;
- }
-
- @Override
- protected Void doInBackground(Void... voids) {
- boolean addIconToHomeScreenEnabled = readValueFromMarketApp();
- Utilities.getPrefs(mContext).edit()
- .putBoolean(ADD_ICON_PREFERENCE_KEY, addIconToHomeScreenEnabled)
- .putBoolean(ADD_ICON_PREFERENCE_INITIALIZED_KEY, true)
- .apply();
- return null;
- }
-
- public boolean readValueFromMarketApp() {
- // Get the marget package
- ResolveInfo ri = mContext.getPackageManager().resolveActivity(
- new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET),
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_SYSTEM_ONLY);
- if (ri == null) {
- return true;
- }
-
- Cursor c = null;
- try {
- c = mContext.getContentResolver().query(
- Uri.parse("content://" + ri.activityInfo.packageName
- + MARKER_PROVIDER_PREFIX),
- null, null, null, null);
- if (c.moveToNext()) {
- return c.getInt(c.getColumnIndexOrThrow(Settings.NameValueTable.VALUE)) != 0;
- }
- } catch (Exception e) {
- Log.d(TAG, "Error reading add to homescreen preference", e);
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return true;
- }
- }
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 6326b7a..2c24c3a 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -26,25 +26,31 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
-public class ShortcutAndWidgetContainer extends ViewGroup {
+public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.FolderIconParent {
static final String TAG = "ShortcutAndWidgetContainer";
// These are temporary variables to prevent having to allocate a new object just to
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
private final int[] mTmpCellXY = new int[2];
- @ContainerType private final int mContainerType;
+ private final Rect mTempRect = new Rect();
+
+ @ContainerType
+ private final int mContainerType;
private final WallpaperManager mWallpaperManager;
private int mCellWidth;
private int mCellHeight;
+ private int mBorderSpacing;
private int mCountX;
+ private int mCountY;
- private ActivityContext mActivity;
+ private final ActivityContext mActivity;
private boolean mInvertIfRtl = false;
public ShortcutAndWidgetContainer(Context context, @ContainerType int containerType) {
@@ -54,20 +60,23 @@
mContainerType = containerType;
}
- public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY) {
+ public void setCellDimensions(int cellWidth, int cellHeight, int countX, int countY,
+ int borderSpacing) {
mCellWidth = cellWidth;
mCellHeight = cellHeight;
mCountX = countX;
+ mCountY = countY;
+ mBorderSpacing = borderSpacing;
}
- public View getChildAt(int x, int y) {
+ public View getChildAt(int cellX, int cellY) {
final int count = getChildCount();
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
- if ((lp.cellX <= x) && (x < lp.cellX + lp.cellHSpan) &&
- (lp.cellY <= y) && (y < lp.cellY + lp.cellVSpan)) {
+ if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
+ && (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
return child;
}
}
@@ -79,7 +88,7 @@
int count = getChildCount();
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
+ int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(widthSpecSize, heightSpecSize);
for (int i = 0; i < count; i++) {
@@ -94,10 +103,12 @@
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
if (child instanceof LauncherAppWidgetHostView) {
DeviceProfile profile = mActivity.getDeviceProfile();
- lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
- profile.appWidgetScale.x, profile.appWidgetScale.y);
+ ((LauncherAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+ profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
} else {
- lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+ mBorderSpacing, null);
}
}
@@ -116,11 +127,12 @@
final DeviceProfile profile = mActivity.getDeviceProfile();
if (child instanceof LauncherAppWidgetHostView) {
- lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX,
- profile.appWidgetScale.x, profile.appWidgetScale.y);
- // Widgets have their own padding
+ ((LauncherAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+ profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
} else {
- lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX);
+ lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
+ mBorderSpacing, null);
// Center the icon/folder
int cHeight = getCellContentHeight();
int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
@@ -220,4 +232,24 @@
child.cancelLongPress();
}
}
+
+ @Override
+ public void drawFolderLeaveBehindForIcon(FolderIcon child) {
+ CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+ // While the folder is open, the position of the icon cannot change.
+ lp.canReorder = false;
+ if (mContainerType == CellLayout.HOTSEAT) {
+ CellLayout cl = (CellLayout) getParent();
+ cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+ }
+ }
+
+ @Override
+ public void clearFolderLeaveBehind(FolderIcon child) {
+ ((CellLayout.LayoutParams) child.getLayoutParams()).canReorder = true;
+ if (mContainerType == CellLayout.HOTSEAT) {
+ CellLayout cl = (CellLayout) getParent();
+ cl.clearFolderLeaveBehind();
+ }
+ }
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 292a808..c440303 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_ICON_BADGED;
-import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.Person;
@@ -26,6 +25,7 @@
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;
@@ -34,6 +34,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.res.Resources;
+import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Matrix;
@@ -44,11 +45,11 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
+import android.net.Uri;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.Message;
-import android.os.PowerManager;
import android.os.TransactionTooLargeException;
import android.provider.Settings;
import android.text.Spannable;
@@ -65,7 +66,6 @@
import androidx.core.os.BuildCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.GridCustomizationsProvider;
import com.android.launcher3.graphics.TintedDrawableSpan;
@@ -85,6 +85,7 @@
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;
@@ -106,18 +107,13 @@
public static final String[] EMPTY_STRING_ARRAY = new String[0];
public static final Person[] EMPTY_PERSON_ARRAY = new Person[0];
- public static final boolean ATLEAST_R = BuildCompat.isAtLeastR();
+ public static final boolean ATLEAST_P = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
public static final boolean ATLEAST_Q = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
- public static final boolean ATLEAST_P =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
+ public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
- public static final boolean ATLEAST_OREO_MR1 =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
-
- public static final boolean ATLEAST_OREO =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
+ public static final boolean ATLEAST_S = BuildCompat.isAtLeastS();
/**
* Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
@@ -141,6 +137,10 @@
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();
@@ -405,6 +405,11 @@
return (size / densityRatio);
}
+ /** Converts a dp value to pixels for the current device. */
+ public static int dpToPx(float dp) {
+ return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
+ }
+
public static int pxFromSp(float size, DisplayMetrics metrics) {
return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
size, metrics));
@@ -456,6 +461,15 @@
}
/**
+ * 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
@@ -494,21 +508,6 @@
LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
}
- /**
- * @return {@link SharedPreferences} that backs {@link FeatureFlags}
- */
- public static SharedPreferences getFeatureFlagsPrefs(Context context) {
- // Use application context for shared preferences, so that we use a single cached instance
- return context.getApplicationContext().getSharedPreferences(
- FeatureFlags.FLAGS_PREF_NAME, Context.MODE_PRIVATE);
- }
-
- public static boolean areAnimationsEnabled(Context context) {
- return ATLEAST_OREO
- ? ValueAnimator.areAnimatorsEnabled()
- : !context.getSystemService(PowerManager.class).isPowerSaveMode();
- }
-
public static boolean isWallpaperAllowed(Context context) {
return context.getSystemService(WallpaperManager.class).isSetWallpaperAllowed();
}
@@ -606,7 +605,6 @@
outObj[0] = activityInfo;
return activityInfo.getFullResIcon(appState.getIconCache());
}
- if (info.getIntent() == null || info.getIntent().getPackage() == null) return null;
List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
.buildRequest(launcher)
.query(ShortcutRequest.ALL);
@@ -663,6 +661,14 @@
}
}
+ /**
+ * @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;
}
@@ -672,6 +678,18 @@
return slop * slop;
}
+ /**
+ * 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);
+ }
+ };
+ }
+
private static class FixedSizeEmptyDrawable extends ColorDrawable {
private final int mSize;
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 2c45c77..446c873 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -50,6 +50,7 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.SQLiteCacheHelper;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetManagerHelper;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a6283ff..0cc965c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -22,12 +22,12 @@
import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
import static com.android.launcher3.LauncherState.FLAG_WORKSPACE_INACCESSIBLE;
+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.LauncherState.SPRING_LOADED;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
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;
@@ -63,7 +63,6 @@
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
-import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.Interpolators;
@@ -81,11 +80,11 @@
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.graphics.WorkspaceDragScrim;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -93,12 +92,10 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.WorkspaceTouchListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
@@ -106,6 +103,8 @@
import com.android.launcher3.util.PackageUserKey;
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.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -114,8 +113,11 @@
import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -141,6 +143,10 @@
public static final int DEFAULT_PAGE = 0;
+ private static final int DEFAULT_SMARTSPACE_HEIGHT = 1;
+
+ private static final int EXPANDED_SMARTSPACE_HEIGHT = 2;
+
private LayoutTransition mLayoutTransition;
@Thunk final WallpaperManager mWallpaperManager;
@@ -180,7 +186,6 @@
@Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
- private final Rect mTempRect = new Rect();
private final int[] mTempXY = new int[2];
private final float[] mTempFXY = new float[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
@@ -195,6 +200,9 @@
private boolean mStripScreensOnPageStopMoving = false;
private DragPreviewProvider mOutlineProvider = null;
+
+ private WorkspaceDragScrim mWorkspaceDragScrim;
+
private boolean mWorkspaceFadeInAdjacentScreens;
final WallpaperOffsetInterpolator mWallpaperOffset;
@@ -358,10 +366,19 @@
}
public float getWallpaperOffsetForCenterPage() {
- int pageScroll = getScrollForPage(getPageNearestToCenterOfScreen());
+ return getWallpaperOffsetForPage(getPageNearestToCenterOfScreen());
+ }
+
+ private float getWallpaperOffsetForPage(int page) {
+ int pageScroll = getScrollForPage(page);
return mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
}
+ /** Returns the number of pages used for the wallpaper parallax. */
+ public int getNumPagesForWallpaperParallax() {
+ return mWallpaperOffset.getNumPagesForWallpaperParallax();
+ }
+
public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
Rect r = new Rect();
cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
@@ -408,7 +425,7 @@
// widgets as they cannot be placed inside a folder.
// Start at the current page and search right (on LTR) until finding a page with
// enough space. Since an empty screen is the furthest right, a page must be found.
- int currentPage = getPageNearestToCenterOfScreen();
+ int currentPage = getDestinationPage();
for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
CellLayout page = (CellLayout) getPageAt(pageIndex);
if (page.hasReorderSolution(dragObject.dragInfo)) {
@@ -436,11 +453,20 @@
enforceDragParity("onDragEnd", 0, 0);
}
- if (!mDeferRemoveExtraEmptyScreen) {
- removeExtraEmptyScreen(mDragSourceInternal != null);
- }
-
updateChildrenLayersEnabled();
+ StateManager<LauncherState> stateManager = mLauncher.getStateManager();
+ stateManager.addStateListener(new StateManager.StateListener<LauncherState>() {
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState == NORMAL) {
+ if (!mDeferRemoveExtraEmptyScreen) {
+ removeExtraEmptyScreen(true /* stripEmptyScreens */);
+ }
+ stateManager.removeStateListener(this);
+ }
+ }
+ });
+
mDragInfo = null;
mOutlineProvider = null;
mDragSourceInternal = null;
@@ -513,7 +539,10 @@
.inflate(R.layout.search_container_workspace, firstPage, false);
}
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
+ int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
+ ? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
+ CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(),
+ cellVSpan);
lp.canReorder = false;
if (!firstPage.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
@@ -657,6 +686,7 @@
convertFinalScreenToEmptyScreenIfNecessary();
if (hasExtraEmptyScreen()) {
removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID));
+ setCurrentPage(getNextPage());
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
@@ -942,7 +972,10 @@
super.onScrollChanged(l, t, oldl, oldt);
// Update the page indicator progress.
- boolean isTransitioning = mIsSwitchingState
+ // Unlike from other states, we show the page indicator when transitioning from HINT_STATE.
+ boolean isSwitchingState = mIsSwitchingState
+ && mLauncher.getStateManager().getCurrentStableState() != HINT_STATE;
+ boolean isTransitioning = isSwitchingState
|| (getLayoutTransition() != null && getLayoutTransition().isRunning());
if (!isTransitioning) {
showPageIndicatorAtCurrentScroll();
@@ -1003,8 +1036,6 @@
public void onOverlayScrollChanged(float scroll) {
if (Float.compare(scroll, 1f) == 0) {
if (!mOverlayShown) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
- Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
mLauncher.getStatsLogManager().logger()
.withSrcState(LAUNCHER_STATE_HOME)
.withDstState(LAUNCHER_STATE_HOME)
@@ -1019,20 +1050,16 @@
// Not announcing the overlay page for accessibility since it announces itself.
} else if (Float.compare(scroll, 0f) == 0) {
if (mOverlayShown) {
- UserEventDispatcher ued = mLauncher.getUserEventDispatcher();
- if (!ued.isPreviousHomeGesture()) {
- mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
- Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
- 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);
- }
+ // 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
@@ -1123,12 +1150,8 @@
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
if (prevPage != mCurrentPage) {
- int swipeDirection = (prevPage < mCurrentPage)
- ? Action.Direction.RIGHT : Action.Direction.LEFT;
StatsLogManager.EventEnum event = (prevPage < mCurrentPage)
? LAUNCHER_SWIPERIGHT : LAUNCHER_SWIPELEFT;
- mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
- swipeDirection, ContainerType.WORKSPACE, prevPage);
mLauncher.getStatsLogManager().logger()
.withSrcState(LAUNCHER_STATE_HOME)
.withDstState(LAUNCHER_STATE_HOME)
@@ -1164,6 +1187,19 @@
}
}
+ public void setWorkspaceDragScrim(WorkspaceDragScrim workspaceDragScrim) {
+ mWorkspaceDragScrim = workspaceDragScrim;
+ }
+
+ @Override
+ public void invalidate() {
+ // The workspace scrim may need to be re-rendered based on the workspace scroll
+ if (mWorkspaceDragScrim != null) {
+ mWorkspaceDragScrim.invalidate();
+ }
+ super.invalidate();
+ }
+
@Override
public void computeScroll() {
super.computeScroll();
@@ -1175,13 +1211,6 @@
}
@Override
- protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
- if (!isSwitchingState()) {
- super.determineScrollingStart(ev, touchSlopScale);
- }
- }
-
- @Override
public void announceForAccessibility(CharSequence text) {
// Don't announce if apps is on top of us.
if (!mLauncher.isInState(ALL_APPS)) {
@@ -1501,7 +1530,6 @@
.showForIcon((BubbleTextView) child);
if (popupContainer != null) {
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
}
}
@@ -1701,7 +1729,6 @@
fi.addItem(destInfo);
fi.addItem(sourceInfo);
}
- mLauncher.folderCreatedFromItem(fi.getFolder(), destInfo);
return true;
}
return false;
@@ -1719,9 +1746,8 @@
FolderIcon fi = (FolderIcon) dropOverView;
if (fi.acceptDrop(d.dragInfo)) {
mStatsLogManager.logger().withItemInfo(fi.mInfo).withInstanceId(d.logInstanceId)
- .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED);
+ .log(LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED_ON_FOLDER_ICON);
fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
-
// if the drag started here, we need to remove it from the workspace
if (!external) {
getParentCellLayoutForView(mDragInfo.cell).removeView(mDragInfo.cell);
@@ -1872,7 +1898,6 @@
};
}
}
-
mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
lp.cellX, lp.cellY, item.spanX, item.spanY);
} else {
@@ -2028,7 +2053,7 @@
}
// Invalidating the scrim will also force this CellLayout
// to be invalidated so that it is highlighted if necessary.
- mLauncher.getDragLayer().getScrim().invalidate();
+ mLauncher.getDragLayer().getWorkspaceDragScrim().invalidate();
}
public CellLayout getCurrentDragOverlappingLayout() {
@@ -2087,40 +2112,40 @@
mLastReorderY = -1;
}
- /*
- *
- * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
- * coordinate space. The argument xy is modified with the return result.
- */
- private void mapPointFromSelfToChild(View v, float[] xy) {
- xy[0] = xy[0] - v.getLeft();
- xy[1] = xy[1] - v.getTop();
- }
+ /*
+ *
+ * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
+ * coordinate space. The argument xy is modified with the return result.
+ */
+ private void mapPointFromSelfToChild(View v, float[] xy) {
+ xy[0] = xy[0] - v.getLeft();
+ xy[1] = xy[1] - v.getTop();
+ }
- boolean isPointInSelfOverHotseat(int x, int y) {
- mTempFXY[0] = x;
- mTempFXY[1] = y;
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
- View hotseat = mLauncher.getHotseat();
- return mTempFXY[0] >= hotseat.getLeft() &&
- mTempFXY[0] <= hotseat.getRight() &&
- mTempFXY[1] >= hotseat.getTop() &&
- mTempFXY[1] <= hotseat.getBottom();
- }
+ boolean isPointInSelfOverHotseat(int x, int y) {
+ mTempFXY[0] = x;
+ mTempFXY[1] = y;
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
+ View hotseat = mLauncher.getHotseat();
+ return mTempFXY[0] >= hotseat.getLeft()
+ && mTempFXY[0] <= hotseat.getRight()
+ && mTempFXY[1] >= hotseat.getTop()
+ && mTempFXY[1] <= hotseat.getBottom();
+ }
/**
* Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
* @param layout either hotseat of a page in workspace
* @param xy the point location in workspace co-ordinate space
*/
- private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
- if (mLauncher.isHotseatLayout(layout)) {
- mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
- mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
- } else {
- mapPointFromSelfToChild(layout, xy);
- }
- }
+ private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
+ if (mLauncher.isHotseatLayout(layout)) {
+ mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
+ mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
+ } else {
+ mapPointFromSelfToChild(layout, xy);
+ }
+ }
private boolean isDragWidget(DragObject d) {
return (d.dragInfo instanceof LauncherAppWidgetInfo ||
@@ -2402,9 +2427,9 @@
spanY = mDragInfo.spanY;
}
- final int container = mLauncher.isHotseatLayout(cellLayout) ?
- LauncherSettings.Favorites.CONTAINER_HOTSEAT :
- LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ final int container = mLauncher.isHotseatLayout(cellLayout)
+ ? LauncherSettings.Favorites.CONTAINER_HOTSEAT
+ : LauncherSettings.Favorites.CONTAINER_DESKTOP;
final int screenId = getIdForScreen(cellLayout);
if (!mLauncher.isHotseatLayout(cellLayout)
&& screenId != getScreenIdForPageIndex(mCurrentPage)
@@ -2452,7 +2477,7 @@
Runnable onAnimationCompleteRunnable = new Runnable() {
@Override
public void run() {
- // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
+ // Normally removeExtraEmptyScreen is called in Workspace#onDrop, but when
// adding an item that may not be dropped right away (due to a config activity)
// we defer the removal until the activity returns.
deferRemoveExtraEmptyScreen();
@@ -2956,7 +2981,7 @@
* @param operators List of operators, in order starting from best matching operator.
* @return
*/
- private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
+ View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator... operators) {
// This array is filled with the first match for each operator.
final View[] matches = new View[operators.length];
// For efficiency, the outer loop should be CellLayout.
@@ -3004,38 +3029,27 @@
* shortcuts are not removed.
*/
public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
- for (final CellLayout layoutParent: getWorkspaceAndHotseatCellLayouts()) {
- final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
+ for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
+ ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+ // Iterate in reverse order as we are removing items
+ for (int i = container.getChildCount() - 1; i >= 0; i--) {
+ View child = container.getChildAt(i);
+ ItemInfo info = (ItemInfo) child.getTag();
- IntSparseArrayMap<View> idToViewMap = new IntSparseArrayMap<>();
- ArrayList<ItemInfo> items = new ArrayList<>();
- for (int j = 0; j < layout.getChildCount(); j++) {
- final View view = layout.getChildAt(j);
- if (view.getTag() instanceof ItemInfo) {
- ItemInfo item = (ItemInfo) view.getTag();
- items.add(item);
- idToViewMap.put(item.id, view);
- }
- }
-
- for (ItemInfo itemToRemove : matcher.filterItemInfos(items)) {
- View child = idToViewMap.get(itemToRemove.id);
-
- if (child != null) {
- // Note: We can not remove the view directly from CellLayoutChildren as this
- // does not re-mark the spaces as unoccupied.
- layoutParent.removeViewInLayout(child);
+ if (matcher.matchesInfo(info)) {
+ layout.removeViewInLayout(child);
if (child instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) child);
}
- } else if (itemToRemove.container >= 0) {
- // The item may belong to a folder.
- View parent = idToViewMap.get(itemToRemove.container);
- if (parent instanceof FolderIcon) {
- FolderInfo folderInfo = (FolderInfo) parent.getTag();
- folderInfo.remove((WorkspaceItemInfo) itemToRemove, false);
- if (((FolderIcon) parent).getFolder().isOpen()) {
- ((FolderIcon) parent).getFolder().close(false /* animate */);
+ } else if (child instanceof FolderIcon) {
+ FolderInfo folderInfo = (FolderInfo) info;
+ List<WorkspaceItemInfo> matches = folderInfo.contents.stream()
+ .filter(matcher::matchesInfo)
+ .collect(Collectors.toList());
+ if (!matches.isEmpty()) {
+ folderInfo.removeAll(matches, false);
+ if (((FolderIcon) child).getFolder().isOpen()) {
+ ((FolderIcon) child).getFolder().close(false /* animate */);
}
}
}
@@ -3087,7 +3101,7 @@
return false;
}
- void updateShortcuts(ArrayList<WorkspaceItemInfo> shortcuts) {
+ void updateShortcuts(List<WorkspaceItemInfo> shortcuts) {
final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
ItemOperator op = (info, v) -> {
if (v instanceof BubbleTextView && updates.contains(info)) {
@@ -3145,9 +3159,8 @@
}
public void removeAbandonedPromise(String packageName, UserHandle user) {
- HashSet<String> packages = new HashSet<>(1);
- packages.add(packageName);
- ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
+ ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(
+ Collections.singleton(packageName), user);
mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
removeItemsByMatcher(matcher);
}
@@ -3156,7 +3169,7 @@
ItemOperator op = (info, v) -> {
if (info instanceof WorkspaceItemInfo && v instanceof BubbleTextView
&& updates.contains(info)) {
- ((BubbleTextView) v).applyPromiseState(false /* promiseStateChanged */);
+ ((BubbleTextView) v).applyLoadingState(false /* promiseStateChanged */);
} else if (v instanceof PendingAppWidgetHostView
&& info instanceof LauncherAppWidgetInfo
&& updates.contains(info)) {
@@ -3262,29 +3275,11 @@
}
if (nScreens == 0) {
// When the workspace is not loaded, we do not know how many screen will be bound.
- return getContext().getString(R.string.all_apps_home_button_label);
+ return getContext().getString(R.string.home_screen);
}
return getContext().getString(R.string.workspace_scroll_format, page + 1, nScreens);
}
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- if (childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
- || childInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
- getHotseat().fillInLogContainerData(childInfo, child, parents);
- return;
- } else if (childInfo.container >= 0) {
- FolderIcon icon = (FolderIcon) getHomescreenIconByItemId(childInfo.container);
- icon.getFolder().fillInLogContainerData(childInfo, child, parents);
- return;
- }
- child.gridX = childInfo.cellX;
- child.gridY = childInfo.cellY;
- child.pageIndex = getCurrentPage();
- parents.add(newContainerTarget(ContainerType.WORKSPACE));
- }
-
/**
* Used as a workaround to ensure that the AppWidgetService receives the
* PACKAGE_ADDED broadcast before updating widgets.
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index c3d4aeb..d6302ce 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -28,7 +28,7 @@
String TAG = "Launcher.Workspace";
- // The screen id used for the empty screen always present to the right.
+ // The screen id used for the empty screen always present at the end.
int EXTRA_EMPTY_SCREEN_ID = -201;
// The is the first screen. It is always present, even if its empty.
int FIRST_SCREEN_ID = 0;
@@ -130,12 +130,16 @@
}
child.setHapticFeedbackEnabled(false);
- child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
+ child.setOnLongClickListener(getWorkspaceChildOnLongClickListener());
if (child instanceof DropTarget) {
onAddDropTarget((DropTarget) child);
}
}
+ default View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
+ return ItemLongClickListener.INSTANCE_WORKSPACE;
+ }
+
Hotseat getHotseat();
CellLayout getScreenWithId(int screenId);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index cd938e1..660eeab 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -28,11 +28,13 @@
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.WORKSPACE_PAGE_INDICATOR;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
-import static com.android.launcher3.graphics.WorkspaceAndHotseatScrim.SYSUI_PROGRESS;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
@@ -49,7 +51,8 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
+import com.android.launcher3.graphics.SysUiScrim;
+import com.android.launcher3.graphics.WorkspaceDragScrim;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.util.DynamicResource;
import com.android.systemui.plugins.ResourceProvider;
@@ -140,9 +143,11 @@
}
float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
- propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha, fadeInterpolator);
+ propertySetter.setViewAlpha(hotseat, hotseatIconsAlpha,
+ config.getInterpolator(ANIM_HOTSEAT_FADE, fadeInterpolator));
+ float workspacePageIndicatorAlpha = (elements & WORKSPACE_PAGE_INDICATOR) != 0 ? 1 : 0;
propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
- hotseatIconsAlpha, fadeInterpolator);
+ workspacePageIndicatorAlpha, fadeInterpolator);
}
if (config.onlyPlayAtomicComponent()) {
@@ -183,10 +188,12 @@
}
public void setScrim(PropertySetter propertySetter, LauncherState state) {
- WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
- propertySetter.setFloat(scrim, SCRIM_PROGRESS, state.getWorkspaceScrimAlpha(mLauncher),
- LINEAR);
- propertySetter.setFloat(scrim, SYSUI_PROGRESS,
+ WorkspaceDragScrim workspaceDragScrim = mLauncher.getDragLayer().getWorkspaceDragScrim();
+ propertySetter.setFloat(workspaceDragScrim, SCRIM_PROGRESS,
+ state.getWorkspaceScrimAlpha(mLauncher), LINEAR);
+
+ SysUiScrim sysUiScrim = mLauncher.getDragLayer().getSysUiScrim();
+ propertySetter.setFloat(sysUiScrim, SYSUI_PROGRESS,
state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR);
}
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index ddb547f..d0fc175 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -31,6 +31,7 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.dragndrop.DragLayer;
import java.util.List;
@@ -41,30 +42,32 @@
implements OnClickListener, OnHoverListener {
protected static final int INVALID_POSITION = -1;
- private static final int[] sTempArray = new int[2];
+ protected final Rect mTempRect = new Rect();
+ protected final int[] mTempCords = new int[2];
protected final CellLayout mView;
protected final Context mContext;
protected final LauncherAccessibilityDelegate mDelegate;
-
- private final Rect mTempRect = new Rect();
+ protected final DragLayer mDragLayer;
public DragAndDropAccessibilityDelegate(CellLayout forView) {
super(forView);
mView = forView;
mContext = mView.getContext();
- mDelegate = Launcher.getLauncher(mContext).getAccessibilityDelegate();
+ Launcher launcher = Launcher.getLauncher(mContext);
+ mDelegate = launcher.getAccessibilityDelegate();
+ mDragLayer = launcher.getDragLayer();
}
@Override
- protected int getVirtualViewAt(float x, float y) {
+ public int getVirtualViewAt(float x, float y) {
if (x < 0 || y < 0 || x > mView.getMeasuredWidth() || y > mView.getMeasuredHeight()) {
return INVALID_ID;
}
- mView.pointToCellExact((int) x, (int) y, sTempArray);
+ mView.pointToCellExact((int) x, (int) y, mTempCords);
// Map cell to id
- int id = sTempArray[0] + sTempArray[1] * mView.getCountX();
+ int id = mTempCords[0] + mTempCords[1] * mView.getCountX();
return intersectsValidDropTarget(id);
}
@@ -75,7 +78,7 @@
protected abstract int intersectsValidDropTarget(int id);
@Override
- protected void getVisibleVirtualViews(List<Integer> virtualViews) {
+ public void getVisibleVirtualViews(List<Integer> virtualViews) {
// We create a virtual view for each cell of the grid
// The cell ids correspond to cells in reading order.
int nCells = mView.getCountX() * mView.getCountY();
@@ -88,7 +91,7 @@
}
@Override
- protected boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
+ public boolean onPerformActionForVirtualView(int viewId, int action, Bundle args) {
if (action == AccessibilityNodeInfoCompat.ACTION_CLICK && viewId != INVALID_ID) {
String confirmation = getConfirmationForIconDrop(viewId);
mDelegate.handleAccessibleDrop(mView, getItemBounds(viewId), confirmation);
@@ -112,13 +115,25 @@
}
@Override
- protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
+ public void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
if (id == INVALID_ID) {
throw new IllegalArgumentException("Invalid virtual view id");
}
node.setContentDescription(getLocationDescriptionForIconDrop(id));
- node.setBoundsInParent(getItemBounds(id));
+
+ Rect itemBounds = getItemBounds(id);
+ node.setBoundsInParent(itemBounds);
+
+ // ExploreByTouchHelper does not currently handle view scale.
+ // Update BoundsInScreen to appropriate value.
+ mTempCords[0] = mTempCords[1] = 0;
+ float scale = mDragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
+ mTempRect.left = mTempCords[0] + (int) (itemBounds.left * scale);
+ mTempRect.right = mTempCords[0] + (int) (itemBounds.right * scale);
+ mTempRect.top = mTempCords[1] + (int) (itemBounds.top * scale);
+ mTempRect.bottom = mTempCords[1] + (int) (itemBounds.bottom * scale);
+ node.setBoundsInScreen(mTempRect);
node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
node.setClickable(true);
@@ -130,6 +145,13 @@
return dispatchHoverEvent(motionEvent);
}
+ /**
+ * Returns the target host container
+ */
+ public View getHost() {
+ return mView;
+ }
+
protected abstract String getLocationDescriptionForIconDrop(int id);
protected abstract String getConfirmationForIconDrop(int id);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 136d43e..db7fd3f 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -3,17 +3,18 @@
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
-import android.app.AlertDialog;
import android.appwidget.AppWidgetProviderInfo;
-import android.content.DialogInterface;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
+import android.view.KeyEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -25,7 +26,6 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
@@ -33,21 +33,26 @@
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.keyboard.CustomActionsPopup;
+import com.android.launcher3.keyboard.KeyboardDragAndDropView;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.popup.ArrowPopup;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.views.OptionsPopupView.OptionItem;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener {
@@ -77,93 +82,105 @@
public View item;
}
- protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
- @Thunk final Launcher mLauncher;
+ protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
+ protected final Launcher mLauncher;
private DragInfo mDragInfo = null;
public LauncherAccessibilityDelegate(Launcher launcher) {
mLauncher = launcher;
- mActions.put(REMOVE, new AccessibilityAction(REMOVE,
- launcher.getText(R.string.remove_drop_target_label)));
- mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
- launcher.getText(R.string.uninstall_drop_target_label)));
- mActions.put(DISMISS_PREDICTION, new AccessibilityAction(DISMISS_PREDICTION,
- launcher.getText(R.string.dismiss_prediction_label)));
- mActions.put(RECONFIGURE, new AccessibilityAction(RECONFIGURE,
- launcher.getText(R.string.gadget_setup_text)));
- mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
- launcher.getText(R.string.action_add_to_workspace)));
- mActions.put(MOVE, new AccessibilityAction(MOVE,
- launcher.getText(R.string.action_move)));
- mActions.put(MOVE_TO_WORKSPACE, new AccessibilityAction(MOVE_TO_WORKSPACE,
- launcher.getText(R.string.action_move_to_workspace)));
- mActions.put(RESIZE, new AccessibilityAction(RESIZE,
- launcher.getText(R.string.action_resize)));
- mActions.put(DEEP_SHORTCUTS, new AccessibilityAction(DEEP_SHORTCUTS,
- launcher.getText(R.string.action_deep_shortcut)));
- mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new AccessibilityAction(DEEP_SHORTCUTS,
- launcher.getText(R.string.shortcuts_menu_with_notifications_description)));
- }
-
- public void addAccessibilityAction(int action, int actionLabel) {
- mActions.put(action, new AccessibilityAction(action, mLauncher.getText(actionLabel)));
+ mActions.put(REMOVE, new LauncherAction(
+ REMOVE, R.string.remove_drop_target_label, KeyEvent.KEYCODE_X));
+ mActions.put(UNINSTALL, new LauncherAction(
+ UNINSTALL, R.string.uninstall_drop_target_label, KeyEvent.KEYCODE_U));
+ mActions.put(DISMISS_PREDICTION, new LauncherAction(DISMISS_PREDICTION,
+ R.string.dismiss_prediction_label, KeyEvent.KEYCODE_X));
+ mActions.put(RECONFIGURE, new LauncherAction(
+ RECONFIGURE, R.string.gadget_setup_text, KeyEvent.KEYCODE_E));
+ mActions.put(ADD_TO_WORKSPACE, new LauncherAction(
+ ADD_TO_WORKSPACE, R.string.action_add_to_workspace, KeyEvent.KEYCODE_P));
+ mActions.put(MOVE, new LauncherAction(
+ MOVE, R.string.action_move, KeyEvent.KEYCODE_M));
+ mActions.put(MOVE_TO_WORKSPACE, new LauncherAction(MOVE_TO_WORKSPACE,
+ R.string.action_move_to_workspace, KeyEvent.KEYCODE_P));
+ mActions.put(RESIZE, new LauncherAction(
+ RESIZE, R.string.action_resize, KeyEvent.KEYCODE_R));
+ mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
+ R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
+ mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new LauncherAction(DEEP_SHORTCUTS,
+ R.string.shortcuts_menu_with_notifications_description, KeyEvent.KEYCODE_S));
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(host, info);
- addSupportedActions(host, info, false);
+ if (host.getTag() instanceof ItemInfo) {
+ ItemInfo item = (ItemInfo) host.getTag();
+
+ List<LauncherAction> actions = new ArrayList<>();
+ getSupportedActions(host, item, actions);
+ actions.forEach(la -> info.addAction(la.accessibilityAction));
+
+ if (!itemSupportsLongClick(host, item)) {
+ info.setLongClickable(false);
+ info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
+ }
+ }
}
- public void addSupportedActions(View host, AccessibilityNodeInfo info, boolean fromKeyboard) {
- if (!(host.getTag() instanceof ItemInfo)) return;
- ItemInfo item = (ItemInfo) host.getTag();
-
- if (host instanceof AccessibilityActionHandler) {
- ((AccessibilityActionHandler) host).addSupportedAccessibilityActions(info);
- }
-
+ /**
+ * Adds all the accessibility actions that can be handled.
+ */
+ protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
// If the request came from keyboard, do not add custom shortcuts as that is already
// exposed as a direct shortcut
- if (!fromKeyboard && ShortcutUtil.supportsShortcuts(item)) {
- info.addAction(mActions.get(NotificationListener.getInstanceIfConnected() != null
+ if (ShortcutUtil.supportsShortcuts(item)) {
+ out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null
? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
}
for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
if (target.supportsAccessibilityDrop(item, host)) {
- info.addAction(mActions.get(target.getAccessibilityAction()));
+ out.add(mActions.get(target.getAccessibilityAction()));
}
}
// Do not add move actions for keyboard request as this uses virtual nodes.
- if (!fromKeyboard && itemSupportsAccessibleDrag(item)) {
- info.addAction(mActions.get(MOVE));
+ if (itemSupportsAccessibleDrag(item)) {
+ out.add(mActions.get(MOVE));
if (item.container >= 0) {
- info.addAction(mActions.get(MOVE_TO_WORKSPACE));
+ out.add(mActions.get(MOVE_TO_WORKSPACE));
} else if (item instanceof LauncherAppWidgetInfo) {
if (!getSupportedResizeActions(host, (LauncherAppWidgetInfo) item).isEmpty()) {
- info.addAction(mActions.get(RESIZE));
+ out.add(mActions.get(RESIZE));
}
}
}
- if (!fromKeyboard && !itemSupportsLongClick(host, item)) {
- info.setLongClickable(false);
- info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
- }
-
if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
- info.addAction(mActions.get(ADD_TO_WORKSPACE));
+ out.add(mActions.get(ADD_TO_WORKSPACE));
}
}
+ /**
+ * Returns all the accessibility actions that can be handled by the host.
+ */
+ public static List<LauncherAction> getSupportedActions(Launcher launcher, View host) {
+ if (host == null || !(host.getTag() instanceof ItemInfo)) {
+ return Collections.emptyList();
+ }
+ PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
+ LauncherAccessibilityDelegate delegate = container != null
+ ? container.getAccessibilityDelegate() : launcher.getAccessibilityDelegate();
+ List<LauncherAction> result = new ArrayList<>();
+ delegate.getSupportedActions(host, (ItemInfo) host.getTag(), result);
+ return result;
+ }
+
private boolean itemSupportsLongClick(View host, ItemInfo info) {
- return PopupContainerWithArrow.canShow(host, info)
- || new CustomActionsPopup(mLauncher, host).canShow();
+ return PopupContainerWithArrow.canShow(host, info);
}
private boolean itemSupportsAccessibleDrag(ItemInfo item) {
@@ -178,13 +195,17 @@
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if ((host.getTag() instanceof ItemInfo)
- && performAction(host, (ItemInfo) host.getTag(), action)) {
+ && performAction(host, (ItemInfo) host.getTag(), action, false)) {
return true;
}
return super.performAccessibilityAction(host, action, args);
}
- public boolean performAction(final View host, final ItemInfo item, int action) {
+ /**
+ * Performs the provided action on the host
+ */
+ protected boolean performAction(final View host, final ItemInfo item, int action,
+ boolean fromKeyboard) {
if (action == ACTION_LONG_CLICK) {
if (PopupContainerWithArrow.canShow(host, item)) {
// Long press should be consumed for workspace items, and it should invoke the
@@ -192,20 +213,9 @@
// standard long press path does.
PopupContainerWithArrow.showForIcon((BubbleTextView) host);
return true;
- } else {
- CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
- if (popup.canShow()) {
- popup.show();
- return true;
- }
}
- }
- if (host instanceof AccessibilityActionHandler
- && ((AccessibilityActionHandler) host).performAccessibilityAction(action, item)) {
- return true;
- }
- if (action == MOVE) {
- beginAccessibleDrag(host, item);
+ } else if (action == MOVE) {
+ return beginAccessibleDrag(host, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
@@ -219,9 +229,7 @@
Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- ArrayList<ItemInfo> itemList = new ArrayList<>();
- itemList.add(info);
- mLauncher.bindItems(itemList, true);
+ mLauncher.bindItems(Collections.singletonList(info), true);
announceConfirmation(R.string.item_added_to_workspace);
} else if (item instanceof PendingAddItemInfo) {
PendingAddItemInfo info = (PendingAddItemInfo) item;
@@ -242,47 +250,31 @@
final int[] coordinates = new int[2];
final int screenId = findSpaceOnWorkspace(item, coordinates);
mLauncher.getModelWriter().moveItemInDatabase(info,
- LauncherSettings.Favorites.CONTAINER_DESKTOP,
+ Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
// Bind the item in next frame so that if a new workspace page was created,
// it will get laid out.
- new Handler().post(new Runnable() {
-
- @Override
- public void run() {
- ArrayList<ItemInfo> itemList = new ArrayList<>();
- itemList.add(item);
- mLauncher.bindItems(itemList, true);
- announceConfirmation(R.string.item_moved);
- }
+ new Handler().post(() -> {
+ mLauncher.bindItems(Collections.singletonList(item), true);
+ announceConfirmation(R.string.item_moved);
});
+ return true;
} else if (action == RESIZE) {
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
- final IntArray actions = getSupportedResizeActions(host, info);
- CharSequence[] labels = new CharSequence[actions.size()];
- for (int i = 0; i < actions.size(); i++) {
- labels[i] = mLauncher.getText(actions.get(i));
- }
-
- new AlertDialog.Builder(mLauncher)
- .setTitle(R.string.action_resize)
- .setItems(labels, new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- performResizeAction(actions.get(which), host, info);
- dialog.dismiss();
- }
- })
- .show();
+ List<OptionItem> actions = getSupportedResizeActions(host, info);
+ Rect pos = new Rect();
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
+ ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions);
+ popup.requestFocus();
+ popup.setOnCloseCallback(host::requestFocus);
return true;
- } else if (action == DEEP_SHORTCUTS) {
+ } else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
} else {
for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
- if (dropTarget.supportsAccessibilityDrop(item, host) &&
- action == dropTarget.getAccessibilityAction()) {
+ if (dropTarget.supportsAccessibilityDrop(item, host)
+ && action == dropTarget.getAccessibilityAction()) {
dropTarget.onAccessibilityDrop(host, item);
return true;
}
@@ -291,9 +283,8 @@
return false;
}
- private IntArray getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
- IntArray actions = new IntArray();
-
+ private List<OptionItem> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
+ List<OptionItem> actions = new ArrayList<>();
AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
if (providerInfo == null) {
return actions;
@@ -303,28 +294,40 @@
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
- actions.add(R.string.action_increase_width);
+ actions.add(new OptionItem(
+ R.string.action_increase_width, R.drawable.ic_widget_width_increase,
+ IGNORE,
+ v -> performResizeAction(R.string.action_increase_width, host, info)));
}
if (info.spanX > info.minSpanX && info.spanX > 1) {
- actions.add(R.string.action_decrease_width);
+ actions.add(new OptionItem(
+ R.string.action_decrease_width, R.drawable.ic_widget_width_decrease,
+ IGNORE,
+ v -> performResizeAction(R.string.action_decrease_width, host, info)));
}
}
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
- actions.add(R.string.action_increase_height);
+ actions.add(new OptionItem(
+ R.string.action_increase_height, R.drawable.ic_widget_height_increase,
+ IGNORE,
+ v -> performResizeAction(R.string.action_increase_height, host, info)));
}
if (info.spanY > info.minSpanY && info.spanY > 1) {
- actions.add(R.string.action_decrease_height);
+ actions.add(new OptionItem(
+ R.string.action_decrease_height, R.drawable.ic_widget_height_decrease,
+ IGNORE,
+ v -> performResizeAction(R.string.action_decrease_height, host, info)));
}
}
return actions;
}
- @Thunk void performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
+ private boolean performResizeAction(int action, View host, LauncherAppWidgetInfo info) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) host.getLayoutParams();
CellLayout layout = (CellLayout) host.getParent().getParent();
layout.markCellsAsUnoccupiedForView(host);
@@ -354,13 +357,12 @@
}
layout.markCellsAsOccupiedForView(host);
- Rect sizeRange = new Rect();
- AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, sizeRange);
- ((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
- sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
+ AppWidgetResizeFrame.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
+ info.spanX, info.spanY);
host.requestLayout();
mLauncher.getModelWriter().updateItemInDatabase(info);
announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
+ return true;
}
@Thunk void announceConfirmation(int resId) {
@@ -406,7 +408,11 @@
}
}
- public void beginAccessibleDrag(View item, ItemInfo info) {
+ private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
+ if (!itemSupportsAccessibleDrag(info)) {
+ return false;
+ }
+
mDragInfo = new DragInfo();
mDragInfo.info = info;
mDragInfo.item = item;
@@ -423,8 +429,17 @@
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
+ options.isKeyboardDrag = fromKeyboard;
options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
- ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+
+ if (fromKeyboard) {
+ KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
+ .inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
+ popup.showForIcon(item, info, options);
+ } else {
+ ItemLongClickListener.beginDrag(item, mLauncher, info, options);
+ }
+ return true;
}
@Override
@@ -475,19 +490,28 @@
return screenId;
}
- /**
- * An interface allowing views to handle their own action.
- */
- public interface AccessibilityActionHandler {
+ public class LauncherAction {
+ public final int keyCode;
+ public final AccessibilityAction accessibilityAction;
+
+ private final LauncherAccessibilityDelegate mDelegate;
+
+ public LauncherAction(int id, int labelRes, int keyCode) {
+ this.keyCode = keyCode;
+ accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes));
+ mDelegate = LauncherAccessibilityDelegate.this;
+ }
/**
- * performs accessibility action and returns true on success
+ * Invokes the action for the provided host
*/
- boolean performAccessibilityAction(int action, ItemInfo itemInfo);
-
- /**
- * adds all the accessibility actions that can be handled.
- */
- void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo);
+ public boolean invokeFromKeyboard(View host) {
+ if (host != null && host.getTag() instanceof ItemInfo) {
+ return mDelegate.performAction(
+ host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
+ } else {
+ return false;
+ }
+ }
}
}
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index d4ba11e..1733e5d 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -18,9 +18,8 @@
import static com.android.launcher3.LauncherState.NORMAL;
+import android.view.KeyEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
@@ -31,7 +30,8 @@
import com.android.launcher3.notification.NotificationMainView;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
/**
* Extension of {@link LauncherAccessibilityDelegate} with actions specific to shortcuts in
@@ -43,23 +43,23 @@
public ShortcutMenuAccessibilityDelegate(Launcher launcher) {
super(launcher);
- mActions.put(DISMISS_NOTIFICATION, new AccessibilityAction(DISMISS_NOTIFICATION,
- launcher.getText(R.string.action_dismiss_notification)));
+ mActions.put(DISMISS_NOTIFICATION, new LauncherAction(DISMISS_NOTIFICATION,
+ R.string.action_dismiss_notification, KeyEvent.KEYCODE_X));
}
@Override
- public void addSupportedActions(View host, AccessibilityNodeInfo info, boolean fromKeyboard) {
+ protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
if ((host.getParent() instanceof DeepShortcutView)) {
- info.addAction(mActions.get(ADD_TO_WORKSPACE));
+ out.add(mActions.get(ADD_TO_WORKSPACE));
} else if (host instanceof NotificationMainView) {
if (((NotificationMainView) host).canChildBeDismissed()) {
- info.addAction(mActions.get(DISMISS_NOTIFICATION));
+ out.add(mActions.get(DISMISS_NOTIFICATION));
}
}
}
@Override
- public boolean performAction(View host, ItemInfo item, int action) {
+ protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
if (action == ADD_TO_WORKSPACE) {
if (!(host.getParent() instanceof DeepShortcutView)) {
return false;
@@ -73,9 +73,7 @@
mLauncher.getModelWriter().addItemToDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
- ArrayList<ItemInfo> itemList = new ArrayList<>();
- itemList.add(info);
- mLauncher.bindItems(itemList, true);
+ mLauncher.bindItems(Collections.singletonList(info), true);
AbstractFloatingView.closeAllOpenViews(mLauncher);
announceConfirmation(R.string.item_added_to_workspace);
}
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 65a261d..a331924 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -17,17 +17,12 @@
package com.android.launcher3.accessibility;
import android.content.Context;
-import android.graphics.Rect;
import android.text.TextUtils;
import android.view.View;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -38,9 +33,6 @@
*/
public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
- private final Rect mTempRect = new Rect();
- private final int[] mTempCords = new int[2];
-
public WorkspaceAccessibilityHelper(CellLayout layout) {
super(layout);
}
@@ -134,26 +126,6 @@
}
return "";
}
-
- @Override
- protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
- super.onPopulateNodeForVirtualView(id, node);
-
-
- // ExploreByTouchHelper does not currently handle view scale.
- // Update BoundsInScreen to appropriate value.
- DragLayer dragLayer = Launcher.getLauncher(mView.getContext()).getDragLayer();
- mTempCords[0] = mTempCords[1] = 0;
- float scale = dragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
-
- node.getBoundsInParent(mTempRect);
- mTempRect.left = mTempCords[0] + (int) (mTempRect.left * scale);
- mTempRect.right = mTempCords[0] + (int) (mTempRect.right * scale);
- mTempRect.top = mTempCords[1] + (int) (mTempRect.top * scale);
- mTempRect.bottom = mTempCords[1] + (int) (mTempRect.bottom * scale);
- node.setBoundsInScreen(mTempRect);
- }
-
@Override
protected String getLocationDescriptionForIconDrop(int id) {
int x = id % mView.getCountX();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c989e7b..edd9a9f 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,7 +15,9 @@
*/
package com.android.launcher3.allapps;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+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.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;
@@ -32,6 +34,7 @@
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -42,6 +45,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.core.os.BuildCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -55,25 +59,25 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
-
-import java.util.ArrayList;
+import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
/**
* The all apps view container.
*/
public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
- Insettable, OnDeviceProfileChangeListener {
+ Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
private static final float FLING_VELOCITY_MULTIPLIER = 135f;
// Starts the springs after at least 55% of the animation has passed.
@@ -83,7 +87,7 @@
protected final BaseDraggingActivity mLauncher;
protected final AdapterHolder[] mAH;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
- private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
+ private final ItemInfoMatcher mWorkMatcher = mPersonalMatcher.negate();
private final AllAppsStore mAllAppsStore = new AllAppsStore();
private final Paint mNavBarScrimPaint;
@@ -107,7 +111,9 @@
private final MultiValueAlpha mMultiValueAlpha;
- Rect mInsets = new Rect();
+ private Rect mInsets = new Rect();
+
+ SearchAdapterProvider mSearchAdapterProvider;
public AllAppsContainerView(Context context) {
this(context, null);
@@ -123,6 +129,7 @@
mLauncher = BaseDraggingActivity.fromContext(context);
mLauncher.addOnDeviceProfileChangeListener(this);
+ mSearchAdapterProvider = mLauncher.createSearchAdapterProvider(this);
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
@@ -140,6 +147,7 @@
addSpringView(R.id.all_apps_tabs_view_pager);
mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
+
}
/**
@@ -191,7 +199,9 @@
break;
}
}
- rebindAdapters(hasWorkApps);
+ if (!mAH[AdapterHolder.MAIN].appsList.hasFilter()) {
+ rebindAdapters(hasWorkApps);
+ }
if (hasWorkApps) {
resetWorkProfile();
}
@@ -203,6 +213,21 @@
mAH[AdapterHolder.WORK].applyPadding();
}
+ private void hideInput() {
+ if (!BuildCompat.isAtLeastR() || !FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
+
+ WindowInsets insets = getRootWindowInsets();
+ if (insets == null) return;
+
+ if (insets.isVisible(WindowInsets.Type.ime())) {
+ hideIme();
+ }
+ }
+
+ protected void hideIme() {
+ getWindowInsetsController().hide(WindowInsets.Type.ime());
+ }
+
/**
* Returns whether the view itself will handle the touch event or not.
*/
@@ -218,9 +243,14 @@
}
if (rv.getScrollbar().getThumbOffsetY() >= 0 &&
mLauncher.getDragLayer().isEventOverView(rv.getScrollbar(), ev)) {
+ hideInput();
return false;
}
- return rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ boolean shouldScroll = rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ if (shouldScroll) {
+ hideInput();
+ }
+ return shouldScroll;
}
@Override
@@ -335,13 +365,6 @@
}
@Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- parents.add(newContainerTarget(
- getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
- }
-
- @Override
public void setInsets(Rect insets) {
mInsets.set(insets);
DeviceProfile grid = mLauncher.getDeviceProfile();
@@ -415,10 +438,20 @@
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
findViewById(R.id.tab_personal)
- .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
+ .setOnClickListener((View view) -> {
+ if (mViewPager.snapToPage(AdapterHolder.MAIN)) {
+ mLauncher.getStatsLogManager().logger()
+ .log(LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB);
+ }
+ });
findViewById(R.id.tab_work)
- .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
- onTabChanged(mViewPager.getNextPage());
+ .setOnClickListener((View view) -> {
+ if (mViewPager.snapToPage(AdapterHolder.WORK)) {
+ mLauncher.getStatsLogManager().logger()
+ .log(LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB);
+ }
+ });
+ onActivePageChanged(mViewPager.getNextPage());
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
@@ -464,10 +497,14 @@
int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
View newView = getLayoutInflater().inflate(layout, this, false);
addView(newView, index);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "should show tabs:" + showTabs,
+ new Exception());
+ }
if (showTabs) {
mViewPager = (AllAppsPagedView) newView;
mViewPager.initParentViews(this);
- mViewPager.getPageIndicator().setContainerView(this);
+ mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
} else {
mViewPager = null;
}
@@ -477,16 +514,17 @@
return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
}
- public void onTabChanged(int pos) {
- mHeader.setMainActive(pos == 0);
- if (mAH[pos].recyclerView != null) {
- mAH[pos].recyclerView.bindFastScrollbar();
+ @Override
+ public void onActivePageChanged(int currentActivePage) {
+ mHeader.setMainActive(currentActivePage == 0);
+ if (mAH[currentActivePage].recyclerView != null) {
+ mAH[currentActivePage].recyclerView.bindFastScrollbar();
}
reset(true /* animate */);
if (mWorkModeSwitch != null) {
- mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK
+ mWorkModeSwitch.setWorkTabVisible(currentActivePage == AdapterHolder.WORK
&& mAllAppsStore.hasModelFlag(
- FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
+ FLAG_HAS_SHORTCUT_PERMISSION | FLAG_QUIET_MODE_CHANGE_PERMISSION));
}
}
@@ -526,6 +564,46 @@
return mViewPager == null ? getActiveRecyclerView() : mViewPager;
}
+ /**
+ * Handles selection on focused view and returns success
+ */
+ public boolean selectFocusedView(View v) {
+ ItemInfo headerItem = getHighlightedItemFromHeader();
+ if (headerItem != null) {
+ return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
+ }
+ AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
+ if (focusedItem != null) {
+ View focusedView = getActiveRecyclerView().getLayoutManager()
+ .findViewByPosition(focusedItem.position);
+ if (focusedView != null && mSearchAdapterProvider.onAdapterItemSelected(focusedItem,
+ focusedView)) {
+ return true;
+ }
+ }
+ if (focusedItem != null && focusedItem.appInfo != null) {
+ ItemInfo itemInfo = focusedItem.appInfo;
+ return mLauncher.startActivitySafely(v, itemInfo.getIntent(), itemInfo);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the ItemInfo of a focused view inside {@link FloatingHeaderView}
+ */
+ public ItemInfo getHighlightedItemFromHeader() {
+ View view = getFloatingHeaderView().getFocusedChild();
+ if (view != null && view.getTag() instanceof ItemInfo) {
+ return ((ItemInfo) view.getTag());
+ }
+
+ return null;
+ }
+
+ public SearchAdapterProvider getSearchAdapterProvider() {
+ return mSearchAdapterProvider;
+ }
+
public RecyclerViewFastScroller getScrollBar() {
AllAppsRecyclerView rv = getActiveRecyclerView();
return rv == null ? null : rv.getScrollbar();
@@ -538,6 +616,10 @@
int padding = mHeader.getMaxTranslation();
for (int i = 0; i < mAH.length; i++) {
mAH[i].padding.top = padding;
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mUsingTabs) {
+ //add extra space between tabs and recycler view
+ mAH[i].padding.top += mLauncher.getDeviceProfile().edgeMarginPx;
+ }
mAH[i].applyPadding();
}
}
@@ -630,7 +712,8 @@
AdapterHolder(boolean isWork) {
mIsWork = isWork;
appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
- adapter = new AllAppsGridAdapter(mLauncher, getLayoutInflater(), appsList);
+ adapter = new AllAppsGridAdapter(mLauncher, getLayoutInflater(), appsList,
+ mSearchAdapterProvider);
appsList.setAdapter(adapter);
layoutManager = adapter.getLayoutManager();
}
@@ -652,6 +735,10 @@
applyVerticalFadingEdgeEnabled(verticalFadingEdge);
applyPadding();
setupOverlay();
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ recyclerView.addItemDecoration(new AllAppsSectionDecorator(
+ AllAppsContainerView.this));
+ }
}
void setupOverlay() {
@@ -690,6 +777,7 @@
int bottomOffset = mWorkModeSwitch != null && mIsWork ? switchH : 0;
recyclerView.setPadding(padding.left, padding.top, padding.right,
padding.bottom + bottomOffset);
+ recyclerView.scrollToTop();
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 8ec4d27..bb175ea 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -30,6 +30,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -40,8 +41,8 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.model.AppLaunchTracker;
+import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.allapps.search.SectionDecorationInfo;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper;
@@ -50,7 +51,8 @@
/**
* The grid view adapter of all the apps.
*/
-public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
+public class AllAppsGridAdapter extends
+ RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
public static final String TAG = "AppsGridAdapter";
@@ -71,6 +73,8 @@
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
+ private final SearchAdapterProvider mSearchAdapterProvider;
+
/**
* ViewHolder for each icon.
*/
@@ -82,6 +86,80 @@
}
/**
+ * Info about a particular adapter item (can be either section or app)
+ */
+ public static class AdapterItem {
+ /** Common properties */
+ // The index of this adapter item in the list
+ public int position;
+ // The type of this item
+ public int viewType;
+
+ /** App-only properties */
+ // The section name of this app. Note that there can be multiple items with different
+ // sectionNames in the same section
+ public String sectionName = null;
+ // The row that this item shows up on
+ public int rowIndex;
+ // The index of this app in the row
+ public int rowAppIndex;
+ // The associated AppInfo for the app
+ public AppInfo appInfo = null;
+ // The index of this app not including sections
+ public int appIndex = -1;
+ // Search section associated to result
+ public SectionDecorationInfo sectionDecorationInfo = null;
+
+ /**
+ * Factory method for AppIcon AdapterItem
+ */
+ public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
+ int appIndex) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_ICON;
+ item.position = pos;
+ item.sectionName = sectionName;
+ item.appInfo = appInfo;
+ item.appIndex = appIndex;
+ return item;
+ }
+
+ /**
+ * Factory method for empty search results view
+ */
+ public static AdapterItem asEmptySearch(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_EMPTY_SEARCH;
+ item.position = pos;
+ return item;
+ }
+
+ /**
+ * Factory method for a dividerView in AllAppsSearch
+ */
+ public static AdapterItem asAllAppsDivider(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_ALL_APPS_DIVIDER;
+ item.position = pos;
+ return item;
+ }
+
+ /**
+ * Factory method for a market search button
+ */
+ public static AdapterItem asMarketSearch(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = VIEW_TYPE_SEARCH_MARKET;
+ item.position = pos;
+ return item;
+ }
+
+ protected boolean isCountedForAccessibility() {
+ return viewType == VIEW_TYPE_ICON || viewType == VIEW_TYPE_SEARCH_MARKET;
+ }
+ }
+
+ /**
* A subclass of GridLayoutManager that overrides accessibility values during app search.
*/
public class AppsGridLayoutManager extends GridLayoutManager {
@@ -161,11 +239,15 @@
@Override
public int getSpanSize(int position) {
- if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
- return 1;
+ int viewType = mApps.getAdapterItems().get(position).viewType;
+ int totalSpans = mGridLayoutMgr.getSpanCount();
+ if (isIconViewType(viewType)) {
+ return totalSpans / mAppsPerRow;
+ } else if (mSearchAdapterProvider.isSearchView(viewType)) {
+ return totalSpans / mSearchAdapterProvider.getItemsPerRow(viewType, mAppsPerRow);
} else {
// Section breaks span the full width
- return mAppsPerRow;
+ return totalSpans;
}
}
}
@@ -189,7 +271,7 @@
private Intent mMarketSearchIntent;
public AllAppsGridAdapter(BaseDraggingActivity launcher, LayoutInflater inflater,
- AlphabeticalAppsList apps) {
+ AlphabeticalAppsList apps, SearchAdapterProvider searchAdapterProvider) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@@ -201,12 +283,19 @@
mOnIconClickListener = launcher.getItemOnClickListener();
+ mSearchAdapterProvider = searchAdapterProvider;
setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
}
public void setAppsPerRow(int appsPerRow) {
mAppsPerRow = appsPerRow;
- mGridLayoutMgr.setSpanCount(mAppsPerRow);
+ int totalSpans = mAppsPerRow;
+ for (int itemPerRow : mSearchAdapterProvider.getSupportedItemsPerRowArray()) {
+ if (totalSpans % itemPerRow != 0) {
+ totalSpans *= itemPerRow;
+ }
+ }
+ mGridLayoutMgr.setSpanCount(totalSpans);
}
/**
@@ -255,11 +344,10 @@
case VIEW_TYPE_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
- icon.setOnClickListener(mOnIconClickListener);
- icon.setOnLongClickListener(mOnIconLongClickListener);
icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener);
-
+ icon.setOnClickListener(mOnIconClickListener);
+ icon.setOnLongClickListener(mOnIconLongClickListener);
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
return new ViewHolder(icon);
@@ -270,12 +358,16 @@
View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
parent, false);
searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
- v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH));
+ v, mMarketSearchIntent, null));
return new ViewHolder(searchMarketView);
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
default:
+ if (mSearchAdapterProvider.isSearchView(viewType)) {
+ return mSearchAdapterProvider.onCreateViewHolder(mLayoutInflater, parent,
+ viewType);
+ }
throw new RuntimeException("Unexpected view type");
}
}
@@ -284,7 +376,8 @@
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
- AppInfo info = mApps.getAdapterItems().get(position).appInfo;
+ AdapterItem adapterItem = mApps.getAdapterItems().get(position);
+ AppInfo info = adapterItem.appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
icon.applyFromApplicationInfo(info);
@@ -306,10 +399,17 @@
case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
break;
+ default:
+ mSearchAdapterProvider.onBindView(holder, position);
}
}
@Override
+ public void onViewRecycled(@NonNull ViewHolder holder) {
+ super.onViewRecycled(holder);
+ }
+
+ @Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
@@ -322,8 +422,7 @@
@Override
public int getItemViewType(int position) {
- AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
+ AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
-
}
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
index f640c3e..14e3b51 100644
--- a/src/com/android/launcher3/allapps/AllAppsPagedView.java
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -15,19 +15,25 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SWIPE_TO_PERSONAL_TAB;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SWIPE_TO_WORK_TAB;
+
import android.content.Context;
import android.util.AttributeSet;
-import android.view.MotionEvent;
+import com.android.launcher3.Launcher;
import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
-public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
+/**
+ * A {@link PagedView} for showing different views for the personal and work profile respectively
+ * in the {@link AllAppsContainerView}.
+ */
+public class AllAppsPagedView extends PersonalWorkPagedView {
- final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
- final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
- final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
-
- public AllAppsPagedView(Context context) {
+ public AllAppsPagedView(Context context) {
this(context, null);
}
@@ -37,53 +43,21 @@
public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+ int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0
+ : context.getResources().getDimensionPixelOffset(
+ R.dimen.all_apps_header_top_padding);
+ setPadding(0, topPadding, 0, 0);
}
@Override
- protected String getCurrentPageDescription() {
- // Not necessary, tab-bar already has two tabs with their own descriptions.
- return "";
- }
-
- @Override
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- super.onScrollChanged(l, t, oldl, oldt);
- mPageIndicator.setScroll(l, mMaxScroll);
- }
-
- @Override
- protected void determineScrollingStart(MotionEvent ev) {
- float absDeltaX = Math.abs(ev.getX() - getDownMotionX());
- float absDeltaY = Math.abs(ev.getY() - getDownMotionY());
-
- if (Float.compare(absDeltaX, 0f) == 0) return;
-
- float slope = absDeltaY / absDeltaX;
- float theta = (float) Math.atan(slope);
-
- if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
- cancelCurrentPageLongPress();
+ protected boolean snapToPageWithVelocity(int whichPage, int velocity) {
+ boolean resp = super.snapToPageWithVelocity(whichPage, velocity);
+ if (resp && whichPage != mCurrentPage) {
+ Launcher.getLauncher(getContext()).getStatsLogManager().logger()
+ .log(mCurrentPage < whichPage
+ ? LAUNCHER_ALLAPPS_SWIPE_TO_WORK_TAB
+ : LAUNCHER_ALLAPPS_SWIPE_TO_PERSONAL_TAB);
}
-
- if (theta > MAX_SWIPE_ANGLE) {
- return;
- } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
- theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
- float extraRatio = (float)
- Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
- super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
- } else {
- super.determineScrollingStart(ev);
- }
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- @Override
- protected boolean canScroll(float absVScroll, float absHScroll) {
- return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
+ return resp;
}
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index cbf02b7..179cb77 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,13 +19,15 @@
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static android.view.View.MeasureSpec.makeMeasureSpec;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+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 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;
@@ -37,11 +39,7 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.views.RecyclerViewFastScroller;
import java.util.ArrayList;
@@ -50,7 +48,9 @@
/**
* A RecyclerView with custom fast scroll support for the all apps view.
*/
-public class AllAppsRecyclerView extends BaseRecyclerView implements LogContainerProvider {
+public class AllAppsRecyclerView extends BaseRecyclerView {
+ private static final String TAG = "AllAppsContainerView";
+ private static final boolean DEBUG = true;
private AlphabeticalAppsList mApps;
private final int mNumAppsPerRow;
@@ -112,23 +112,6 @@
mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
- /**
- * Scrolls this recycler view to the top.
- */
- public void scrollToTop() {
- // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
- if (mScrollbar != null) {
- mScrollbar.reattachThumbToScroll();
- }
- if (getLayoutManager() instanceof AppsGridLayoutManager) {
- AppsGridLayoutManager layoutManager = (AppsGridLayoutManager) getLayoutManager();
- if (layoutManager.findFirstCompletelyVisibleItemPosition() == 0) {
- // We are at the top, so don't scrollToPosition (would cause unnecessary relayout).
- return;
- }
- }
- scrollToPosition(0);
- }
@Override
public void onDraw(Canvas c) {
@@ -136,7 +119,9 @@
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.draw(c);
}
-
+ if (DEBUG) {
+ Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
+ }
super.onDraw(c);
}
@@ -175,13 +160,6 @@
mAutoSizedOverlays.clear();
}
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- parents.add(newContainerTarget(
- getApps().hasFilter() ? ContainerType.SEARCHRESULT : ContainerType.ALLAPPS));
- }
-
public void onSearchResultsChanged() {
// Always scroll the view to the top so the user can see the changed results
scrollToTop();
@@ -202,6 +180,23 @@
}
@Override
+ public void onScrollStateChanged(int state) {
+ super.onScrollStateChanged(state);
+
+ StatsLogManager mgr = BaseDraggingActivity.fromContext(getContext()).getStatsLogManager();
+ switch (state) {
+ case SCROLL_STATE_DRAGGING:
+ mgr.logger().sendToInteractionJankMonitor(
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN, this);
+ break;
+ case SCROLL_STATE_IDLE:
+ mgr.logger().sendToInteractionJankMonitor(
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END, this);
+ break;
+ }
+ }
+
+ @Override
public boolean onInterceptTouchEvent(MotionEvent e) {
boolean result = super.onInterceptTouchEvent(e);
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
@@ -277,7 +272,7 @@
if (mApps == null) {
return;
}
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
if (items.isEmpty() || mNumAppsPerRow == 0) {
@@ -351,7 +346,7 @@
@Override
public int getCurrentScrollY() {
// Return early if there are no items or we haven't been measured
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ List<AllAppsGridAdapter.AdapterItem> items = mApps.getAdapterItems();
if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
return -1;
}
@@ -367,14 +362,14 @@
}
public int getCurrentScrollY(int position, int offset) {
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- AlphabeticalAppsList.AdapterItem posItem = position < items.size() ?
- items.get(position) : null;
+ 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++) {
- AlphabeticalAppsList.AdapterItem item = items.get(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 &&
diff --git a/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
new file mode 100644
index 0000000..f4d735e
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsSectionDecorator.java
@@ -0,0 +1,187 @@
+/*
+ * 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.launcher3.allapps;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
+import com.android.launcher3.allapps.search.SectionDecorationInfo;
+import com.android.launcher3.util.Themes;
+
+import java.util.List;
+
+/**
+ * ItemDecoration class that groups items in {@link AllAppsRecyclerView}
+ */
+public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
+
+ private final AllAppsContainerView mAppsView;
+
+ AllAppsSectionDecorator(AllAppsContainerView appsContainerView) {
+ mAppsView = appsContainerView;
+ }
+
+ @Override
+ public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+ List<AllAppsGridAdapter.AdapterItem> adapterItems = mAppsView.getApps().getAdapterItems();
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ View view = parent.getChildAt(i);
+ int position = parent.getChildAdapterPosition(view);
+ AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
+ if (adapterItem.sectionDecorationInfo != null) {
+ SectionDecorationInfo sectionInfo = adapterItem.sectionDecorationInfo;
+ SectionDecorationHandler decorationHandler = sectionInfo.getDecorationHandler();
+ if (decorationHandler != null) {
+ decorationHandler.extendBounds(view);
+ if (sectionInfo.isFocusedView()) {
+ decorationHandler.onFocusDraw(c, view);
+ } else {
+ decorationHandler.onGroupDraw(c);
+ }
+ }
+ }
+ }
+ }
+
+ // Fallback logic in case non of the SearchTarget is labeled as focused item.
+ private void drawDecoration(@NonNull Canvas c,
+ @NonNull SectionDecorationHandler decorationHandler,
+ @NonNull RecyclerView parent) {
+ if (decorationHandler.mIsFullWidth) {
+ decorationHandler.mBounds.left = parent.getPaddingLeft();
+ decorationHandler.mBounds.right = parent.getWidth() - parent.getPaddingRight();
+ }
+ if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
+ && mAppsView.getApps().getFocusedChild() != null) {
+ int index = mAppsView.getApps().getFocusedChildIndex();
+ AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
+ mAppsView.getActiveRecyclerView().getLayoutManager();
+ if (layoutManager.findFirstVisibleItemPosition() <= index
+ && index < parent.getChildCount()) {
+ RecyclerView.ViewHolder vh = parent.findViewHolderForAdapterPosition(index);
+ if (vh != null) decorationHandler.onFocusDraw(c, vh.itemView);
+ }
+ }
+ decorationHandler.reset();
+ }
+
+ /**
+ * Handles grouping and drawing of items in the same all apps sections.
+ */
+ public static class SectionDecorationHandler {
+ protected RectF mBounds = new RectF();
+ private final boolean mIsFullWidth;
+ private final float mRadius;
+
+ protected final int mFocusColor; // main focused item color
+ protected final int mFillcolor; // grouping color
+
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final boolean mIsTopRound;
+ private final boolean mIsBottomRound;
+ private float [] mCorners;
+ private float mFillSpacing;
+
+ public SectionDecorationHandler(Context context, boolean isFullWidth, int fillAlpha,
+ boolean isTopRound, boolean isBottomRound) {
+
+ mIsFullWidth = isFullWidth;
+ int endScrim = Themes.getColorBackground(context);
+ mFillcolor = ColorUtils.setAlphaComponent(endScrim, fillAlpha);
+ mFocusColor = endScrim;
+
+ mIsTopRound = isTopRound;
+ mIsBottomRound = isBottomRound;
+
+ mRadius = context.getResources().getDimensionPixelSize(
+ R.dimen.search_decoration_corner_radius);
+ mFillSpacing = context.getResources().getDimensionPixelSize(
+ R.dimen.search_decoration_padding);
+ mCorners = new float[]{
+ mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top left radius in px
+ mIsTopRound ? mRadius : 0, mIsTopRound ? mRadius : 0, // Top right radius in px
+ mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0, // Bottom right
+ mIsBottomRound ? mRadius : 0, mIsBottomRound ? mRadius : 0 // Bottom left
+ };
+
+ }
+
+ /**
+ * Extends current bounds to include the view.
+ */
+ public void extendBounds(View view) {
+ if (mBounds.isEmpty()) {
+ mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ } else {
+ mBounds.set(
+ Math.min(mBounds.left, view.getLeft()),
+ Math.min(mBounds.top, view.getTop()),
+ Math.max(mBounds.right, view.getRight()),
+ Math.max(mBounds.bottom, view.getBottom())
+ );
+ }
+ }
+
+ /**
+ * Draw bounds onto canvas.
+ */
+ public void onGroupDraw(Canvas canvas) {
+ mPaint.setColor(mFillcolor);
+ onDraw(canvas);
+ }
+
+ /**
+ * Draw the bound of the view to the canvas.
+ */
+ public void onFocusDraw(Canvas canvas, @Nullable View view) {
+ if (view == null) {
+ return;
+ }
+ mPaint.setColor(mFocusColor);
+ mBounds.set(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
+ onDraw(canvas);
+ }
+
+
+ private void onDraw(Canvas canvas) {
+ final Path path = new Path();
+ RectF finalBounds = new RectF(mBounds.left + mFillSpacing,
+ mBounds.top + mFillSpacing,
+ mBounds.right - mFillSpacing,
+ mBounds.bottom - mFillSpacing);
+ path.addRoundRect(finalBounds, mCorners, Path.Direction.CW);
+ canvas.drawPath(path, mPaint);
+ }
+
+ /**
+ * Reset view bounds to empty.
+ */
+ public void reset() {
+ mBounds.setEmpty();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 3ae0a18..355ccad 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -21,10 +21,11 @@
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
@@ -79,6 +80,11 @@
return (mModelFlags & mask) != 0;
}
+ /**
+ * Returns {@link AppInfo} if any apps matches with provided {@link ComponentKey}, otherwise
+ * null.
+ */
+ @Nullable
public AppInfo getApp(ComponentKey key) {
mTempInfo.componentName = key.componentName;
mTempInfo.user = key.user;
@@ -145,10 +151,17 @@
});
}
- public void updatePromiseAppProgress(PromiseAppInfo app) {
+ /**
+ * Sets the AppInfo's associated icon's progress bar.
+ *
+ * If this app is installed and supports incremental downloads, the progress bar will be updated
+ * the app's total download progress. Otherwise, the progress bar will be updated to the app's
+ * installation progress.
+ */
+ public void updateProgressBar(AppInfo app) {
updateAllIcons((child) -> {
if (child.getTag() == app) {
- child.applyProgressLevel(app.level);
+ child.applyProgressLevel();
}
});
}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a9b030e..abf63dc 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,10 +1,24 @@
+/*
+ * 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 static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.VERTICAL_SWIPE_INDICATOR;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
import static com.android.launcher3.anim.Interpolators.INSTANT;
@@ -14,31 +28,27 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
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.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.content.Context;
import android.util.FloatProperty;
import android.view.View;
-import android.view.ViewGroup;
import android.view.animation.Interpolator;
-import android.widget.EditText;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
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.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.views.ScrimView;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
-import com.android.systemui.plugins.PluginListener;
/**
* Handles AllApps view transition.
@@ -50,22 +60,22 @@
* If release velocity < THRES1, snap according to either top or bottom depending on whether it's
* closer to top or closer to the page indicator.
*/
-public class AllAppsTransitionController implements StateHandler<LauncherState>,
- OnDeviceProfileChangeListener, PluginListener<AllAppsSearchPlugin> {
+public class AllAppsTransitionController
+ implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
- @Override
- public Float get(AllAppsTransitionController controller) {
- return controller.mProgress;
- }
+ @Override
+ public Float get(AllAppsTransitionController controller) {
+ return controller.mProgress;
+ }
- @Override
- public void setValue(AllAppsTransitionController controller, float progress) {
- controller.setProgress(progress);
- }
- };
+ @Override
+ public void setValue(AllAppsTransitionController controller, float progress) {
+ controller.setProgress(progress);
+ }
+ };
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
@@ -86,10 +96,6 @@
private float mScrollRangeDelta = 0;
- // plugin related variables
- private AllAppsSearchPlugin mPlugin;
- private View mPluginContent;
-
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
mShiftRange = mLauncher.getDeviceProfile().heightPx;
@@ -120,19 +126,14 @@
* in xml-based animations which also handle updating the appropriate UI.
*
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
- *
* @see #setState(LauncherState)
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
*/
public void setProgress(float progress) {
mProgress = progress;
- mScrimView.setProgress(progress);
- float shiftCurrent = progress * mShiftRange;
- mAppsView.setTranslationY(shiftCurrent);
- if (mPlugin != null) {
- mPlugin.setProgress(progress);
- }
+ mScrimView.setProgress(progress);
+ mAppsView.setTranslationY(progress * mShiftRange);
}
public float getProgress() {
@@ -201,20 +202,13 @@
Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
- if (mPlugin == null) {
- setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
- mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
- hasAllAppsContent, setter, headerFade, allAppsFade);
- } else {
- setter.setViewAlpha(mPluginContent, hasAllAppsContent ? 1 : 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getContentView(), 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getScrollBar(), 0, allAppsFade);
- }
- mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
- setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
- (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
+ setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
+ setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
+ mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
+ hasAllAppsContent, setter, headerFade, allAppsFade);
+
+ mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
// Set visibility of the container at the very beginning or end of the transition.
setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
@@ -228,8 +222,11 @@
public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) {
mAppsView = appsView;
mScrimView = scrimView;
- PluginManagerWrapper.INSTANCE.get(mLauncher)
- .addPluginListener(this, AllAppsSearchPlugin.class, false);
+ 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);
+ }
}
/**
@@ -252,47 +249,5 @@
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
}
- updatePluginAnimationEnd();
- }
-
- @Override
- public void onPluginConnected(AllAppsSearchPlugin plugin, Context context) {
- mPlugin = plugin;
- mPluginContent = mLauncher.getLayoutInflater().inflate(
- R.layout.all_apps_content_layout, mAppsView, false);
- mAppsView.addView(mPluginContent);
- mPluginContent.setAlpha(0f);
- mPlugin.setup((ViewGroup) mPluginContent, mLauncher, mShiftRange);
- }
-
- @Override
- public void onPluginDisconnected(AllAppsSearchPlugin plugin) {
- mPlugin = null;
- mAppsView.removeView(mPluginContent);
- }
-
- public void onActivityDestroyed() {
- PluginManagerWrapper.INSTANCE.get(mLauncher).removePluginListener(this);
- }
-
- /** Used for the plugin to signal when drag starts happens
- * @param toAllApps*/
- public void onDragStart(boolean toAllApps) {
- if (mPlugin == null) return;
-
- if (toAllApps) {
- EditText editText = mAppsView.getSearchUiManager().setTextSearchEnabled(true);
- mPlugin.setEditText(editText);
- }
- mPlugin.onDragStart(toAllApps ? 1f : 0f);
- }
-
- private void updatePluginAnimationEnd() {
- if (mPlugin == null) return;
- mPlugin.onAnimationEnd(mProgress);
- if (Float.compare(mProgress, 1f) == 0) {
- mAppsView.getSearchUiManager().setTextSearchEnabled(false);
- mPlugin.setEditText(null);
- }
}
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 06209bb..fefd97a 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -19,8 +19,10 @@
import android.content.Context;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.allapps.search.SectionDecorationInfo;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
@@ -60,61 +62,6 @@
}
}
- /**
- * Info about a particular adapter item (can be either section or app)
- */
- public static class AdapterItem {
- /** Common properties */
- // The index of this adapter item in the list
- public int position;
- // The type of this item
- public int viewType;
-
- /** App-only properties */
- // The section name of this app. Note that there can be multiple items with different
- // sectionNames in the same section
- public String sectionName = null;
- // The row that this item shows up on
- public int rowIndex;
- // The index of this app in the row
- public int rowAppIndex;
- // The associated AppInfo for the app
- public AppInfo appInfo = null;
- // The index of this app not including sections
- public int appIndex = -1;
-
- public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
- int appIndex) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_ICON;
- item.position = pos;
- item.sectionName = sectionName;
- item.appInfo = appInfo;
- item.appIndex = appIndex;
- return item;
- }
-
- public static AdapterItem asEmptySearch(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
- item.position = pos;
- return item;
- }
-
- public static AdapterItem asAllAppsDivider(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
- item.position = pos;
- return item;
- }
-
- public static AdapterItem asMarketSearch(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET;
- item.position = pos;
- return item;
- }
- }
private final BaseDraggingActivity mLauncher;
@@ -122,8 +69,8 @@
private final List<AppInfo> mApps = new ArrayList<>();
private final AllAppsStore mAllAppsStore;
- // The set of filtered apps with the current filter
- private final List<AppInfo> mFilteredApps = new ArrayList<>();
+ // The number of results in current adapter
+ private int mAccessibilityResultsCount = 0;
// The current set of adapter items
private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
// The set of sections that we allow fast-scrolling to (includes non-merged sections)
@@ -132,7 +79,7 @@
private final boolean mIsWork;
// The of ordered component names as a result of a search query
- private ArrayList<ComponentKey> mSearchResults;
+ private ArrayList<AdapterItem> mSearchResults;
private AllAppsGridAdapter mAdapter;
private AppInfoComparator mAppNameComparator;
private final int mNumAppsPerRow;
@@ -182,6 +129,28 @@
}
/**
+ * Returns the child adapter item with IME launch focus.
+ */
+ public AdapterItem getFocusedChild() {
+ if (mAdapterItems.size() == 0 || getFocusedChildIndex() == -1) {
+ return null;
+ }
+ return mAdapterItems.get(getFocusedChildIndex());
+ }
+
+ /**
+ * Returns the index of the child with IME launch focus.
+ */
+ public int getFocusedChildIndex() {
+ for (AdapterItem item : mAdapterItems) {
+ if (item.isCountedForAccessibility()) {
+ return mAdapterItems.indexOf(item);
+ }
+ }
+ return -1;
+ }
+
+ /**
* Returns the number of rows of applications
*/
public int getNumAppRows() {
@@ -192,7 +161,7 @@
* Returns the number of applications in this list.
*/
public int getNumFilteredApps() {
- return mFilteredApps.size();
+ return mAccessibilityResultsCount;
}
/**
@@ -206,22 +175,43 @@
* Returns whether there are no filtered results.
*/
public boolean hasNoFilteredResults() {
- return (mSearchResults != null) && mFilteredApps.isEmpty();
+ return (mSearchResults != null) && mAccessibilityResultsCount == 0;
}
/**
- * Sets the sorted list of filtered components.
+ * Sets results list for search
*/
- public boolean setOrderedFilter(ArrayList<ComponentKey> f) {
- if (mSearchResults != f) {
- boolean same = mSearchResults != null && mSearchResults.equals(f);
- mSearchResults = f;
- onAppsUpdated();
+ public boolean setSearchResults(ArrayList<AdapterItem> results) {
+ if (results == null || mSearchResults != results) {
+ boolean same = mSearchResults != null && mSearchResults.equals(results);
+ mSearchResults = results;
+ updateAdapterItems();
return !same;
}
return false;
}
+ public boolean appendSearchResults(ArrayList<AdapterItem> results) {
+ if (mSearchResults != null && results != null && results.size() > 0) {
+ updateSearchAdapterItems(results, mSearchResults.size());
+ refreshRecyclerView();
+ return true;
+ }
+ return false;
+ }
+
+ void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
+ for (int i = 0; i < list.size(); i++) {
+ AdapterItem adapterItem = list.get(i);
+ adapterItem.position = offset + i;
+ mAdapterItems.add(adapterItem);
+
+ if (adapterItem.isCountedForAccessibility()) {
+ mAccessibilityResultsCount++;
+ }
+ }
+ }
+
/**
* Updates internals when the set of apps are updated.
*/
@@ -267,11 +257,13 @@
}
// Recompose the set of adapter items from the current set of apps
- updateAdapterItems();
+ if (mSearchResults == null) {
+ updateAdapterItems();
+ }
}
/**
- * Updates the set of filtered apps with the current filter. At this point, we expect
+ * Updates the set of filtered apps with the current filter. At this point, we expect
* mCachedSectionNames to have been calculated for the set of all apps in mApps.
*/
private void updateAdapterItems() {
@@ -292,41 +284,53 @@
int appIndex = 0;
// Prepare to update the list of sections, filtered apps, etc.
- mFilteredApps.clear();
+ mAccessibilityResultsCount = 0;
mFastScrollerSections.clear();
mAdapterItems.clear();
+ SectionDecorationInfo appSection = new SectionDecorationInfo();
+ appSection.setDecorationHandler(
+ new AllAppsSectionDecorator.SectionDecorationHandler(mLauncher, true,
+ 0, false, false));
+
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
- for (AppInfo info : getFiltersAppInfos()) {
- String sectionName = info.sectionName;
- // Create a new section if the section names do not match
- if (!sectionName.equals(lastSectionName)) {
- lastSectionName = sectionName;
- lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
- mFastScrollerSections.add(lastFastScrollerSectionInfo);
- }
+ if (!hasFilter()) {
+ mAccessibilityResultsCount = mApps.size();
+ for (AppInfo info : mApps) {
+ String sectionName = info.sectionName;
- // Create an app item
- AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
- if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
- lastFastScrollerSectionInfo.fastScrollToItem = appItem;
+ // Create a new section if the section names do not match
+ if (!sectionName.equals(lastSectionName)) {
+ lastSectionName = sectionName;
+ lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
+ mFastScrollerSections.add(lastFastScrollerSectionInfo);
+ }
+
+ // Create an app item
+ AdapterItem appItem = AdapterItem.asApp(position++, sectionName, info, appIndex++);
+ if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
+ lastFastScrollerSectionInfo.fastScrollToItem = appItem;
+ }
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ appItem.sectionDecorationInfo = appSection;
+ }
+ mAdapterItems.add(appItem);
}
- mAdapterItems.add(appItem);
- mFilteredApps.add(info);
+ } else {
+ updateSearchAdapterItems(mSearchResults, 0);
+ if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ // Append the search market item
+ if (hasNoFilteredResults()) {
+ mAdapterItems.add(AdapterItem.asEmptySearch(position++));
+ } else {
+ mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
+ }
+ mAdapterItems.add(AdapterItem.asMarketSearch(position++));
+
+ }
}
-
- if (hasFilter()) {
- // Append the search market item
- if (hasNoFilteredResults()) {
- mAdapterItems.add(AdapterItem.asEmptySearch(position++));
- } else {
- mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
- }
- mAdapterItems.add(AdapterItem.asMarketSearch(position++));
- }
-
if (mNumAppsPerRow != 0) {
// Update the number of rows in the adapter after we do all the merging (otherwise, we
// would have to shift the values again)
@@ -381,18 +385,4 @@
}
}
}
-
- private List<AppInfo> getFiltersAppInfos() {
- if (mSearchResults == null) {
- return mApps;
- }
- ArrayList<AppInfo> result = new ArrayList<>();
- for (ComponentKey key : mSearchResults) {
- AppInfo match = mAllAppsStore.getApp(key);
- if (match != null) {
- result.add(match);
- }
- }
- return result;
- }
}
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index b4ff5ea..d8ef18e 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -17,9 +17,6 @@
package com.android.launcher3.allapps;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.HOTSEAT;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.PREDICTION;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -34,7 +31,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.OnboardingPrefs;
/**
@@ -116,19 +112,14 @@
}
@Override
- public void logActionCommand(int command) {
- // Since this is on-boarding popup, it is not a user controlled action.
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_DISCOVERY_BOUNCE) != 0;
}
- private void show(int containerType) {
+ private void show() {
mIsOpen = true;
mLauncher.getDragLayer().addView(this);
- mLauncher.getUserEventDispatcher().logActionBounceTip(containerType);
+ // TODO: add WW log for discovery bounce tip show event.
}
public static void showForHomeIfNeeded(Launcher launcher) {
@@ -151,40 +142,7 @@
}
onboardingPrefs.incrementEventCount(OnboardingPrefs.HOME_BOUNCE_COUNT);
- new DiscoveryBounce(launcher, 0).show(HOTSEAT);
- }
-
- public static void showForOverviewIfNeeded(Launcher launcher,
- PagedOrientationHandler orientationHandler) {
- showForOverviewIfNeeded(launcher, true, orientationHandler);
- }
-
- private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay,
- PagedOrientationHandler orientationHandler) {
- OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
- if (!launcher.isInState(OVERVIEW)
- || !launcher.hasBeenResumed()
- || launcher.isForceInvisible()
- || launcher.getDeviceProfile().isVerticalBarLayout()
- || !orientationHandler.isLayoutNaturalToLauncher()
- || onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
- || launcher.getSystemService(UserManager.class).isDemoUser()
- || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
- return;
- }
-
- if (withDelay) {
- new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false,
- orientationHandler), DELAY_MS);
- return;
- } else if (AbstractFloatingView.getTopOpenView(launcher) != null) {
- // TODO: Move these checks to the top and call this method after invalidate handler.
- return;
- }
- onboardingPrefs.incrementEventCount(OnboardingPrefs.SHELF_BOUNCE_COUNT);
-
- new DiscoveryBounce(launcher, (1 - OVERVIEW.getVerticalProgress(launcher)))
- .show(PREDICTION);
+ new DiscoveryBounce(launcher, 0).show();
}
/**
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index f899587..31c6cc7 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -16,6 +16,7 @@
package com.android.launcher3.allapps;
import android.graphics.Rect;
+import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.DeviceProfile;
@@ -55,4 +56,16 @@
void setVerticalScroll(int scroll, boolean isScrolledOut);
Class<? extends FloatingHeaderRow> getTypeClass();
+
+ /**
+ * Returns a child that has focus to be launched by the IME.
+ */
+ View getFocusedChild();
+
+ /**
+ * Returns true if view is currently visible
+ */
+ default boolean isVisible() {
+ return shouldDraw();
+ }
}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 81e1b94..9056e8a 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -38,6 +38,7 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.R;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.systemui.plugins.AllAppsRow;
import com.android.systemui.plugins.AllAppsRow.OnHeightUpdatedListener;
@@ -110,8 +111,8 @@
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
- mHeaderTopPadding = context.getResources()
- .getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
+ mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 :
+ context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
}
@Override
@@ -129,6 +130,7 @@
}
}
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
+ setPadding(0, mHeaderTopPadding, 0, 0);
mAllRows = mFixedRows;
}
@@ -194,6 +196,19 @@
onHeightUpdated();
}
+ @Override
+ public View getFocusedChild() {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ for (FloatingHeaderRow row : mAllRows) {
+ if (row.hasVisibleContent() && row.isVisible()) {
+ return row.getFocusedChild();
+ }
+ }
+ return null;
+ }
+ return super.getFocusedChild();
+ }
+
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
for (FloatingHeaderRow row : mAllRows) {
row.setup(this, mAllRows, tabsHidden);
@@ -233,7 +248,9 @@
public int getMaxTranslation() {
if (mMaxTranslation == 0 && mTabsHidden) {
- return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
+ int paddingOffset = getResources().getDimensionPixelSize(
+ R.dimen.all_apps_search_bar_bottom_padding);
+ return FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : paddingOffset;
} else if (mMaxTranslation > 0 && mTabsHidden) {
return mMaxTranslation + getPaddingTop();
} else {
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index a6bc6cf..13ddc12 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
+
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -83,14 +85,20 @@
}
@Override
- public void onTabChanged(int pos) {
- super.onTabChanged(pos);
+ public void onActivePageChanged(int currentActivePage) {
+ super.onActivePageChanged(currentActivePage);
if (mUsingTabs) {
- if (pos == AdapterHolder.WORK) {
+ if (currentActivePage == AdapterHolder.WORK) {
WorkEduView.showWorkEduIfNeeded(mLauncher);
} else {
mWorkTabListener = WorkEduView.showEduFlowIfNeeded(mLauncher, mWorkTabListener);
}
}
}
+
+ @Override
+ protected void hideIme() {
+ super.hideIme();
+ mLauncher.getStatsLogManager().logger().log(LAUNCHER_ALLAPPS_KEYBOARD_CLOSED);
+ }
}
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index 3089b18..cf7142c 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -83,4 +83,9 @@
public Class<PluginHeaderRow> getTypeClass() {
return PluginHeaderRow.class;
}
+
+ @Override
+ public View getFocusedChild() {
+ return null;
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 7d5363f..0d42950 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -20,10 +20,10 @@
import android.graphics.Rect;
import android.view.KeyEvent;
import android.view.animation.Interpolator;
-import android.widget.EditText;
import androidx.annotation.Nullable;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.anim.PropertySetter;
/**
@@ -59,6 +59,12 @@
Interpolator interpolator);
/**
+ * Called when activity is destroyed. Used to close search system services
+ */
+ default void destroy() {
+ }
+
+ /**
* Returns true if the QSB should be visible for the given set of visible elements
*/
default boolean isQsbVisible(int visibleElements) {
@@ -66,12 +72,13 @@
}
/**
- * Called to control how the search UI result should be handled.
- *
- * @param isEnabled when {@code true}, the search is all handled inside AOSP
- * and is not overlayable.
- * @return the searchbox edit text object
+ * @return the edit text object
*/
@Nullable
- EditText setTextSearchEnabled(boolean isEnabled);
+ ExtendedEditText getEditText();
+
+ /**
+ * sets highlight result's title
+ */
+ default void setFocusedResultTitle(@Nullable CharSequence title) { }
}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index ed45749..3319018 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps.search;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME;
+
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -27,13 +29,14 @@
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.model.AppLaunchTracker;
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
import com.android.launcher3.util.PackageManagerHelper;
-import java.util.ArrayList;
-
/**
* An interface to a search box that AllApps can command.
*/
@@ -42,22 +45,23 @@
OnFocusChangeListener {
protected BaseDraggingActivity mLauncher;
- protected Callbacks mCb;
+ protected SearchCallback<AdapterItem> mCallback;
protected ExtendedEditText mInput;
protected String mQuery;
- protected SearchAlgorithm mSearchAlgorithm;
+ protected SearchAlgorithm<AdapterItem> mSearchAlgorithm;
public void setVisibility(int visibility) {
mInput.setVisibility(visibility);
}
+
/**
* Sets the references to the apps model and the search result callback.
*/
public final void initialize(
- SearchAlgorithm searchAlgorithm, ExtendedEditText input,
- BaseDraggingActivity launcher, Callbacks cb) {
- mCb = cb;
+ SearchAlgorithm<AdapterItem> searchAlgorithm, ExtendedEditText input,
+ BaseDraggingActivity launcher, SearchCallback<AdapterItem> callback) {
+ mCallback = callback;
mLauncher = launcher;
mInput = input;
@@ -69,7 +73,7 @@
}
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
// Do nothing
}
@@ -83,10 +87,10 @@
mQuery = s.toString();
if (mQuery.isEmpty()) {
mSearchAlgorithm.cancel(true);
- mCb.clearSearchResult();
+ mCallback.clearSearchResult();
} else {
mSearchAlgorithm.cancel(false);
- mSearchAlgorithm.doSearch(mQuery, mCb);
+ mSearchAlgorithm.doSearch(mQuery, mCallback);
}
}
@@ -96,11 +100,22 @@
}
// If play store continues auto updating an app, we want to show partial result.
mSearchAlgorithm.cancel(false);
- mSearchAlgorithm.doSearch(mQuery, mCb);
+ mSearchAlgorithm.doSearch(mQuery, mCallback);
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
+ mLauncher.getStatsLogManager().logger()
+ .log(LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME);
+ // selectFocusedView should return SearchTargetEvent that is passed onto onClick
+ if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
+ return true;
+ }
+ }
+ }
+
// Skip if it's not the right action
if (actionId != EditorInfo.IME_ACTION_SEARCH) {
return false;
@@ -112,8 +127,8 @@
return false;
}
return mLauncher.startActivitySafely(v,
- PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null,
- AppLaunchTracker.CONTAINER_SEARCH);
+ PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null
+ );
}
@Override
@@ -138,7 +153,7 @@
* Resets the search bar state.
*/
public void reset() {
- mCb.clearSearchResult();
+ mCallback.clearSearchResult();
mInput.reset();
mQuery = null;
}
@@ -156,23 +171,4 @@
public boolean isSearchFieldFocused() {
return mInput.isFocused();
}
-
- /**
- * Callback for getting search results.
- */
- public interface Callbacks {
-
- /**
- * Called when the search is complete.
- *
- * @param apps sorted list of matching components or null if in case of failure.
- */
- void onSearchResult(String query, ArrayList<ComponentKey> apps);
-
- /**
- * Called when the search results should be cleared.
- */
- void clearSearchResult();
- }
-
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 356c52c..2261d51 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -32,19 +32,21 @@
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
import android.view.animation.Interpolator;
-import android.widget.EditText;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.anim.PropertySetter;
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.search.SearchCallback;
import java.util.ArrayList;
@@ -52,7 +54,7 @@
* Layout to contain the All-apps search UI.
*/
public class AppsSearchContainerLayout extends ExtendedEditText
- implements SearchUiManager, AllAppsSearchBarController.Callbacks,
+ implements SearchUiManager, SearchCallback<AdapterItem>,
AllAppsStore.OnUpdateListener, Insettable {
private final BaseDraggingActivity mLauncher;
@@ -107,7 +109,8 @@
int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
- mAppsView.getActiveRecyclerView().getPaddingRight();
- int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons);
+ int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.cellLayoutBorderSpacingPx,
+ dp.inv.numHotseatIcons);
int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
int iconPadding = cellWidth - iconVisibleSize;
@@ -135,7 +138,8 @@
mApps = appsView.getApps();
mAppsView = appsView;
mSearchBarController.initialize(
- new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this);
+ new DefaultAppSearchAlgorithm(mLauncher, LauncherAppState.getInstance(mLauncher)),
+ this, mLauncher, this);
}
@Override
@@ -168,17 +172,25 @@
}
@Override
- public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
- if (apps != null) {
- mApps.setOrderedFilter(apps);
+ public void onSearchResult(String query, ArrayList<AdapterItem> items) {
+ if (items != null) {
+ mApps.setSearchResults(items);
notifyResultChanged();
mAppsView.setLastSearchQuery(query);
}
}
@Override
+ public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
+ if (items != null) {
+ mApps.appendSearchResults(items);
+ notifyResultChanged();
+ }
+ }
+
+ @Override
public void clearSearchResult() {
- if (mApps.setOrderedFilter(null)) {
+ if (mApps.setSearchResults(null)) {
notifyResultChanged();
}
@@ -202,7 +214,8 @@
@Override
public float getScrollRangeDelta(Rect insets) {
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()
+ || FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
return 0;
} else {
return insets.bottom + insets.top;
@@ -216,7 +229,7 @@
}
@Override
- public EditText setTextSearchEnabled(boolean isEnabled) {
+ public ExtendedEditText getEditText() {
return this;
}
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
new file mode 100644
index 0000000..f9fb22e
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/AppsSearchPipeline.java
@@ -0,0 +1,90 @@
+/*
+ * 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.launcher3.allapps.search;
+
+import android.content.Context;
+import android.os.CancellationSignal;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
+import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.data.AppInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A device search section for handling app searches
+ */
+public class AppsSearchPipeline implements SearchPipeline {
+
+ private static final int MAX_RESULTS_COUNT = 5;
+
+ private final SectionDecorationInfo mSearchSectionInfo;
+ private final LauncherAppState mLauncherAppState;
+
+ public AppsSearchPipeline(Context context, LauncherAppState launcherAppState) {
+ mLauncherAppState = launcherAppState;
+ mSearchSectionInfo = new SectionDecorationInfo();
+ mSearchSectionInfo.setDecorationHandler(
+ new SectionDecorationHandler(context, true, 0, true, true));
+ }
+
+ @Override
+ public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
+ CancellationSignal cancellationSignal) {
+ mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
+ callback.accept(getAdapterItems(matchingResults));
+ }
+ });
+ }
+
+ /**
+ * Filters {@link AppInfo}s matching specified query
+ */
+ public static ArrayList<AppInfo> getTitleMatchResult(List<AppInfo> apps, String query) {
+ // Do an intersection of the words in the query and each title, and filter out all the
+ // apps that don't match all of the words in the query.
+ final String queryTextLower = query.toLowerCase();
+ final ArrayList<AppInfo> result = new ArrayList<>();
+ DefaultAppSearchAlgorithm.StringMatcher matcher =
+ DefaultAppSearchAlgorithm.StringMatcher.getInstance();
+ for (AppInfo info : apps) {
+ if (DefaultAppSearchAlgorithm.matches(info, queryTextLower, matcher)) {
+ result.add(info);
+ }
+ }
+ return result;
+ }
+
+ private ArrayList<AdapterItem> getAdapterItems(List<AppInfo> matchingApps) {
+ ArrayList<AdapterItem> items = new ArrayList<>();
+ for (int i = 0; i < matchingApps.size() && i < MAX_RESULTS_COUNT; i++) {
+ AdapterItem appItem = AdapterItem.asApp(i, "", matchingApps.get(i), i);
+ appItem.sectionDecorationInfo = mSearchSectionInfo;
+ items.add(appItem);
+ }
+
+ return items;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index bcb5427..4e213b0 100644
--- a/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -15,26 +15,28 @@
*/
package com.android.launcher3.allapps.search;
+import android.content.Context;
import android.os.Handler;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
import java.text.Collator;
-import java.util.ArrayList;
-import java.util.List;
/**
* The default search implementation.
*/
-public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
+public class DefaultAppSearchAlgorithm implements SearchAlgorithm<AdapterItem> {
- private final List<AppInfo> mApps;
protected final Handler mResultHandler;
+ private final AppsSearchPipeline mAppsSearchPipeline;
- public DefaultAppSearchAlgorithm(List<AppInfo> apps) {
- mApps = apps;
+ public DefaultAppSearchAlgorithm(Context context, LauncherAppState launcherAppState) {
mResultHandler = new Handler();
+ mAppsSearchPipeline = new AppsSearchPipeline(context, launcherAppState);
}
@Override
@@ -46,29 +48,11 @@
@Override
public void doSearch(final String query,
- final AllAppsSearchBarController.Callbacks callback) {
- final ArrayList<ComponentKey> result = getTitleMatchResult(query);
- mResultHandler.post(new Runnable() {
-
- @Override
- public void run() {
- callback.onSearchResult(query, result);
- }
- });
- }
-
- private ArrayList<ComponentKey> getTitleMatchResult(String query) {
- // Do an intersection of the words in the query and each title, and filter out all the
- // apps that don't match all of the words in the query.
- final String queryTextLower = query.toLowerCase();
- final ArrayList<ComponentKey> result = new ArrayList<>();
- StringMatcher matcher = StringMatcher.getInstance();
- for (AppInfo info : mApps) {
- if (matches(info, queryTextLower, matcher)) {
- result.add(info.toComponentKey());
- }
- }
- return result;
+ final SearchCallback<AdapterItem> callback) {
+ mAppsSearchPipeline.query(query,
+ results -> mResultHandler.post(
+ () -> callback.onSearchResult(query, results)),
+ null);
}
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
new file mode 100644
index 0000000..330ec68
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -0,0 +1,54 @@
+/*
+ * 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.search;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+
+/**
+ * Provides views for local search results
+ */
+public class DefaultSearchAdapterProvider extends SearchAdapterProvider {
+
+ public DefaultSearchAdapterProvider(BaseDraggingActivity launcher) {
+ super(launcher);
+ }
+
+ @Override
+ public void onBindView(AllAppsGridAdapter.ViewHolder holder, int position) {
+
+ }
+
+ @Override
+ public boolean isSearchView(int viewType) {
+ return false;
+ }
+
+ @Override
+ public AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
+ ViewGroup parent, int viewType) {
+ return null;
+ }
+
+ @Override
+ public boolean onAdapterItemSelected(AllAppsGridAdapter.AdapterItem adapterItem, View view) {
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
new file mode 100644
index 0000000..76099a6
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
@@ -0,0 +1,286 @@
+/*
+ * 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.launcher3.allapps.search;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
+
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Observer;
+import androidx.slice.Slice;
+import androidx.slice.SliceViewManager;
+import androidx.slice.SliceViewManager.SliceCallback;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SafeCloseable;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Manages Lifecycle for Live search results
+ */
+public class LiveSearchManager implements StateListener<LauncherState> {
+
+ private static final String TAG = "LiveSearchManager";
+
+ public static final int SEARCH_APPWIDGET_HOST_ID = 2048;
+
+ private final Launcher mLauncher;
+ private final HashMap<Uri, SliceLifeCycle> mUriSliceMap = new HashMap<>();
+
+ private final HashMap<ComponentKey, SearchWidgetInfoContainer> mWidgetPlaceholders =
+ new HashMap<>();
+ private SearchWidgetHost mSearchWidgetHost;
+
+ public LiveSearchManager(Launcher launcher) {
+ mLauncher = launcher;
+ mLauncher.getStateManager().addStateListener(this);
+ }
+
+ /**
+ * Creates new {@link AppWidgetHostView} from {@link AppWidgetProviderInfo}. Caches views for
+ * quicker result within the same search session
+ */
+ public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) {
+ if (mSearchWidgetHost == null) {
+ mSearchWidgetHost = new SearchWidgetHost(mLauncher);
+ mSearchWidgetHost.startListening();
+ }
+
+ ComponentName provider = providerInfo.provider;
+ UserHandle userHandle = providerInfo.getProfile();
+
+ ComponentKey key = new ComponentKey(provider, userHandle);
+ if (mWidgetPlaceholders.containsKey(key)) {
+ return mWidgetPlaceholders.get(key);
+ }
+
+ LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
+ mLauncher, providerInfo);
+ PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo);
+
+ Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo);
+ int appWidgetId = mSearchWidgetHost.allocateAppWidgetId();
+ boolean success = AppWidgetManager.getInstance(mLauncher)
+ .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options);
+ if (!success) {
+ mSearchWidgetHost.deleteAppWidgetId(appWidgetId);
+ mWidgetPlaceholders.put(key, null);
+ return null;
+ }
+
+ SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(
+ mLauncher, appWidgetId, providerInfo);
+ view.setTag(pendingAddWidgetInfo);
+ mWidgetPlaceholders.put(key, view);
+ return view;
+ }
+
+ /**
+ * Stop search session
+ */
+ public void stop() {
+ clearWidgetHost();
+ }
+
+ private void clearWidgetHost() {
+ if (mSearchWidgetHost != null) {
+ mSearchWidgetHost.stopListening();
+ mSearchWidgetHost.clearViews();
+ mSearchWidgetHost.deleteHost();
+ mWidgetPlaceholders.clear();
+ mSearchWidgetHost = null;
+ }
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (finalState != ALL_APPS) {
+ // Clear all search session related objects
+ mUriSliceMap.values().forEach(SliceLifeCycle::destroy);
+ mUriSliceMap.clear();
+
+ clearWidgetHost();
+ }
+ }
+
+ /**
+ * Adds a new observer for the provided uri and returns a callback to cancel this observer
+ */
+ public SafeCloseable addObserver(Uri uri, Observer<Slice> listener) {
+ SliceLifeCycle slc = mUriSliceMap.get(uri);
+ if (slc == null) {
+ slc = new SliceLifeCycle(uri, mLauncher);
+ mUriSliceMap.put(uri, slc);
+ }
+ slc.addListener(listener);
+
+ final SliceLifeCycle sliceLifeCycle = slc;
+ return () -> sliceLifeCycle.removeListener(listener);
+ }
+
+ static class SearchWidgetHost extends AppWidgetHost {
+ SearchWidgetHost(Context context) {
+ super(context, SEARCH_APPWIDGET_HOST_ID);
+ }
+
+ @Override
+ protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+ AppWidgetProviderInfo appWidget) {
+ return new SearchWidgetInfoContainer(context);
+ }
+
+ @Override
+ public void clearViews() {
+ super.clearViews();
+ }
+ }
+
+ private static class SliceLifeCycle
+ implements ActivityLifecycleCallbacks, SliceCallback {
+
+ private final Uri mUri;
+ private final Launcher mLauncher;
+ private final SliceViewManager mSliceViewManager;
+ private final ArrayList<Observer<Slice>> mListeners = new ArrayList<>();
+
+ private boolean mDestroyed = false;
+ private boolean mWasListening = false;
+
+ SliceLifeCycle(Uri uri, Launcher launcher) {
+ mUri = uri;
+ mLauncher = launcher;
+ mSliceViewManager = SliceViewManager.getInstance(launcher);
+ launcher.registerActivityLifecycleCallbacks(this);
+
+ if (launcher.isDestroyed()) {
+ onActivityDestroyed(launcher);
+ } else if (launcher.isStarted()) {
+ onActivityStarted(launcher);
+ }
+ }
+
+ @Override
+ public void onActivityDestroyed(Activity activity) {
+ destroy();
+ }
+
+ @Override
+ public void onActivityStarted(Activity activity) {
+ updateListening();
+ }
+
+ @Override
+ public void onActivityStopped(Activity activity) {
+ updateListening();
+ }
+
+ private void updateListening() {
+ boolean isListening = mDestroyed
+ ? false
+ : (mLauncher.isStarted() && !mListeners.isEmpty());
+ UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening));
+ }
+
+ @WorkerThread
+ private void uploadListeningBg(boolean isListening) {
+ if (mWasListening != isListening) {
+ mWasListening = isListening;
+ if (isListening) {
+ mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this);
+ // Update slice one-time on the different thread so that we can display
+ // multiple slices in parallel
+ THREAD_POOL_EXECUTOR.execute(this::updateSlice);
+ } else {
+ mSliceViewManager.unregisterSliceCallback(mUri, this);
+ }
+ }
+ }
+
+ @UiThread
+ private void addListener(Observer<Slice> listener) {
+ mListeners.add(listener);
+ updateListening();
+ }
+
+ @UiThread
+ private void removeListener(Observer<Slice> listener) {
+ mListeners.remove(listener);
+ updateListening();
+ }
+
+ @WorkerThread
+ private void updateSlice() {
+ try {
+ Slice s = mSliceViewManager.bindSlice(mUri);
+ MAIN_EXECUTOR.execute(() -> onSliceUpdated(s));
+ } catch (Exception e) {
+ Log.d(TAG, "Error fetching slice", e);
+ }
+ }
+
+ @UiThread
+ @Override
+ public void onSliceUpdated(@Nullable Slice s) {
+ mListeners.forEach(l -> l.onChanged(s));
+ }
+
+ private void destroy() {
+ if (mDestroyed) {
+ return;
+ }
+ mDestroyed = true;
+ mLauncher.unregisterActivityLifecycleCallbacks(this);
+ mListeners.clear();
+ }
+
+ @Override
+ public void onActivityCreated(Activity activity, Bundle bundle) { }
+
+ @Override
+ public void onActivityPaused(Activity activity) { }
+
+ @Override
+ public void onActivityResumed(Activity activity) { }
+
+ @Override
+ public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
+ }
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
new file mode 100644
index 0000000..fdacd3d
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchAdapterProvider.java
@@ -0,0 +1,73 @@
+/*
+ * 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.search;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+
+/**
+ * A UI expansion wrapper providing for search results
+ */
+public abstract class SearchAdapterProvider {
+
+ protected final BaseDraggingActivity mLauncher;
+
+ public SearchAdapterProvider(BaseDraggingActivity launcher) {
+ mLauncher = launcher;
+ }
+
+ /**
+ * Called from RecyclerView.Adapter#onBindViewHolder
+ */
+ public abstract void onBindView(AllAppsGridAdapter.ViewHolder holder, int position);
+
+ /**
+ * Returns whether or not viewType can be handled by searchProvider
+ */
+ public abstract boolean isSearchView(int viewType);
+
+ /**
+ * Called from RecyclerView.Adapter#onCreateViewHolder
+ */
+ public abstract AllAppsGridAdapter.ViewHolder onCreateViewHolder(LayoutInflater layoutInflater,
+ ViewGroup parent, int viewType);
+
+ /**
+ * Returns supported item per row combinations supported
+ */
+ public int[] getSupportedItemsPerRowArray() {
+ return new int[]{};
+ }
+
+ /**
+ * Returns how many cells a view should span
+ */
+ public int getItemsPerRow(int viewType, int appsPerRow) {
+ return appsPerRow;
+ }
+
+ /**
+ * handles selection event on search adapter item. Returns false if provider can not handle
+ * event
+ */
+ public abstract boolean onAdapterItemSelected(AllAppsGridAdapter.AdapterItem adapterItem,
+ View view);
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchPipeline.java b/src/com/android/launcher3/allapps/search/SearchPipeline.java
new file mode 100644
index 0000000..3516a41
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchPipeline.java
@@ -0,0 +1,37 @@
+/*
+ * 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.launcher3.allapps.search;
+
+import android.os.CancellationSignal;
+
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * An interface for handling search within pipeline
+ */
+// Remove when System Service API is added.
+public interface SearchPipeline {
+
+ /**
+ * Perform query
+ */
+ void query(String input,
+ Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
+ CancellationSignal cancellationSignal);
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
new file mode 100644
index 0000000..8e5f8cb
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
@@ -0,0 +1,73 @@
+/*
+ * 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.launcher3.allapps.search;
+
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.widget.RemoteViews;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A placeholder {@link AppWidgetHostView} used for managing widget search results
+ */
+public class SearchWidgetInfoContainer extends AppWidgetHostView {
+ private int mAppWidgetId;
+ private AppWidgetProviderInfo mProviderInfo;
+ private RemoteViews mViews;
+ private List<AppWidgetHostView> mListeners = new ArrayList<>();
+
+ public SearchWidgetInfoContainer(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void setAppWidget(int appWidgetId, AppWidgetProviderInfo info) {
+ mAppWidgetId = appWidgetId;
+ mProviderInfo = info;
+ for (AppWidgetHostView listener : mListeners) {
+ listener.setAppWidget(mAppWidgetId, mProviderInfo);
+ }
+ }
+
+ @Override
+ public void updateAppWidget(RemoteViews remoteViews) {
+ mViews = remoteViews;
+ for (AppWidgetHostView listener : mListeners) {
+ listener.updateAppWidget(remoteViews);
+ }
+ }
+
+ /**
+ * Create a live {@link AppWidgetHostView} from placeholder
+ */
+ public void attachWidget(AppWidgetHostView hv) {
+ hv.setTag(getTag());
+ hv.setAppWidget(mAppWidgetId, mProviderInfo);
+ hv.updateAppWidget(mViews);
+ mListeners.add(hv);
+ }
+
+ /**
+ * stops AppWidgetHostView from getting updates
+ */
+ public void detachWidget(AppWidgetHostView hostView) {
+ mListeners.remove(hostView);
+ }
+
+}
diff --git a/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
new file mode 100644
index 0000000..0b64fca
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SectionDecorationInfo.java
@@ -0,0 +1,62 @@
+/*
+ * 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.launcher3.allapps.search;
+
+import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHandler;
+
+/**
+ * Info class for a search section that is primarily used for decoration.
+ */
+public class SectionDecorationInfo {
+
+ public static final int QUICK_LAUNCH = 1 << 0;
+ public static final int GROUPING = 1 << 1;
+
+ private String mSectionId;
+ private boolean mFocused;
+ private SectionDecorationHandler mDecorationHandler;
+
+ public boolean isFocusedView() {
+ return mFocused;
+ }
+
+ public void setFocusedView(boolean focused) {
+ mFocused = focused;
+ }
+
+ public SectionDecorationInfo() {
+ this(null);
+ }
+
+ public SectionDecorationInfo(String sectionId) {
+ mSectionId = sectionId;
+ }
+
+ public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
+ mDecorationHandler = sectionDecorationHandler;
+ }
+
+ public SectionDecorationHandler getDecorationHandler() {
+ return mDecorationHandler;
+ }
+
+ /**
+ * Returns the section's ID
+ */
+ public String getSectionId() {
+ return mSectionId == null ? "" : mSectionId;
+ }
+}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index ea0ff8b..f6d1651 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
@@ -29,7 +29,7 @@
import android.animation.ValueAnimator;
import android.content.Context;
-import androidx.annotation.Nullable;
+import com.android.launcher3.Utilities;
import java.util.ArrayList;
import java.util.Collections;
@@ -72,7 +72,6 @@
private Runnable mEndAction;
protected boolean mTargetCancelled = false;
- protected Runnable mOnCancelRunnable;
/** package private */
AnimatorPlaybackController(AnimatorSet anim, long duration, ArrayList<Holder> childAnims) {
@@ -88,16 +87,11 @@
@Override
public void onAnimationCancel(Animator animation) {
mTargetCancelled = true;
- if (mOnCancelRunnable != null) {
- mOnCancelRunnable.run();
- mOnCancelRunnable = null;
- }
}
@Override
public void onAnimationEnd(Animator animation) {
mTargetCancelled = false;
- mOnCancelRunnable = null;
}
@Override
@@ -141,15 +135,20 @@
/**
* Starts playing the animation with the provided velocity optionally playing any
- * physics based animations
+ * physics based animations.
+ * @param goingToEnd Whether we are going to the end (progress = 1) or not (progress = 0).
+ * @param velocityPxPerMs The velocity at which to start the animation, in pixels / millisecond.
+ * @param endDistance The distance (pixels) that the animation will travel from progress 0 to 1.
+ * @param animationDuration The duration of the non-physics based animation.
*/
public void startWithVelocity(Context context, boolean goingToEnd,
- float velocity, float scale, long animationDuration) {
- float scaleInverse = 1 / Math.abs(scale);
- float scaledVelocity = velocity * scaleInverse;
+ float velocityPxPerMs, float endDistance, long animationDuration) {
+ float distanceInverse = 1 / Math.abs(endDistance);
+ float velocityProgressPerMs = velocityPxPerMs * distanceInverse;
+ float oneFrameProgress = velocityProgressPerMs * getSingleFrameMs(context);
float nextFrameProgress = boundToRange(getProgressFraction()
- + scaledVelocity * getSingleFrameMs(context), 0f, 1f);
+ + oneFrameProgress, 0f, 1f);
// Update setters for spring
int springFlag = goingToEnd
@@ -162,8 +161,8 @@
SpringAnimationBuilder s = new SpringAnimationBuilder(context)
.setStartValue(mCurrentFraction)
.setEndValue(goingToEnd ? 1 : 0)
- .setStartVelocity(scaledVelocity)
- .setMinimumVisibleChange(scaleInverse)
+ .setStartVelocity(velocityProgressPerMs)
+ .setMinimumVisibleChange(distanceInverse)
.setDampingRatio(h.springProperty.mDampingRatio)
.setStiffness(h.springProperty.mStiffness)
.computeParams();
@@ -172,8 +171,18 @@
springDuration = Math.max(expectedDurationL, springDuration);
float expectedDuration = expectedDurationL;
- h.mapper = (progress, globalEndProgress) ->
- mAnimationPlayer.getCurrentPlayTime() / expectedDuration;
+ h.mapper = (progress, globalEndProgress) -> {
+ if (expectedDuration <= 0 || oneFrameProgress >= 1) {
+ return 1;
+ } else {
+ // Start from one frame ahead of the current position.
+ return Utilities.mapToRange(
+ mAnimationPlayer.getCurrentPlayTime() / expectedDuration,
+ 0, 1,
+ Math.abs(oneFrameProgress), 1,
+ LINEAR);
+ }
+ };
h.anim.setInterpolator(s::getInterpolatedValue);
}
}
@@ -182,7 +191,7 @@
if (springDuration <= animationDuration) {
mAnimationPlayer.setDuration(animationDuration);
- mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ mAnimationPlayer.setInterpolator(scrollInterpolatorForVelocity(velocityPxPerMs));
} else {
// Since spring requires more time to run, we let the other animations play with
// current time and interpolation and by clamping the duration.
@@ -190,7 +199,7 @@
float cutOff = animationDuration / (float) springDuration;
mAnimationPlayer.setInterpolator(
- clampToProgress(scrollInterpolatorForVelocity(velocity), 0, cutOff));
+ clampToProgress(scrollInterpolatorForVelocity(velocityPxPerMs), 0, cutOff));
}
mAnimationPlayer.start();
}
@@ -269,33 +278,6 @@
}
}
- /** @see #dispatchOnCancelWithoutCancelRunnable(Runnable) */
- public void dispatchOnCancelWithoutCancelRunnable() {
- dispatchOnCancelWithoutCancelRunnable(null);
- }
-
- /**
- * Sets mOnCancelRunnable = null before dispatching the cancel and restoring the runnable. This
- * is intended to be used only if you need to cancel but want to defer cleaning up yourself.
- * @param callback An optional callback to run after dispatching the cancel but before resetting
- * the onCancelRunnable.
- */
- public void dispatchOnCancelWithoutCancelRunnable(@Nullable Runnable callback) {
- Runnable onCancel = mOnCancelRunnable;
- setOnCancelRunnable(null);
- dispatchOnCancel();
- if (callback != null) {
- callback.run();
- }
- setOnCancelRunnable(onCancel);
- }
-
-
- public AnimatorPlaybackController setOnCancelRunnable(Runnable runnable) {
- mOnCancelRunnable = runnable;
- return this;
- }
-
public void dispatchOnStart() {
callListenerCommandRecursively(mAnim, AnimatorListener::onAnimationStart);
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 6ad43ea..6b9ed09 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -16,9 +16,6 @@
package com.android.launcher3.anim;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
-
-import android.content.Context;
import android.graphics.Path;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
@@ -67,9 +64,6 @@
*/
public static final Interpolator FINAL_FRAME = t -> t < 1 ? 0 : 1;
- private static final int MIN_SETTLE_DURATION = 200;
- private static final float OVERSHOOT_FACTOR = 0.9f;
-
static {
Path exaggeratedEase = new Path();
exaggeratedEase.moveTo(0, 0);
@@ -175,76 +169,4 @@
float upperBound) {
return t -> Utilities.mapRange(interpolator.getInterpolation(t), lowerBound, upperBound);
}
-
- /**
- * Computes parameters necessary for an overshoot effect.
- */
- public static class OvershootParams {
- public Interpolator interpolator;
- public float start;
- public float end;
- public long duration;
-
- /**
- * Given the input params, sets OvershootParams variables to be used by the caller.
- * @param startProgress The progress from 0 to 1 that the overshoot starts from.
- * @param overshootPastProgress The progress from 0 to 1 where we overshoot past (should
- * either be equal to startProgress or endProgress, depending on if we want to
- * overshoot immediately or only once we reach the end).
- * @param endProgress The final progress from 0 to 1 that we will settle to.
- * @param velocityPxPerMs The initial velocity that causes this overshoot.
- * @param totalDistancePx The distance against which progress is calculated.
- */
- public OvershootParams(float startProgress, float overshootPastProgress,
- float endProgress, float velocityPxPerMs, int totalDistancePx, Context context) {
- velocityPxPerMs = Math.abs(velocityPxPerMs);
- overshootPastProgress = Math.max(overshootPastProgress, startProgress);
- start = startProgress;
- int startPx = (int) (start * totalDistancePx);
- // Overshoot by about half a frame.
- float overshootBy = OVERSHOOT_FACTOR * velocityPxPerMs *
- getSingleFrameMs(context) / totalDistancePx / 2;
- overshootBy = Utilities.boundToRange(overshootBy, 0.02f, 0.15f);
- end = overshootPastProgress + overshootBy;
- int endPx = (int) (end * totalDistancePx);
- int overshootDistance = endPx - startPx;
- // Calculate deceleration necessary to reach overshoot distance.
- // Formula: velocityFinal^2 = velocityInitial^2 + 2 * acceleration * distance
- // 0 = v^2 + 2ad (velocityFinal == 0)
- // a = v^2 / -2d
- float decelerationPxPerMs = velocityPxPerMs * velocityPxPerMs / (2 * overshootDistance);
- // Calculate time necessary to reach peak of overshoot.
- // Formula: acceleration = velocity / time
- // time = velocity / acceleration
- duration = (long) (velocityPxPerMs / decelerationPxPerMs);
-
- // Now that we're at the top of the overshoot, need to settle back to endProgress.
- float settleDistance = end - endProgress;
- int settleDistancePx = (int) (settleDistance * totalDistancePx);
- // Calculate time necessary for the settle.
- // Formula: distance = velocityInitial * time + 1/2 * acceleration * time^2
- // d = 1/2at^2 (velocityInitial = 0, since we just stopped at the top)
- // t = sqrt(2d/a)
- // Above formula assumes constant acceleration. Since we use ACCEL_DEACCEL, we actually
- // have acceleration to halfway then deceleration the rest. So the formula becomes:
- // t = sqrt(d/a) * 2 (half the distance for accel, half for deaccel)
- long settleDuration = (long) Math.sqrt(settleDistancePx / decelerationPxPerMs) * 4;
-
- settleDuration = Math.max(MIN_SETTLE_DURATION, settleDuration);
- // How much of the animation to devote to playing the overshoot (the rest is for settle).
- float overshootFraction = (float) duration / (duration + settleDuration);
- duration += settleDuration;
- // Finally, create the interpolator, composed of two interpolators: an overshoot, which
- // reaches end > 1, and then a settle to endProgress.
- Interpolator overshoot = Interpolators.clampToProgress(DEACCEL, 0, overshootFraction);
- // The settle starts at 1, where 1 is the top of the overshoot, and maps to a fraction
- // such that final progress is endProgress. For example, if we overshot to 1.1 but want
- // to end at 1, we need to map to 1/1.1.
- Interpolator settle = Interpolators.clampToProgress(Interpolators.mapToProgress(
- ACCEL_DEACCEL, 1, (endProgress - start) / (end - start)), overshootFraction, 1);
- interpolator = t -> t <= overshootFraction
- ? overshoot.getInterpolation(t)
- : settle.getInterpolation(t);
- }
- }
}
diff --git a/src/com/android/launcher3/anim/PendingAnimation.java b/src/com/android/launcher3/anim/PendingAnimation.java
index 4195933..4e90c9e 100644
--- a/src/com/android/launcher3/anim/PendingAnimation.java
+++ b/src/com/android/launcher3/anim/PendingAnimation.java
@@ -15,10 +15,12 @@
*/
package com.android.launcher3.anim;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -43,8 +45,6 @@
*/
public class PendingAnimation implements PropertySetter {
- private final ArrayList<Consumer<EndState>> mEndListeners = new ArrayList<>();
-
private final ArrayList<Holder> mAnimHolders = new ArrayList<>();
private final AnimatorSet mAnim;
private final long mDuration;
@@ -73,13 +73,6 @@
addAnimationHoldersRecur(a, mDuration, springProperty, mAnimHolders);
}
- public void finish(boolean isSuccess, int logAction) {
- for (Consumer<EndState> listeners : mEndListeners) {
- listeners.accept(new EndState(isSuccess, logAction));
- }
- mEndListeners.clear();
- }
-
@Override
public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
if (view == null || view.getAlpha() == alpha) {
@@ -149,7 +142,7 @@
mProgressAnimator = null;
}
if (mAnimHolders.isEmpty()) {
- // Add a dummy animation to that the duration is respected
+ // Add a placeholder animation to that the duration is respected
add(ValueAnimator.ofFloat(0, 1).setDuration(mDuration));
}
return mAnim;
@@ -163,21 +156,38 @@
}
/**
- * Add a listener of receiving the end state.
- * Note that the listeners are called as a result of calling {@link #finish(boolean, int)}
- * and not automatically
+ * Add a listener of receiving the success/failure callback in the end.
*/
- public void addEndListener(Consumer<EndState> listener) {
- mEndListeners.add(listener);
+ public void addEndListener(Consumer<Boolean> listener) {
+ if (mProgressAnimator == null) {
+ mProgressAnimator = ValueAnimator.ofFloat(0, 1);
+ }
+ mProgressAnimator.addListener(new EndStateCallbackWrapper(listener));
}
- public static class EndState {
- public boolean isSuccess;
- public int logAction;
+ private static class EndStateCallbackWrapper extends AnimatorListenerAdapter {
- public EndState(boolean isSuccess, int logAction) {
- this.isSuccess = isSuccess;
- this.logAction = logAction;
+ private final Consumer<Boolean> mListener;
+ private boolean mCalled = false;
+
+ EndStateCallbackWrapper(Consumer<Boolean> listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!mCalled) {
+ mCalled = true;
+ mListener.accept(false);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCalled) {
+ ValueAnimator anim = (ValueAnimator) animation;
+ mListener.accept(anim.getAnimatedFraction() > SUCCESS_TRANSITION_PROGRESS);
+ }
}
}
}
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index a9702b4..bd52158 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -25,7 +25,7 @@
import androidx.annotation.FloatRange;
import androidx.dynamicanimation.animation.SpringForce;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
/**
* Utility class to build an object animator which follows the same path as a spring animation for
@@ -134,7 +134,7 @@
}
public SpringAnimationBuilder computeParams() {
- int singleFrameMs = DefaultDisplay.getSingleFrameMs(mContext);
+ int singleFrameMs = DisplayController.getSingleFrameMs(mContext);
double naturalFreq = Math.sqrt(mStiffness);
double dampedFreq = naturalFreq * Math.sqrt(1 - mDampingRatio * mDampingRatio);
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 1d32d1d..30c3417 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -70,7 +70,8 @@
final Bundle parcel = new Bundle();
parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
- sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
+ sendEventToTest(
+ accessibilityManager, context, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
Log.d(TestProtocol.PERMANENT_DIAG_TAG, "sendStateEventToTest: " + stateOrdinal);
}
@@ -78,22 +79,24 @@
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
- sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
+ sendEventToTest(accessibilityManager, context, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
}
public static void sendPauseDetectedEventToTest(Context context) {
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
- sendEventToTest(accessibilityManager, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
+ sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
}
private static void sendEventToTest(
- AccessibilityManager accessibilityManager, String eventTag, Bundle data) {
+ AccessibilityManager accessibilityManager,
+ Context context, String eventTag, Bundle data) {
final AccessibilityEvent e = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
e.setClassName(eventTag);
e.setParcelableData(data);
+ e.setPackageName(context.getApplicationContext().getPackageName());
accessibilityManager.sendAccessibilityEvent(e);
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 88a9aba..e406e9b 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,9 +72,6 @@
"PROMISE_APPS_NEW_INSTALLS", true,
"Adds a promise icon to the home screen for new install sessions.");
- public static final BooleanFlag APPLY_CONFIG_AT_RUNTIME = getDebugFlag(
- "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
-
public static final BooleanFlag QUICKSTEP_SPRINGS = getDebugFlag(
"QUICKSTEP_SPRINGS", true, "Enable springs for quickstep animations");
@@ -94,6 +91,10 @@
public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
"ENABLE_SUGGESTED_ACTIONS_OVERVIEW", true, "Show chip hints on the overview screen");
+
+ public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
+ "ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
+
public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
"FOLDER_NAME_SUGGEST", true,
"Suggests folder names instead of blank text.");
@@ -123,9 +124,6 @@
"ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
"Allow Launcher to handle nav bar gestures while Assistant is running over it");
- public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
- "ENABLE_HYBRID_HOTSEAT", true, "Fill gaps in hotseat with predicted apps");
-
public static final BooleanFlag HOTSEAT_MIGRATE_TO_FOLDER = getDebugFlag(
"HOTSEAT_MIGRATE_TO_FOLDER", false, "Should move hotseat items into a folder");
@@ -138,10 +136,6 @@
public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
"ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
- public static final BooleanFlag ENABLE_OVERVIEW_ACTIONS = getDebugFlag(
- "ENABLE_OVERVIEW_ACTIONS", true, "Show app actions instead of the shelf in Overview."
- + " As part of this decoupling, also distinguish swipe up from nav bar vs above it.");
-
// Keep as DeviceFlag for remote disable in emergency.
public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
"ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
@@ -149,17 +143,25 @@
public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag(
"ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions");
+ public static final BooleanFlag ENABLE_OVERVIEW_SHARING_TO_PEOPLE = getDebugFlag(
+ "ENABLE_OVERVIEW_SHARING_TO_PEOPLE", false,
+ "Show indicators for content on Overview to share with top people. ");
+
+ public static final BooleanFlag ENABLE_OVERVIEW_CONTENT_PUSH = getDebugFlag(
+ "ENABLE_OVERVIEW_CONTENT_PUSH", false, "Show Content Push button in Overview Actions");
+
public static final BooleanFlag ENABLE_DATABASE_RESTORE = getDebugFlag(
- "ENABLE_DATABASE_RESTORE", true,
+ "ENABLE_DATABASE_RESTORE", false,
"Enable database restore when new restore session is created");
- public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag(
- "ENABLE_UNIVERSAL_SMARTSPACE", false,
+ public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
+ "ENABLE_SMARTSPACE_UNIVERSAL", false,
"Replace Smartspace with a version rendered by System UI.");
- public static final BooleanFlag ENABLE_LSQ_VELOCITY_PROVIDER = getDebugFlag(
- "ENABLE_LSQ_VELOCITY_PROVIDER", true,
- "Use Least Square algorithm for motion pause detection.");
+ public static final BooleanFlag ENABLE_SMARTSPACE_ENHANCED = new DeviceFlag(
+ "ENABLE_SMARTSPACE_ENHANCED", false,
+ "Replace Smartspace with the enhanced version. "
+ + "Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
getDebugFlag(
@@ -174,13 +176,35 @@
"SEPARATE_RECENTS_ACTIVITY", false,
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
- public static final BooleanFlag USER_EVENT_DISPATCHER = new DeviceFlag(
- "USER_EVENT_DISPATCHER", true, "User event dispatcher collects logs.");
-
- public static final BooleanFlag ENABLE_MINIMAL_DEVICE = new DeviceFlag(
+ public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
"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.");
+
+ public static final BooleanFlag ENABLE_FOUR_COLUMNS = new DeviceFlag(
+ "ENABLE_FOUR_COLUMNS", false, "Uses 4 columns in launcher grid."
+ + "Warning: This will permanently alter your home screen items and is not reversible.");
+
+ // 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 "
+ + "crossfade animation when the system these changes.");
+
+ // TODO: b/174174514 Remove ENABLE_APP_PREDICTIONS_WHILE_VISIBLE feature flag.
+ public static final BooleanFlag ENABLE_APP_PREDICTIONS_WHILE_VISIBLE = new DeviceFlag(
+ "ENABLE_APP_PREDICTIONS_WHILE_VISIBLE", true, "Allows app "
+ + "predictions to be updated while they are visible to the user.");
+
+ public static final BooleanFlag ENABLE_TASKBAR = new DeviceFlag(
+ "ENABLE_TASKBAR", false, "Allows a system Taskbar to be shown on larger devices.");
+
+ public static final BooleanFlag ENABLE_OVERVIEW_GRID = new DeviceFlag(
+ "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
+ + "Only applicable on large screen devices.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
@@ -239,6 +263,8 @@
}
public void addChangeListener(Context context, Runnable r) { }
+
+ public void removeChangeListener(Runnable r) {}
}
public static class DebugFlag extends BooleanFlag {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 0df6713..c972cbb 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,10 +16,11 @@
package com.android.launcher3.dragndrop;
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_BACK;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED;
+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 android.annotation.TargetApi;
@@ -43,21 +44,21 @@
import android.view.View.OnTouchListener;
import com.android.launcher3.BaseActivity;
-import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
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.data.ItemInfo;
import com.android.launcher3.pm.PinRequestHelper;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetCell;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -78,7 +79,7 @@
private LauncherAppState mApp;
private InvariantDeviceProfile mIdp;
- private LivePreviewWidgetCell mWidgetCell;
+ private WidgetCell mWidgetCell;
// Widget request specific options.
private LauncherAppWidgetHost mAppWidgetHost;
@@ -87,7 +88,6 @@
private Bundle mWidgetOptions;
private boolean mFinishOnPause = false;
- private InstantAppResolver mInstantAppResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -101,7 +101,6 @@
mApp = LauncherAppState.getInstance(this);
mIdp = mApp.getInvariantDeviceProfile();
- mInstantAppResolver = InstantAppResolver.newInstance(this);
// Use the application context to get the device profile, as in multiwindow-mode, the
// confirmation activity might be rotated.
@@ -119,13 +118,14 @@
}
}
- mWidgetCell.setOnTouchListener(this);
- mWidgetCell.setOnLongClickListener(this);
+ WidgetImageView preview = mWidgetCell.findViewById(R.id.widget_preview);
+ preview.setOnTouchListener(this);
+ preview.setOnLongClickListener(this);
// savedInstanceState is null when the activity is created the first time (i.e., avoids
// duplicate logging during rotation)
if (savedInstanceState == null) {
- logCommand(Action.Command.ENTRY);
+ logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_START);
}
}
@@ -178,6 +178,7 @@
startActivity(homeIntent,
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out)
.toBundle());
+ logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED);
mFinishOnPause = true;
return false;
}
@@ -240,7 +241,7 @@
* Called when the cancel button is clicked.
*/
public void onCancelClick(View v) {
- logCommand(Action.Command.CANCEL);
+ logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED);
finish();
}
@@ -249,8 +250,8 @@
*/
public void onPlaceAutomaticallyClick(View v) {
if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
- InstallShortcutReceiver.queueShortcut(mRequest.getShortcutInfo(), this);
- logCommand(Action.Command.CONFIRM);
+ ItemInstallQueue.INSTANCE.get(this).queueItem(mRequest.getShortcutInfo());
+ logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
mRequest.accept();
finish();
return;
@@ -270,16 +271,17 @@
}
private void acceptWidget(int widgetId) {
- InstallShortcutReceiver.queueWidget(mRequest.getAppWidgetProviderInfo(this), widgetId, this);
+ ItemInstallQueue.INSTANCE.get(this)
+ .queueItem(mRequest.getAppWidgetProviderInfo(this), widgetId);
mWidgetOptions.putInt(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
mRequest.accept(mWidgetOptions);
- logCommand(Action.Command.CONFIRM);
+ logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY);
finish();
}
@Override
public void onBackPressed() {
- logCommand(Action.Command.BACK);
+ logCommand(LAUNCHER_ADD_EXTERNAL_ITEM_BACK);
super.onBackPressed();
}
@@ -319,10 +321,9 @@
throw new UnsupportedOperationException();
}
- private void logCommand(int command) {
- getUserEventDispatcher().dispatchUserEvent(newLauncherEvent(
- newCommandAction(command),
- newItemTarget(mWidgetCell.getWidgetView(), mInstantAppResolver),
- newContainerTarget(ContainerType.PINITEM)), null);
+ private void logCommand(StatsLogManager.EventEnum command) {
+ getStatsLogManager().logger()
+ .withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
+ .log(command);
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 03028d3..93df599 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -40,12 +40,14 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.TouchController;
import java.util.ArrayList;
+import java.util.Optional;
/**
* Class for initiating a drag within a view or across multiple views.
@@ -206,7 +208,6 @@
}
handleMoveEvent(mLastTouch.x, mLastTouch.y);
- mLauncher.getUserEventDispatcher().resetActionDurationMillis();
if (!mLauncher.isTouchInProgress() && options.simulatedDndStartPoint == null) {
// If it is an internal drag and the touch is already complete, cancel immediately
@@ -231,6 +232,11 @@
}
}
+ public Optional<InstanceId> getLogInstanceId() {
+ return Optional.ofNullable(mDragObject)
+ .map(dragObject -> dragObject.logInstanceId);
+ }
+
/**
* Call this from a drag source view like this:
*
@@ -463,10 +469,10 @@
}
public void forceTouchMove() {
- int[] dummyCoordinates = mCoordinatesTemp;
- DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, dummyCoordinates);
- mDragObject.x = dummyCoordinates[0];
- mDragObject.y = dummyCoordinates[1];
+ int[] placeholderCoordinates = mCoordinatesTemp;
+ DropTarget dropTarget = findDropTarget(mLastTouch.x, mLastTouch.y, placeholderCoordinates);
+ mDragObject.x = placeholderCoordinates[0];
+ mDragObject.y = placeholderCoordinates[1];
checkTouchMove(dropTarget);
}
@@ -544,7 +550,6 @@
}
}
final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
- mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
dispatchDropComplete(dropTargetAsView, accepted);
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index ddf44ca..7a6b4f9 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -41,12 +41,14 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherRootView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.OverviewScrim;
-import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
+import com.android.launcher3.graphics.SysUiScrim;
+import com.android.launcher3.graphics.WorkspaceDragScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
@@ -82,11 +84,10 @@
// Related to adjacent page hints
private final ViewGroupFocusHelper mFocusIndicatorHelper;
- private final WorkspaceAndHotseatScrim mWorkspaceScrim;
private final OverviewScrim mOverviewScrim;
-
- // View that should handle move events
- private View mMoveTarget;
+ private WorkspaceDragScrim mWorkspaceDragScrim;
+ private SysUiScrim mSysUiScrim;
+ private LauncherRootView mRootView;
/**
* Used to create a new DragLayer from XML.
@@ -102,15 +103,23 @@
setChildrenDrawingOrderEnabled(true);
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
- mWorkspaceScrim = new WorkspaceAndHotseatScrim(this);
mOverviewScrim = new OverviewScrim(this);
}
public void setup(DragController dragController, Workspace workspace) {
mDragController = dragController;
- mWorkspaceScrim.setWorkspace(workspace);
- mMoveTarget = workspace;
recreateControllers();
+
+ mWorkspaceDragScrim = new WorkspaceDragScrim((this));
+ mWorkspaceDragScrim.setWorkspace(workspace);
+
+ // We delegate drawing of the workspace scrim to LauncherRootView (one level up), so as
+ // to avoid artifacts when translating the entire drag layer in the -1 transition.
+ mRootView = (LauncherRootView) getParent();
+ mSysUiScrim = new SysUiScrim(mRootView);
+ mRootView.setSysUiScrim(mSysUiScrim);
+
+
}
@Override
@@ -215,12 +224,6 @@
}
@Override
- public boolean dispatchUnhandledMove(View focused, int direction) {
- return super.dispatchUnhandledMove(focused, direction)
- || mMoveTarget.dispatchUnhandledMove(focused, direction);
- }
-
- @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
ev.offsetLocation(getTranslationX(), 0);
try {
@@ -525,7 +528,7 @@
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the background below children.
- mWorkspaceScrim.draw(canvas);
+ mWorkspaceDragScrim.draw(canvas);
mOverviewScrim.updateCurrentScrimmedView(this);
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
@@ -545,18 +548,22 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mWorkspaceScrim.setSize(w, h);
+ mSysUiScrim.setSize(w, h);
}
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- mWorkspaceScrim.onInsetsChanged(insets, mAllowSysuiScrims);
+ mSysUiScrim.onInsetsChanged(insets, mAllowSysuiScrims);
mOverviewScrim.onInsetsChanged(insets);
}
- public WorkspaceAndHotseatScrim getScrim() {
- return mWorkspaceScrim;
+ public WorkspaceDragScrim getWorkspaceDragScrim() {
+ return mWorkspaceDragScrim;
+ }
+
+ public SysUiScrim getSysUiScrim() {
+ return mSysUiScrim;
}
public OverviewScrim getOverviewScrim() {
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 959602b..e8ff8da 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -28,6 +28,9 @@
/** Whether or not an accessible drag operation is in progress. */
public boolean isAccessibleDrag = false;
+ /** Whether or not the drag operation is controlled by keyboard. */
+ public boolean isKeyboardDrag = false;
+
/**
* Specifies the start location for a simulated DnD (like system drag or accessibility drag),
* null when using internal DnD
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index de0fa1a..86b93d0 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -187,9 +187,6 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public void setItemInfo(final ItemInfo info) {
- if (!Utilities.ATLEAST_OREO) {
- return;
- }
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
diff --git a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
index 7788f93..0a1aba1 100644
--- a/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
+++ b/src/com/android/launcher3/dragndrop/FlingToDeleteHelper.java
@@ -65,6 +65,9 @@
}
public Runnable getFlingAnimation(DropTarget.DragObject dragObject, DragOptions options) {
+ if (options == null) {
+ return null;
+ }
PointF vel = isFlingingToDelete();
options.isFlingToDelete = vel != null;
if (!options.isFlingToDelete) {
diff --git a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
deleted file mode 100644
index a9389bc..0000000
--- a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.android.launcher3.dragndrop;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.RemoteViews;
-
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.icons.BitmapRenderer;
-import com.android.launcher3.widget.WidgetCell;
-
-/**
- * Extension of {@link WidgetCell} which supports generating previews from {@link RemoteViews}
- */
-public class LivePreviewWidgetCell extends WidgetCell {
-
- private RemoteViews mPreview;
-
- public LivePreviewWidgetCell(Context context) {
- this(context, null);
- }
-
- public LivePreviewWidgetCell(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public LivePreviewWidgetCell(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setPreview(RemoteViews view) {
- mPreview = view;
- }
-
- @Override
- public void ensurePreview() {
- if (mPreview != null && mActiveRequest == null) {
- Bitmap preview = generateFromRemoteViews(
- mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]);
- if (preview != null) {
- applyPreview(preview);
- return;
- }
- }
- super.ensurePreview();
- }
-
- /**
- * Generates a bitmap by inflating {@param views}.
- * @see com.android.launcher3.WidgetPreviewLoader#generateWidgetPreview
- *
- * TODO: Consider moving this to the background thread.
- */
- public static Bitmap generateFromRemoteViews(BaseActivity activity, RemoteViews views,
- LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
-
- DeviceProfile dp = activity.getDeviceProfile();
- int viewWidth = dp.cellWidthPx * info.spanX;
- int viewHeight = dp.cellHeightPx * info.spanY;
-
- final View v;
- try {
- v = views.apply(activity, new FrameLayout(activity));
- v.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
-
- viewWidth = v.getMeasuredWidth();
- viewHeight = v.getMeasuredHeight();
- v.layout(0, 0, viewWidth, viewHeight);
- } catch (Exception e) {
- return null;
- }
-
- preScaledWidthOut[0] = viewWidth;
- final int bitmapWidth, bitmapHeight;
- final float scale;
- if (viewWidth > previewSize) {
- scale = ((float) previewSize) / viewWidth;
- bitmapWidth = previewSize;
- bitmapHeight = (int) (viewHeight * scale);
- } else {
- scale = 1;
- bitmapWidth = viewWidth;
- bitmapHeight = viewHeight;
- }
-
- return BitmapRenderer.createSoftwareBitmap(bitmapWidth, bitmapHeight, c -> {
- c.scale(scale, scale);
- v.draw(c);
- });
- }
-}
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index bf3aa7f..2290473 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -16,7 +16,6 @@
package com.android.launcher3.dragndrop;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
@@ -31,17 +30,13 @@
import com.android.launcher3.DragSource;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingItemDragHelper;
import com.android.launcher3.widget.WidgetAddFlowHandler;
-import java.util.ArrayList;
-
/**
* {@link DragSource} for handling drop from a different window. This object is initialized
* in the source window and is passed on to the Launcher activity as an Intent extra.
@@ -107,12 +102,6 @@
}
@Override
- public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
- ArrayList<LauncherLogProto.Target> parents) {
- parents.add(newContainerTarget(LauncherLogProto.ContainerType.PINITEM));
- }
-
- @Override
protected void postCleanup() {
super.postCleanup();
mCancelSignal.cancel();
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index 9982b39..f543e47 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -49,14 +49,14 @@
// Class name used in the target component, such that it will never represent an
// actual existing class.
- private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut";
+ private static final String STUB_COMPONENT_CLASS = "pinned-shortcut";
private final PinItemRequest mRequest;
private final ShortcutInfo mInfo;
private final Context mContext;
public PinShortcutRequestActivityInfo(PinItemRequest request, Context context) {
- super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS),
+ super(new ComponentName(request.getShortcutInfo().getPackage(), STUB_COMPONENT_CLASS),
request.getShortcutInfo().getUserHandle());
mRequest = request;
mInfo = request.getShortcutInfo();
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index b91d1c3..bcb3a54 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -17,13 +17,13 @@
package com.android.launcher3.folder;
import static android.text.TextUtils.isEmpty;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
@@ -34,24 +34,34 @@
import android.appwidget.AppWidgetHostView;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Insets;
import android.graphics.Path;
import android.graphics.Rect;
+import android.os.Build;
import android.text.InputType;
import android.text.Selection;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
+import android.util.TypedValue;
import android.view.FocusFinder;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.EditorInfo;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Alarm;
import com.android.launcher3.BubbleTextView;
@@ -74,7 +84,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
@@ -86,9 +95,10 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.views.ClipPathView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -158,7 +168,11 @@
private AnimatorSet mCurrentAnimator;
private boolean mIsAnimatingClosed = false;
+ // Folder can be displayed in Launcher's activity or a separate window (e.g. Taskbar).
+ // Anything specific to Launcher should use mLauncher, otherwise should use mActivityContext.
protected final Launcher mLauncher;
+ protected final ActivityContext mActivityContext;
+
protected DragController mDragController;
public FolderInfo mInfo;
private CharSequence mFromTitle;
@@ -208,6 +222,8 @@
private StatsLogManager mStatsLogManager;
+ @Nullable private FolderWindowInsetsAnimationCallback mFolderWindowInsetsAnimationCallback;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -219,6 +235,7 @@
setAlwaysDrawnWithCacheEnabled(false);
mLauncher = Launcher.getLauncher(context);
+ mActivityContext = ActivityContext.lookupContext(context);
mStatsLogManager = StatsLogManager.newInstance(context);
// We need this view to be focusable in touch mode so that when text editing of the folder
// name is complete, we have something to focus on, thus hiding the cursor and giving
@@ -230,11 +247,16 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ final DeviceProfile dp = mLauncher.getDeviceProfile();
+ final int paddingLeftRight = dp.folderContentPaddingLeftRight;
+
mContent = findViewById(R.id.folder_content);
+ mContent.setPadding(paddingLeftRight, dp.folderContentPaddingTop, paddingLeftRight, 0);
mContent.setFolder(this);
mPageIndicator = findViewById(R.id.folder_page_indicator);
mFolderName = findViewById(R.id.folder_name);
+ mFolderName.setTextSize(TypedValue.COMPLEX_UNIT_PX, dp.folderLabelTextSizePx);
mFolderName.setOnBackKeyListener(this);
mFolderName.setOnFocusChangeListener(this);
mFolderName.setOnEditorActionListener(this);
@@ -246,12 +268,14 @@
mFolderName.forceDisableSuggestions(true);
mFooter = findViewById(R.id.folder_footer);
+ mFooterHeight = getResources().getDimensionPixelSize(R.dimen.folder_label_height);
- // We find out how tall footer wants to be (it is set to wrap_content), so that
- // we can allocate the appropriate amount of space for it.
- int measureSpec = MeasureSpec.UNSPECIFIED;
- mFooter.measure(measureSpec, measureSpec);
- mFooterHeight = mFooter.getMeasuredHeight();
+ if (Utilities.ATLEAST_R) {
+ mFolderWindowInsetsAnimationCallback =
+ new FolderWindowInsetsAnimationCallback(DISPATCH_MODE_STOP, this);
+
+ setWindowInsetsAnimationCallback(mFolderWindowInsetsAnimationCallback);
+ }
}
public boolean onLongClick(View v) {
@@ -373,6 +397,26 @@
return false;
}
+ @Override
+ public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+ if (Utilities.ATLEAST_R) {
+ this.setTranslationY(0);
+
+ if (windowInsets.isVisible(WindowInsets.Type.ime())) {
+ Insets keyboardInsets = windowInsets.getInsets(WindowInsets.Type.ime());
+ int folderHeightFromBottom = getHeightFromBottom();
+
+ if (keyboardInsets.bottom > folderHeightFromBottom) {
+ // Translate this folder above the keyboard, then add the folder name's padding
+ this.setTranslationY(folderHeightFromBottom - keyboardInsets.bottom
+ - mFolderName.getPaddingBottom());
+ }
+ }
+ }
+
+ return windowInsets;
+ }
+
public FolderIcon getFolderIcon() {
return mFolderIcon;
}
@@ -421,9 +465,9 @@
Collections.sort(children, ITEM_POS_COMPARATOR);
updateItemLocationsInDatabaseBatch(true);
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) getLayoutParams();
if (lp == null) {
- lp = new DragLayer.LayoutParams(0, 0);
+ lp = new BaseDragLayer.LayoutParams(0, 0);
lp.customPosition = true;
setLayoutParams(lp);
}
@@ -477,13 +521,14 @@
/**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
- * @param launcher The main activity.
+ * @param activityContext The main ActivityContext in which to inflate this Folder. It must also
+ * be an instance or ContextWrapper around the Launcher activity context.
*
* @return A new UserFolder.
*/
@SuppressLint("InflateParams")
- static Folder fromXml(Launcher launcher) {
- return (Folder) launcher.getLayoutInflater()
+ static <T extends Context & ActivityContext> Folder fromXml(T activityContext) {
+ return (Folder) LayoutInflater.from(activityContext).cloneInContext(activityContext)
.inflate(R.layout.user_folder_icon_normalized, null);
}
@@ -561,16 +606,7 @@
* is played.
*/
private void animateOpen(List<WorkspaceItemInfo> items, int pageNo) {
- animateOpen(items, pageNo, false);
- }
-
- /**
- * Opens the user folder described by the specified tag. The opening of the folder
- * is animated relative to the specified View. If the View is null, no animation
- * is played.
- */
- private void animateOpen(List<WorkspaceItemInfo> items, int pageNo, boolean skipUserEventLog) {
- Folder openFolder = getOpen(mLauncher);
+ Folder openFolder = getOpen(mActivityContext);
if (openFolder != null && openFolder != this) {
// Close any open folder before opening a folder.
openFolder.close(true);
@@ -583,7 +619,7 @@
mIsOpen = true;
- DragLayer dragLayer = mLauncher.getDragLayer();
+ BaseDragLayer dragLayer = mActivityContext.getDragLayer();
// Just verify that the folder hasn't already been added to the DragLayer.
// There was a one-off crash where the folder had a parent already.
if (getParent() == null) {
@@ -619,14 +655,6 @@
mState = STATE_OPEN;
announceAccessibilityChanges();
- if (!skipUserEventLog) {
- mLauncher.getUserEventDispatcher().logActionOnItem(
- LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.Action.Direction.NONE,
- LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
- }
-
-
mContent.setFocusOnFirstChild();
}
});
@@ -705,7 +733,7 @@
// Notify the accessibility manager that this folder "window" has disappeared and no
// longer occludes the workspace items
- mLauncher.getDragLayer().sendAccessibilityEvent(
+ mActivityContext.getDragLayer().sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
@@ -720,11 +748,17 @@
a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ if (Utilities.ATLEAST_R) {
+ setWindowInsetsAnimationCallback(null);
+ }
mIsAnimatingClosed = true;
}
@Override
public void onAnimationEnd(Animator animation) {
+ if (Utilities.ATLEAST_R && mFolderWindowInsetsAnimationCallback != null) {
+ setWindowInsetsAnimationCallback(mFolderWindowInsetsAnimationCallback);
+ }
closeComplete(true);
announceAccessibilityChanges();
mIsAnimatingClosed = false;
@@ -747,7 +781,7 @@
private void closeComplete(boolean wasAnimated) {
// TODO: Clear all active animations.
- DragLayer parent = (DragLayer) getParent();
+ BaseDragLayer parent = (BaseDragLayer) getParent();
if (parent != null) {
parent.removeView(this);
}
@@ -986,7 +1020,7 @@
private void updateItemLocationsInDatabaseBatch(boolean isBind) {
FolderGridOrganizer verifier = new FolderGridOrganizer(
- mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+ mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo);
ArrayList<ItemInfo> items = new ArrayList<>();
int total = mInfo.contents.size();
@@ -1023,10 +1057,8 @@
}
private void centerAboutIcon() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
-
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- DragLayer parent = mLauncher.getDragLayer();
+ BaseDragLayer.LayoutParams lp = (BaseDragLayer.LayoutParams) getLayoutParams();
+ BaseDragLayer parent = mActivityContext.getDragLayer();
int width = getFolderWidth();
int height = getFolderHeight();
@@ -1036,38 +1068,13 @@
int centeredLeft = centerX - width / 2;
int centeredTop = centerY - height / 2;
- // We need to bound the folder to the currently visible workspace area
- if (mLauncher.getStateManager().getState().overviewUi) {
- parent.getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(), sTempRect);
- } else {
- mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
- }
- int left = Math.min(Math.max(sTempRect.left, centeredLeft),
- sTempRect.right- width);
- int top = Math.min(Math.max(sTempRect.top, centeredTop),
- sTempRect.bottom - height);
-
- int distFromEdgeOfScreen = mLauncher.getWorkspace().getPaddingLeft() + getPaddingLeft();
-
- if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) {
- // Center the folder if it is very close to being centered anyway, by virtue of
- // filling the majority of the viewport. ie. remove it from the uncanny valley
- // of centeredness.
- left = (grid.availableWidthPx - width) / 2;
- } else if (width >= sTempRect.width()) {
- // If the folder doesn't fit within the bounds, center it about the desired bounds
- left = sTempRect.left + (sTempRect.width() - width) / 2;
- }
- if (height >= sTempRect.height()) {
- // Folder height is greater than page height, center on page
- top = sTempRect.top + (sTempRect.height() - height) / 2;
- } else {
- // Folder height is less than page height, so bound it to the absolute open folder
- // bounds if necessary
- Rect folderBounds = grid.getAbsoluteOpenFolderBounds();
- left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width));
- top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height));
- }
+ sTempRect.set(mActivityContext.getFolderBoundingBox());
+ int left = Utilities.boundToRange(centeredLeft, sTempRect.left, sTempRect.right - width);
+ int top = Utilities.boundToRange(centeredTop, sTempRect.top, sTempRect.bottom - height);
+ int[] inOutPosition = new int[] {left, top};
+ mActivityContext.updateOpenFolderPosition(inOutPosition, sTempRect, width, height);
+ left = inOutPosition[0];
+ top = inOutPosition[1];
int folderPivotX = width / 2 + (centeredLeft - left);
int folderPivotY = height / 2 + (centeredTop - top);
@@ -1081,7 +1088,7 @@
}
protected int getContentAreaHeight() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
+ DeviceProfile grid = mActivityContext.getDeviceProfile();
int maxContentAreaHeight = grid.availableHeightPx - grid.getTotalWorkspacePadding().y
- mFooterHeight;
int height = Math.min(maxContentAreaHeight,
@@ -1183,7 +1190,10 @@
newIcon.requestFocus();
}
if (finalItem != null) {
- mLauncher.folderConvertedToItem(mFolderIcon.getFolder(), finalItem);
+ StatsLogger logger = mStatsLogManager.logger().withItemInfo(finalItem);
+ mDragController.getLogInstanceId().map(logger::withInstanceId)
+ .orElse(logger)
+ .log(LAUNCHER_FOLDER_CONVERTED_TO_ICON);
}
}
}
@@ -1356,7 +1366,7 @@
@Override
public void onAdd(WorkspaceItemInfo item, int rank) {
FolderGridOrganizer verifier = new FolderGridOrganizer(
- mLauncher.getDeviceProfile().inv).setFolderInfo(mInfo);
+ mActivityContext.getDeviceProfile().inv).setFolderInfo(mInfo);
verifier.updateRankAndPos(item, rank);
mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
item.cellY);
@@ -1368,10 +1378,10 @@
mItemsInvalidated = true;
}
- public void onRemove(WorkspaceItemInfo item) {
+ @Override
+ public void onRemove(List<WorkspaceItemInfo> items) {
mItemsInvalidated = true;
- View v = getViewForInfo(item);
- mContent.removeItem(v);
+ items.stream().map(this::getViewForInfo).forEach(mContent::removeItem);
if (mState == STATE_ANIMATING) {
mRearrangeOnClose = true;
} else {
@@ -1468,7 +1478,6 @@
}
statsLogger.log(LAUNCHER_FOLDER_LABEL_UPDATED);
- logFolderLabelState(mFromLabelState, toLabelState);
mFolderName.dispatchBackKey();
}
}
@@ -1481,27 +1490,6 @@
outRect.right += mScrollAreaOffset;
}
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, LauncherLogProto.Target child,
- ArrayList<LauncherLogProto.Target> targets) {
- child.gridX = childInfo.cellX;
- child.gridY = childInfo.cellY;
- child.pageIndex = mContent.getCurrentPage();
-
- LauncherLogProto.Target target = newContainerTarget(LauncherLogProto.ContainerType.FOLDER);
- target.pageIndex = mInfo.screenId;
- target.gridX = mInfo.cellX;
- target.gridY = mInfo.cellY;
- targets.add(target);
-
- // continue to parent
- if (mInfo.container == CONTAINER_HOTSEAT) {
- mLauncher.getHotseat().fillInLogContainerData(mInfo, target, targets);
- } else {
- mLauncher.getWorkspace().fillInLogContainerData(mInfo, target, targets);
- }
- }
-
private class OnScrollHintListener implements OnAlarmListener {
private final DragObject mDragObject;
@@ -1585,19 +1573,8 @@
/**
* Returns a folder which is already open or null
*/
- public static Folder getOpen(Launcher launcher) {
- return getOpenView(launcher, TYPE_FOLDER);
- }
-
- @Override
- public void logActionCommand(int command) {
- mLauncher.getUserEventDispatcher().logActionCommand(
- command, getFolderIcon(), getLogContainerType());
- }
-
- @Override
- public int getLogContainerType() {
- return LauncherLogProto.ContainerType.FOLDER;
+ public static Folder getOpen(ActivityContext activityContext) {
+ return getOpenView(activityContext, TYPE_FOLDER);
}
/**
@@ -1616,7 +1593,7 @@
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- DragLayer dl = mLauncher.getDragLayer();
+ BaseDragLayer dl = (BaseDragLayer) getParent();
if (isEditingName()) {
if (!dl.isEventOverView(mFolderName, ev)) {
@@ -1631,8 +1608,7 @@
return true;
}
} else {
- mLauncher.getUserEventDispatcher().logActionTapOutside(
- newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
+ // TODO: add ww log if need to gather tap outside to close folder
close(true);
return true;
}
@@ -1641,6 +1617,11 @@
return false;
}
+ @Override
+ public boolean canInterceptEventsInSystemGestureRegion() {
+ return true;
+ }
+
/**
* Alternative to using {@link #getClipToOutline()} as it only works with derivatives of
* rounded rect.
@@ -1667,14 +1648,63 @@
return mContent;
}
- /**
- * Logs current folder label info.
- *
- * @deprecated This method is only used for log validation and soon will be removed.
- */
- @Deprecated
- public void logFolderLabelState(FromState fromState, ToState toState) {
- mLauncher.getUserEventDispatcher()
- .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
+ /** Returns the height of the current folder's bottom edge from the bottom of the screen. */
+ private int getHeightFromBottom() {
+ BaseDragLayer.LayoutParams layoutParams = (BaseDragLayer.LayoutParams) getLayoutParams();
+ int folderBottomPx = layoutParams.y + layoutParams.height;
+ int windowBottomPx = mActivityContext.getDeviceProfile().heightPx;
+
+ return windowBottomPx - folderBottomPx;
+ }
+
+ /** Callback that animates a folder sliding up above the ime. */
+ @RequiresApi(api = Build.VERSION_CODES.R)
+ private static class FolderWindowInsetsAnimationCallback
+ extends WindowInsetsAnimation.Callback {
+
+ private final Folder mFolder;
+ float mFolderTranslationStart;
+ float mFolderTranslationEnd;
+
+ FolderWindowInsetsAnimationCallback(int dispatchMode, Folder folder) {
+ super(dispatchMode);
+
+ mFolder = folder;
+ }
+
+ @Override
+ public void onPrepare(@NonNull WindowInsetsAnimation animation) {
+ mFolderTranslationStart = mFolder.getTranslationY();
+ }
+
+ @NonNull
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ @NonNull WindowInsetsAnimation animation,
+ @NonNull WindowInsetsAnimation.Bounds bounds) {
+ mFolderTranslationEnd = mFolder.getTranslationY();
+
+ mFolder.setTranslationY(mFolderTranslationStart);
+
+ return super.onStart(animation, bounds);
+ }
+
+ @NonNull
+ @Override
+ public WindowInsets onProgress(@NonNull WindowInsets windowInsets,
+ @NonNull List<WindowInsetsAnimation> list) {
+ if (list.size() == 0) {
+ mFolder.setTranslationY(0);
+
+ return windowInsets;
+ }
+ float progress = list.get(0).getInterpolatedFraction();
+
+ mFolder.setTranslationY(
+ Utilities.mapRange(progress, mFolderTranslationStart, mFolderTranslationEnd));
+
+ return windowInsets;
+ }
+
}
}
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 3d72b49..feb528c 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -39,14 +39,13 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PropertyResetListener;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.BaseDragLayer;
import java.util.List;
@@ -69,7 +68,6 @@
private PreviewBackground mPreviewBackground;
private Context mContext;
- private Launcher mLauncher;
private final boolean mIsOpening;
@@ -92,8 +90,7 @@
mPreviewBackground = mFolderIcon.mBackground;
mContext = folder.getContext();
- mLauncher = folder.mLauncher;
- mPreviewVerifier = new FolderGridOrganizer(mLauncher.getDeviceProfile().inv);
+ mPreviewVerifier = new FolderGridOrganizer(folder.mActivityContext.getDeviceProfile().inv);
mIsOpening = isOpening;
@@ -114,14 +111,15 @@
* Prepares the Folder for animating between open / closed states.
*/
public AnimatorSet getAnimator() {
- final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+ final BaseDragLayer.LayoutParams lp =
+ (BaseDragLayer.LayoutParams) mFolder.getLayoutParams();
mFolderIcon.getPreviewItemManager().recomputePreviewDrawingParams();
ClippedFolderIconLayoutRule rule = mFolderIcon.getLayoutRule();
final List<BubbleTextView> itemsInPreview = getPreviewIconsOnPage(0);
// Match position of the FolderIcon
final Rect folderIconPos = new Rect();
- float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+ float scaleRelativeToDragLayer = mFolder.mActivityContext.getDragLayer()
.getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
int scaledRadius = mPreviewBackground.getScaledRadius();
float initialSize = (scaledRadius * 2) * scaleRelativeToDragLayer;
@@ -326,7 +324,9 @@
final int previewPosX =
(int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale);
- final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY) / folderScale);
+ final float paddingTop = btv.getPaddingTop() * iconScale;
+ final int previewPosY = (int) ((mTmpParams.transY + previewItemOffsetY - paddingTop)
+ / folderScale);
final float xDistance = previewPosX - btvLp.x;
final float yDistance = previewPosY - btvLp.y;
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 32d061c..6b02021 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -166,17 +166,19 @@
mDotParams = new DotRenderer.DrawParams();
}
- public static FolderIcon inflateFolderAndIcon(int resId, Launcher launcher, ViewGroup group,
- FolderInfo folderInfo) {
- Folder folder = Folder.fromXml(launcher);
- folder.setDragController(launcher.getDragController());
+ public static <T extends Context & ActivityContext> FolderIcon inflateFolderAndIcon(int resId,
+ T activityContext, ViewGroup group, FolderInfo folderInfo) {
+ Folder folder = Folder.fromXml(activityContext);
+ folder.setDragController(folder.mLauncher.getDragController());
- FolderIcon icon = inflateIcon(resId, launcher, group, folderInfo);
+ FolderIcon icon = inflateIcon(resId, activityContext, group, folderInfo);
folder.setFolderIcon(icon);
folder.bind(folderInfo);
icon.setFolder(folder);
- icon.setOnFocusChangeListener(launcher.getFocusHandler());
+ icon.setOnFocusChangeListener(folder.mLauncher.getFocusHandler());
+ icon.mBackground.paddingY = icon.isInHotseat()
+ ? 0 : activityContext.getDeviceProfile().cellYPaddingPx;
return icon;
}
@@ -199,7 +201,7 @@
icon.mFolderName.setText(folderInfo.title);
icon.mFolderName.setCompoundDrawablePadding(0);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
- lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
+ lp.topMargin = grid.cellYPaddingPx + grid.iconSizePx + grid.iconDrawablePaddingPx;
icon.setTag(folderInfo);
icon.setOnClickListener(ItemClickHandler.INSTANCE);
@@ -218,6 +220,7 @@
icon.setAccessibilityDelegate(activity.getAccessibilityDelegate());
+ icon.mBackground.paddingY = icon.isInHotseat() ? 0 : grid.cellYPaddingPx;
icon.mPreviewVerifier = new FolderGridOrganizer(activity.getDeviceProfile().inv);
icon.mPreviewVerifier.setFolderInfo(folderInfo);
icon.updatePreviewItems(false);
@@ -478,7 +481,6 @@
// event is assumed to be folder creation on the server side.
.withEditText(newTitle.toString())
.log(LAUNCHER_FOLDER_AUTO_LABELED);
- mFolder.logFolderLabelState(fromState, ToState.TO_SUGGESTION0);
}
@@ -580,6 +582,7 @@
public void setFolderBackground(PreviewBackground bg) {
mBackground = bg;
mBackground.setInvalidateDelegate(this);
+ mBackground.paddingY = isInHotseat() ? 0 : mActivity.getDeviceProfile().cellYPaddingPx;
}
@Override
@@ -696,9 +699,9 @@
}
@Override
- public void onRemove(WorkspaceItemInfo item) {
+ public void onRemove(List<WorkspaceItemInfo> items) {
boolean wasDotted = mDotInfo.hasDot();
- mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
+ items.stream().map(mActivity::getDotInfoForItem).forEach(mDotInfo::subtractDotInfo);
boolean isDotted = mDotInfo.hasDot();
updateDotScale(wasDotted, isDotted);
setContentDescription(getAccessiblityTitle(mInfo.title));
@@ -746,21 +749,19 @@
mInfo.removeListener(mFolder);
}
+ private boolean isInHotseat() {
+ return mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ }
+
public void clearLeaveBehindIfExists() {
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
- if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- CellLayout cl = (CellLayout) getParent().getParent();
- cl.clearFolderLeaveBehind();
+ if (getParent() instanceof FolderIconParent) {
+ ((FolderIconParent) getParent()).clearFolderLeaveBehind(this);
}
}
public void drawLeaveBehindIfExists() {
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
- // While the folder is open, the position of the icon cannot change.
- lp.canReorder = false;
- if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- CellLayout cl = (CellLayout) getParent().getParent();
- cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+ if (getParent() instanceof FolderIconParent) {
+ ((FolderIconParent) getParent()).drawFolderLeaveBehindForIcon(this);
}
}
@@ -829,4 +830,19 @@
MAX_NUM_ITEMS_IN_PREVIEW);
}
}
+
+ /**
+ * Interface that provides callbacks to a parent ViewGroup that hosts this FolderIcon.
+ */
+ public interface FolderIconParent {
+ /**
+ * Tells the FolderIconParent to draw a "leave-behind" when the Folder is open and leaving a
+ * gap where the FolderIcon would be when the Folder is closed.
+ */
+ void drawFolderLeaveBehindForIcon(FolderIcon child);
+ /**
+ * Tells the FolderIconParent to stop drawing the "leave-behind" as the Folder is closed.
+ */
+ void clearFolderLeaveBehind(FolderIcon child);
+ }
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index a08dd30..0235dfa 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -36,7 +36,6 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
@@ -193,7 +192,7 @@
int pageNo = rank / mOrganizer.getMaxItemsPerPage();
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
- lp.setXY(mOrganizer.getPosForRank(rank));
+ lp.setCellXY(mOrganizer.getPosForRank(rank));
getPageAt(pageNo).addViewToCellLayout(view, -1, item.getViewId(), lp, true);
}
@@ -230,7 +229,7 @@
}
private CellLayout createAndAddNewPage() {
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ DeviceProfile grid = mFolder.mActivityContext.getDeviceProfile();
CellLayout page = mViewCache.getView(R.layout.folder_page, getContext(), this);
page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
@@ -306,7 +305,7 @@
if (v != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
ItemInfo info = (ItemInfo) v.getTag();
- lp.setXY(mOrganizer.getPosForRank(rank));
+ lp.setCellXY(mOrganizer.getPosForRank(rank));
currentPage.addViewToCellLayout(v, -1, info.getViewId(), lp, true);
if (mOrganizer.isItemInPreview(rank) && v instanceof BubbleTextView) {
@@ -624,7 +623,7 @@
@Override
protected boolean canScroll(float absVScroll, float absHScroll) {
- return AbstractFloatingView.getTopOpenViewWithType(mFolder.mLauncher,
+ return AbstractFloatingView.getTopOpenViewWithType(mFolder.mActivityContext,
TYPE_ALL & ~TYPE_FOLDER) == null;
}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 27b906b..767fffe 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -74,6 +74,7 @@
int previewSize;
int basePreviewOffsetX;
int basePreviewOffsetY;
+ int paddingY;
private CellLayout mDrawingDelegate;
@@ -157,7 +158,7 @@
previewSize = grid.folderIconSizePx;
basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
- basePreviewOffsetY = topPadding + grid.folderIconOffsetYPx;
+ basePreviewOffsetY = paddingY + topPadding + grid.folderIconOffsetYPx;
// Stroke width is 1dp
mStrokeWidth = context.getResources().getDisplayMetrics().density;
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 7f8a15c..9ae7faf 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -39,6 +39,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
@@ -394,9 +395,10 @@
}
private void setDrawable(PreviewItemDrawingParams p, WorkspaceItemInfo item) {
- if (item.hasPromiseIconUi()) {
+ if (item.hasPromiseIconUi() || (item.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
PreloadIconDrawable drawable = newPendingIcon(mContext, item);
- drawable.setLevel(item.getInstallProgress());
+ drawable.setLevel(item.getProgressLevel());
p.drawable = drawable;
} else {
p.drawable = newIcon(mContext, item);
diff --git a/src/com/android/launcher3/graphics/IconShape.java b/src/com/android/launcher3/graphics/IconShape.java
index 4369385..b208a40 100644
--- a/src/com/android/launcher3/graphics/IconShape.java
+++ b/src/com/android/launcher3/graphics/IconShape.java
@@ -43,8 +43,9 @@
import android.view.View;
import android.view.ViewOutlineProvider;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.IconNormalizer;
@@ -59,8 +60,6 @@
import java.util.ArrayList;
import java.util.List;
-import androidx.annotation.Nullable;
-
/**
* Abstract representation of the shape of an icon shape
*/
@@ -381,9 +380,6 @@
* Initializes the shape which is closest to the {@link AdaptiveIconDrawable}
*/
public static void init(Context context) {
- if (!Utilities.ATLEAST_OREO) {
- return;
- }
pickBestShape(context);
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 7b769b8..5e9b179 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -19,6 +19,7 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static android.view.View.VISIBLE;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
@@ -57,13 +58,11 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceLayoutManager;
-import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
@@ -72,11 +71,12 @@
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.model.LoaderTask;
+import com.android.launcher3.model.ModelDelegate;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -90,10 +90,12 @@
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -116,7 +118,8 @@
* 4) Measure and draw the view on a canvas
*/
@TargetApi(Build.VERSION_CODES.O)
-public class LauncherPreviewRenderer {
+public class LauncherPreviewRenderer extends ContextThemeWrapper
+ implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
private static final String TAG = "LauncherPreviewRenderer";
@@ -210,10 +213,14 @@
private final DeviceProfile mDp;
private final boolean mMigrated;
private final Rect mInsets;
-
private final WorkspaceItemInfo mWorkspaceItemInfo;
+ private final LayoutInflater mHomeElementInflater;
+ private final InsettableFrameLayout mRootView;
+ private final Hotseat mHotseat;
+ private final CellLayout mWorkspace;
public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
+ super(context, R.style.AppTheme);
mUiHandler = new Handler(Looper.getMainLooper());
mContext = context;
mIdp = idp;
@@ -238,291 +245,277 @@
mWorkspaceItemInfo.intent = new Intent();
mWorkspaceItemInfo.contentDescription = mWorkspaceItemInfo.title =
context.getString(R.string.label_application);
+
+ mHomeElementInflater = LayoutInflater.from(
+ new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
+ mHomeElementInflater.setFactory2(this);
+
+ mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
+ R.layout.launcher_preview_layout, null, false);
+ mRootView.setInsets(mInsets);
+ measureView(mRootView, mDp.widthPx, mDp.heightPx);
+
+ mHotseat = mRootView.findViewById(R.id.hotseat);
+ mHotseat.resetLayout(false);
+
+ mWorkspace = mRootView.findViewById(R.id.workspace);
+ mWorkspace.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
+ mDp.workspacePadding.top,
+ mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
+ mDp.workspacePadding.bottom);
}
/** Populate preview and render it. */
public View getRenderedView() {
- MainThreadRenderer renderer = new MainThreadRenderer(mContext);
- renderer.populate();
- return renderer.mRootView;
+ populate();
+ return mRootView;
}
- private class MainThreadRenderer extends ContextThemeWrapper
- implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
+ public boolean shouldShowRealLauncherPreview() {
+ return ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get();
+ }
- private final LayoutInflater mHomeElementInflater;
- private final InsettableFrameLayout mRootView;
+ public boolean shouldShowQsb() {
+ return FeatureFlags.QSB_ON_FIRST_SCREEN;
+ }
- private final Hotseat mHotseat;
- private final CellLayout mWorkspace;
+ @Override
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ if ("TextClock".equals(name)) {
+ // Workaround for TextClock accessing handler for unregistering ticker.
+ return new TextClock(context, attrs) {
- MainThreadRenderer(Context context) {
- super(context, R.style.AppTheme);
-
- mHomeElementInflater = LayoutInflater.from(
- new ContextThemeWrapper(this, R.style.HomeScreenElementTheme));
- mHomeElementInflater.setFactory2(this);
-
- mRootView = (InsettableFrameLayout) mHomeElementInflater.inflate(
- R.layout.launcher_preview_layout, null, false);
- mRootView.setInsets(mInsets);
- measureView(mRootView, mDp.widthPx, mDp.heightPx);
-
- mHotseat = mRootView.findViewById(R.id.hotseat);
- mHotseat.resetLayout(false);
-
- mWorkspace = mRootView.findViewById(R.id.workspace);
- mWorkspace.setPadding(mDp.workspacePadding.left + mDp.cellLayoutPaddingLeftRightPx,
- mDp.workspacePadding.top,
- mDp.workspacePadding.right + mDp.cellLayoutPaddingLeftRightPx,
- mDp.workspacePadding.bottom);
+ @Override
+ public Handler getHandler() {
+ return mUiHandler;
+ }
+ };
+ } else if (!"fragment".equals(name)) {
+ return null;
}
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- if ("TextClock".equals(name)) {
- // Workaround for TextClock accessing handler for unregistering ticker.
- return new TextClock(context, attrs) {
+ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PreviewFragment);
+ FragmentWithPreview f = (FragmentWithPreview) Fragment.instantiate(
+ context, ta.getString(R.styleable.PreviewFragment_android_name));
+ f.enterPreviewMode(context);
+ f.onInit(null);
- @Override
- public Handler getHandler() {
- return mUiHandler;
- }
- };
- } else if (!"fragment".equals(name)) {
- return null;
- }
+ View view = f.onCreateView(LayoutInflater.from(context), (ViewGroup) parent, null);
+ view.setId(ta.getInt(R.styleable.PreviewFragment_android_id, View.NO_ID));
+ return view;
+ }
- TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PreviewFragment);
- FragmentWithPreview f = (FragmentWithPreview) Fragment.instantiate(
- context, ta.getString(R.styleable.PreviewFragment_android_name));
- f.enterPreviewMode(context);
- f.onInit(null);
+ @Override
+ public View onCreateView(String name, Context context, AttributeSet attrs) {
+ return onCreateView(null, name, context, attrs);
+ }
- View view = f.onCreateView(LayoutInflater.from(context), (ViewGroup) parent, null);
- view.setId(ta.getInt(R.styleable.PreviewFragment_android_id, View.NO_ID));
- return view;
+ @Override
+ public BaseDragLayer getDragLayer() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public DeviceProfile getDeviceProfile() {
+ return mDp;
+ }
+
+ @Override
+ public Hotseat getHotseat() {
+ return mHotseat;
+ }
+
+ @Override
+ public CellLayout getScreenWithId(int screenId) {
+ return mWorkspace;
+ }
+
+ private void inflateAndAddIcon(WorkspaceItemInfo info) {
+ BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
+ R.layout.app_icon, mWorkspace, false);
+ icon.applyFromWorkspaceItem(info);
+ addInScreenFromBind(icon, info);
+ }
+
+ private void inflateAndAddFolder(FolderInfo info) {
+ FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
+ info);
+ addInScreenFromBind(folderIcon, info);
+ }
+
+ private void inflateAndAddWidgets(
+ LauncherAppWidgetInfo info,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ if (widgetProviderInfoMap == null) {
+ return;
}
-
- @Override
- public View onCreateView(String name, Context context, AttributeSet attrs) {
- return onCreateView(null, name, context, attrs);
+ AppWidgetProviderInfo providerInfo = widgetProviderInfoMap.get(
+ new ComponentKey(info.providerName, info.user));
+ if (providerInfo == null) {
+ return;
}
+ inflateAndAddWidgets(info, LauncherAppWidgetProviderInfo.fromProviderInfo(
+ getApplicationContext(), providerInfo));
+ }
- @Override
- public BaseDragLayer getDragLayer() {
- throw new UnsupportedOperationException();
+ private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
+ WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
+ info.providerName);
+ if (widgetItem == null) {
+ return;
}
+ inflateAndAddWidgets(info, widgetItem.widgetInfo);
+ }
- @Override
- public DeviceProfile getDeviceProfile() {
- return mDp;
- }
+ private void inflateAndAddWidgets(
+ LauncherAppWidgetInfo info, LauncherAppWidgetProviderInfo providerInfo) {
+ AppWidgetHostView view = new AppWidgetHostView(mContext);
+ view.setAppWidget(-1, providerInfo);
+ view.updateAppWidget(null);
+ view.setTag(info);
+ addInScreenFromBind(view, info);
+ }
- @Override
- public Hotseat getHotseat() {
- return mHotseat;
- }
-
- @Override
- public CellLayout getScreenWithId(int screenId) {
- return mWorkspace;
- }
-
- private void inflateAndAddIcon(WorkspaceItemInfo info) {
- BubbleTextView icon = (BubbleTextView) mHomeElementInflater.inflate(
- R.layout.app_icon, mWorkspace, false);
- icon.applyFromWorkspaceItem(info);
- addInScreenFromBind(icon, info);
- }
-
- private void inflateAndAddFolder(FolderInfo info) {
- FolderIcon folderIcon = FolderIcon.inflateIcon(R.layout.folder_icon, this, mWorkspace,
- info);
- addInScreenFromBind(folderIcon, info);
- }
-
- private void inflateAndAddWidgets(LauncherAppWidgetInfo info,
- Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
- if (widgetProviderInfoMap == null) {
- return;
- }
- AppWidgetProviderInfo providerInfo = widgetProviderInfoMap.get(
- new ComponentKey(info.providerName, info.user));
- if (providerInfo == null) {
- return;
- }
- inflateAndAddWidgets(info, LauncherAppWidgetProviderInfo.fromProviderInfo(
- getApplicationContext(), providerInfo));
- }
-
- private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
- WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
- info.providerName);
- if (widgetItem == null) {
- return;
- }
- inflateAndAddWidgets(info, widgetItem.widgetInfo);
- }
-
- private void inflateAndAddWidgets(LauncherAppWidgetInfo info,
- LauncherAppWidgetProviderInfo providerInfo) {
- AppWidgetHostView view = new AppWidgetHostView(mContext);
- view.setAppWidget(-1, providerInfo);
- view.updateAppWidget(null);
- view.setTag(info);
+ private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
+ View view = PredictedAppIconInflater.inflate(mHomeElementInflater, mWorkspace, info);
+ if (view != null) {
addInScreenFromBind(view, info);
}
+ }
- private void inflateAndAddPredictedIcon(WorkspaceItemInfo info) {
- View view = PredictedAppIconInflater.inflate(mHomeElementInflater, mWorkspace, info);
- if (view != null) {
- addInScreenFromBind(view, info);
- }
+ private void dispatchVisibilityAggregated(View view, boolean isVisible) {
+ // Similar to View.dispatchVisibilityAggregated implementation.
+ final boolean thisVisible = view.getVisibility() == VISIBLE;
+ if (thisVisible || !isVisible) {
+ view.onVisibilityAggregated(isVisible);
}
- private void dispatchVisibilityAggregated(View view, boolean isVisible) {
- // Similar to View.dispatchVisibilityAggregated implementation.
- final boolean thisVisible = view.getVisibility() == VISIBLE;
- if (thisVisible || !isVisible) {
- view.onVisibilityAggregated(isVisible);
- }
+ if (view instanceof ViewGroup) {
+ isVisible = thisVisible && isVisible;
+ ViewGroup vg = (ViewGroup) view;
+ int count = vg.getChildCount();
- if (view instanceof ViewGroup) {
- isVisible = thisVisible && isVisible;
- ViewGroup vg = (ViewGroup) view;
- int count = vg.getChildCount();
-
- for (int i = 0; i < count; i++) {
- dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
- }
+ for (int i = 0; i < count; i++) {
+ dispatchVisibilityAggregated(vg.getChildAt(i), isVisible);
}
}
+ }
- private void populate() {
- if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
- WorkspaceFetcher fetcher;
- PreviewContext previewContext = null;
- if (mMigrated) {
- previewContext = new PreviewContext(mContext, mIdp);
- LauncherAppState appForPreview = new LauncherAppState(
- previewContext, null /* iconCacheFileName */);
- fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
- MODEL_EXECUTOR.execute(fetcher);
- } else {
- fetcher = new WorkspaceItemsInfoFetcher();
- LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
- (LauncherModel.ModelUpdateTask) fetcher);
- }
- WorkspaceResult workspaceResult = fetcher.get();
- if (previewContext != null) {
- previewContext.onDestroy();
- }
-
- if (workspaceResult == null) {
- return;
- }
-
- // Separate the items that are on the current screen, and all the other remaining
- // items
- ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
- ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
-
- filterCurrentWorkspaceItems(0 /* currentScreenId */,
- workspaceResult.mWorkspaceItems, currentWorkspaceItems,
- otherWorkspaceItems);
- filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
- currentAppWidgets, otherAppWidgets);
- sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
-
- for (ItemInfo itemInfo : currentWorkspaceItems) {
- switch (itemInfo.itemType) {
- case Favorites.ITEM_TYPE_APPLICATION:
- case Favorites.ITEM_TYPE_SHORTCUT:
- case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- inflateAndAddFolder((FolderInfo) itemInfo);
- break;
- default:
- break;
- }
- }
- for (ItemInfo itemInfo : currentAppWidgets) {
- switch (itemInfo.itemType) {
- case Favorites.ITEM_TYPE_APPWIDGET:
- case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- if (mMigrated) {
- inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
- workspaceResult.mWidgetProvidersMap);
- } else {
- inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
- workspaceResult.mWidgetsModel);
- }
- break;
- default:
- break;
- }
- }
-
- IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
- mIdp.numHotseatIcons);
- int count = Math.min(ranks.size(), workspaceResult.mCachedPredictedItems.size());
- for (int i = 0; i < count; i++) {
- AppInfo appInfo = workspaceResult.mCachedPredictedItems.get(i);
- int rank = ranks.get(i);
- WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(appInfo);
- itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
- itemInfo.rank = rank;
- itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
- itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
- itemInfo.screenId = rank;
- inflateAndAddPredictedIcon(itemInfo);
- }
+ private void populate() {
+ if (shouldShowRealLauncherPreview()) {
+ WorkspaceFetcher fetcher;
+ PreviewContext previewContext = null;
+ if (mMigrated) {
+ previewContext = new PreviewContext(mContext, mIdp);
+ LauncherAppState appForPreview = new LauncherAppState(
+ previewContext, null /* iconCacheFileName */);
+ fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
+ MODEL_EXECUTOR.execute(fetcher);
} else {
- // Add hotseat icons
- for (int i = 0; i < mIdp.numHotseatIcons; i++) {
- WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
- info.container = Favorites.CONTAINER_HOTSEAT;
- info.screenId = i;
- inflateAndAddIcon(info);
- }
- // Add workspace icons
- for (int i = 0; i < mIdp.numColumns; i++) {
- WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
- info.container = Favorites.CONTAINER_DESKTOP;
- info.screenId = 0;
- info.cellX = i;
- info.cellY = mIdp.numRows - 1;
- inflateAndAddIcon(info);
- }
+ fetcher = new WorkspaceItemsInfoFetcher();
+ LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
+ (LauncherModel.ModelUpdateTask) fetcher);
+ }
+ WorkspaceResult workspaceResult = fetcher.get();
+ if (previewContext != null) {
+ previewContext.onDestroy();
}
- // Add first page QSB
- if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
- View qsb = mHomeElementInflater.inflate(
- R.layout.search_container_workspace, mWorkspace, false);
- CellLayout.LayoutParams lp =
- new CellLayout.LayoutParams(0, 0, mWorkspace.getCountX(), 1);
- lp.canReorder = false;
- mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
+ if (workspaceResult == null) {
+ return;
}
- // Setup search view
- SearchUiManager searchUiManager =
- mRootView.findViewById(R.id.search_container_all_apps);
- mRootView.findViewById(R.id.apps_view).setTranslationY(
- mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets));
-
- measureView(mRootView, mDp.widthPx, mDp.heightPx);
- dispatchVisibilityAggregated(mRootView, true);
- measureView(mRootView, mDp.widthPx, mDp.heightPx);
- // Additional measure for views which use auto text size API
- measureView(mRootView, mDp.widthPx, mDp.heightPx);
+ // Separate the items that are on the current screen, and the other remaining items.
+ ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
+ ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
+ filterCurrentWorkspaceItems(0 /* currentScreenId */,
+ workspaceResult.mWorkspaceItems, currentWorkspaceItems,
+ otherWorkspaceItems);
+ filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
+ currentAppWidgets, otherAppWidgets);
+ sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
+ for (ItemInfo itemInfo : currentWorkspaceItems) {
+ switch (itemInfo.itemType) {
+ case Favorites.ITEM_TYPE_APPLICATION:
+ case Favorites.ITEM_TYPE_SHORTCUT:
+ case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
+ break;
+ case Favorites.ITEM_TYPE_FOLDER:
+ inflateAndAddFolder((FolderInfo) itemInfo);
+ break;
+ default:
+ break;
+ }
+ }
+ for (ItemInfo itemInfo : currentAppWidgets) {
+ switch (itemInfo.itemType) {
+ case Favorites.ITEM_TYPE_APPWIDGET:
+ case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+ if (mMigrated) {
+ inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+ workspaceResult.mWidgetProvidersMap);
+ } else {
+ inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+ workspaceResult.mWidgetsModel);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
+ mIdp.numHotseatIcons);
+ List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
+ ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
+ int count = Math.min(ranks.size(), predictions.size());
+ for (int i = 0; i < count; i++) {
+ int rank = ranks.get(i);
+ WorkspaceItemInfo itemInfo =
+ new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i));
+ itemInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+ itemInfo.rank = rank;
+ itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
+ itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
+ itemInfo.screenId = rank;
+ inflateAndAddPredictedIcon(itemInfo);
+ }
+ } else {
+ // Add hotseat icons
+ for (int i = 0; i < mIdp.numHotseatIcons; i++) {
+ WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
+ info.container = Favorites.CONTAINER_HOTSEAT;
+ info.screenId = i;
+ inflateAndAddIcon(info);
+ }
+ // Add workspace icons
+ for (int i = 0; i < mIdp.numColumns; i++) {
+ WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
+ info.container = Favorites.CONTAINER_DESKTOP;
+ info.screenId = 0;
+ info.cellX = i;
+ info.cellY = mIdp.numRows - 1;
+ inflateAndAddIcon(info);
+ }
}
+
+ // Add first page QSB
+ if (shouldShowQsb()) {
+ View qsb = mHomeElementInflater.inflate(
+ R.layout.search_container_workspace, mWorkspace, false);
+ CellLayout.LayoutParams lp =
+ new CellLayout.LayoutParams(0, 0, mWorkspace.getCountX(), 1);
+ lp.canReorder = false;
+ mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
+ }
+
+ measureView(mRootView, mDp.widthPx, mDp.heightPx);
+ dispatchVisibilityAggregated(mRootView, true);
+ measureView(mRootView, mDp.widthPx, mDp.heightPx);
+ // Additional measure for views which use auto text size API
+ measureView(mRootView, mDp.widthPx, mDp.heightPx);
}
private static void measureView(View view, int width, int height) {
@@ -568,8 +561,7 @@
return null;
}
- return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
- mBgDataModel.cachedPredictedItems, mBgDataModel.widgetsModel, null);
+ return new WorkspaceResult(mBgDataModel, mBgDataModel.widgetsModel, null);
}
}
@@ -579,7 +571,7 @@
private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
WorkspaceItemsInfoFromPreviewFetcher(LauncherAppState app) {
- super(app, null, new BgDataModel(), null);
+ super(app, null, new BgDataModel(), new ModelDelegate(), null);
}
@Override
@@ -593,14 +585,13 @@
}
@Override
- public WorkspaceResult call() throws Exception {
+ public WorkspaceResult call() {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts, LauncherSettings.Favorites.PREVIEW_CONTENT_URI,
LauncherSettings.Favorites.SCREEN + " = 0 or "
- + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
- return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
- mBgDataModel.cachedPredictedItems, null, mWidgetProvidersMap);
+ + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT);
+ return new WorkspaceResult(mBgDataModel, null, mWidgetProvidersMap);
}
}
@@ -620,19 +611,20 @@
private static class WorkspaceResult {
private final ArrayList<ItemInfo> mWorkspaceItems;
private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
- private final ArrayList<AppInfo> mCachedPredictedItems;
+ private final FixedContainerItems mHotseatPredictions;
private final WidgetsModel mWidgetsModel;
private final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
- private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
- ArrayList<LauncherAppWidgetInfo> appWidgets,
- ArrayList<AppInfo> cachedPredictedItems, WidgetsModel widgetsModel,
+ private WorkspaceResult(BgDataModel dataModel,
+ WidgetsModel widgetsModel,
Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
- mWorkspaceItems = workspaceItems;
- mAppWidgets = appWidgets;
- mCachedPredictedItems = cachedPredictedItems;
- mWidgetsModel = widgetsModel;
- mWidgetProvidersMap = widgetProviderInfoMap;
+ synchronized (dataModel) {
+ mWorkspaceItems = dataModel.workspaceItems;
+ mAppWidgets = dataModel.appWidgets;
+ mHotseatPredictions = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
+ mWidgetsModel = widgetsModel;
+ mWidgetProvidersMap = widgetProviderInfoMap;
+ }
}
}
}
diff --git a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
index d347e8f..b6d25c4 100644
--- a/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PlaceHolderIconDrawable.java
@@ -19,10 +19,19 @@
import static com.android.launcher3.graphics.IconShape.getShapePath;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import androidx.core.graphics.ColorUtils;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R;
@@ -53,4 +62,27 @@
canvas.drawPath(mProgressPath, mPaint);
canvas.restoreToCount(saveCount);
}
+
+ /** Updates this placeholder to {@code newIcon} with animation. */
+ public void animateIconUpdate(Drawable newIcon) {
+ int placeholderColor = mPaint.getColor();
+ int originalAlpha = Color.alpha(placeholderColor);
+
+ ValueAnimator iconUpdateAnimation = ValueAnimator.ofInt(originalAlpha, 0);
+ iconUpdateAnimation.setDuration(375);
+ iconUpdateAnimation.addUpdateListener(valueAnimator -> {
+ int newAlpha = (int) valueAnimator.getAnimatedValue();
+ int newColor = ColorUtils.setAlphaComponent(placeholderColor, newAlpha);
+
+ newIcon.setColorFilter(new PorterDuffColorFilter(newColor, PorterDuff.Mode.SRC_ATOP));
+ });
+ iconUpdateAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ newIcon.setColorFilter(null);
+ }
+ });
+ iconUpdateAnimation.start();
+ }
+
}
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index e85b056..ce824df 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -24,6 +24,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -34,10 +35,12 @@
import android.util.Pair;
import android.util.Property;
import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.Themes;
import java.lang.ref.WeakReference;
@@ -77,6 +80,9 @@
private static final SparseArray<WeakReference<Pair<Path, Bitmap>>> sShadowCache =
new SparseArray<>();
+ private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
+ private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
+
private final Matrix mTmpMatrix = new Matrix();
private final PathMeasure mPathMeasure = new PathMeasure();
@@ -91,6 +97,9 @@
private Bitmap mShadowBitmap;
private final int mIndicatorColor;
+ private final int mSystemAccentColor;
+ private final int mSystemBackgroundColor;
+ private final boolean mIsDarkMode;
private int mTrackAlpha;
private float mTrackLength;
@@ -104,7 +113,23 @@
private ObjectAnimator mCurrentAnim;
+ private boolean mIsStartable;
+
public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
+ this(
+ info,
+ IconPalette.getPreloadProgressColor(context, info.bitmap.color),
+ getPreloadColors(context),
+ (context.getResources().getConfiguration().uiMode
+ & Configuration.UI_MODE_NIGHT_MASK
+ & Configuration.UI_MODE_NIGHT_YES) != 0) /* isDarkMode */;
+ }
+
+ public PreloadIconDrawable(
+ ItemInfoWithIcon info,
+ int indicatorColor,
+ int[] preloadColors,
+ boolean isDarkMode) {
super(info.bitmap);
mItem = info;
mShapePath = getShapePath();
@@ -114,9 +139,14 @@
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
- mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
+ mIndicatorColor = indicatorColor;
- setInternalProgress(0);
+ mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
+ mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
+ mIsDarkMode = isDarkMode;
+
+ setInternalProgress(info.getProgressLevel());
+ setIsStartable(info.isAppStartable());
}
@Override
@@ -142,7 +172,7 @@
}
private Bitmap getShadowBitmap(int width, int height, float shadowRadius) {
- int key = (width << 16) | height;
+ 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;
@@ -151,8 +181,9 @@
}
shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(shadow);
- mProgressPaint.setShadowLayer(shadowRadius, 0, 0, COLOR_SHADOW);
- mProgressPaint.setColor(COLOR_TRACK);
+ 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();
@@ -170,7 +201,7 @@
}
// Draw track.
- mProgressPaint.setColor(mIndicatorColor);
+ mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor);
mProgressPaint.setAlpha(mTrackAlpha);
if (mShadowBitmap != null) {
canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
@@ -209,6 +240,14 @@
return !mRanFinishAnimation;
}
+ /** Sets whether this icon should display the startable app UI. */
+ public void setIsStartable(boolean isStartable) {
+ if (mIsStartable != isStartable) {
+ mIsStartable = isStartable;
+ setIsDisabled(!isStartable);
+ }
+ }
+
private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) {
if (mCurrentAnim != null) {
mCurrentAnim.cancel();
@@ -266,14 +305,12 @@
mIconScale = SMALL_SCALE;
mScaledTrackPath.reset();
mTrackAlpha = MAX_PAINT_ALPHA;
- setIsDisabled(true);
}
if (progress < 1 && progress > 0) {
mPathMeasure.getSegment(0, progress * mTrackLength, mScaledProgressPath, true);
mIconScale = SMALL_SCALE;
mTrackAlpha = MAX_PAINT_ALPHA;
- setIsDisabled(true);
} else if (progress >= 1) {
setIsDisabled(mItem.isDisabled());
mScaledTrackPath.set(mScaledProgressPath);
@@ -291,10 +328,73 @@
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);
+
+ return preloadColors;
+ }
+
/**
* Returns a FastBitmapDrawable with the icon.
*/
public static PreloadIconDrawable newPendingIcon(Context context, ItemInfoWithIcon info) {
return new PreloadIconDrawable(info, context);
}
+
+ @Override
+ public ConstantState getConstantState() {
+ return new PreloadIconConstantState(
+ mBitmap,
+ mIconColor,
+ !mItem.isAppStartable(),
+ mItem,
+ mIndicatorColor,
+ new int[] {mSystemAccentColor, mSystemBackgroundColor},
+ mIsDarkMode);
+ }
+
+ protected static class PreloadIconConstantState extends FastBitmapConstantState {
+
+ protected final ItemInfoWithIcon mInfo;
+ protected final int mIndicatorColor;
+ protected final int[] mPreloadColors;
+ protected final boolean mIsDarkMode;
+ protected final int mLevel;
+
+ public PreloadIconConstantState(
+ Bitmap bitmap,
+ int iconColor,
+ boolean isDisabled,
+ ItemInfoWithIcon info,
+ int indicatorColor,
+ int[] preloadColors,
+ boolean isDarkMode) {
+ super(bitmap, iconColor, isDisabled);
+ mInfo = info;
+ mIndicatorColor = indicatorColor;
+ mPreloadColors = preloadColors;
+ mIsDarkMode = isDarkMode;
+ mLevel = info.getProgressLevel();
+ }
+
+ @Override
+ public PreloadIconDrawable newDrawable() {
+ return new PreloadIconDrawable(
+ mInfo,
+ mIndicatorColor,
+ mPreloadColors,
+ mIsDarkMode);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
}
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/SysUiScrim.java
similarity index 83%
rename from src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
rename to src/com/android/launcher3/graphics/SysUiScrim.java
index 7b7ab5e..d9c648b 100644
--- a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
+++ b/src/com/android/launcher3/graphics/SysUiScrim.java
@@ -34,7 +34,6 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Region;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
@@ -44,41 +43,39 @@
import androidx.core.graphics.ColorUtils;
-import com.android.launcher3.CellLayout;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.util.Themes;
/**
* View scrim which draws behind hotseat and workspace
*/
-public class WorkspaceAndHotseatScrim extends Scrim {
+public class SysUiScrim extends Scrim {
- public static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_PROGRESS =
- new FloatProperty<WorkspaceAndHotseatScrim>("sysUiProgress") {
+ public static final FloatProperty<SysUiScrim> SYSUI_PROGRESS =
+ new FloatProperty<SysUiScrim>("sysUiProgress") {
@Override
- public Float get(WorkspaceAndHotseatScrim scrim) {
+ public Float get(SysUiScrim scrim) {
return scrim.mSysUiProgress;
}
@Override
- public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
+ public void setValue(SysUiScrim scrim, float value) {
scrim.setSysUiProgress(value);
}
};
- private static final FloatProperty<WorkspaceAndHotseatScrim> SYSUI_ANIM_MULTIPLIER =
- new FloatProperty<WorkspaceAndHotseatScrim>("sysUiAnimMultiplier") {
+ private static final FloatProperty<SysUiScrim> SYSUI_ANIM_MULTIPLIER =
+ new FloatProperty<SysUiScrim>("sysUiAnimMultiplier") {
@Override
- public Float get(WorkspaceAndHotseatScrim scrim) {
+ public Float get(SysUiScrim scrim) {
return scrim.mSysUiAnimMultiplier;
}
@Override
- public void setValue(WorkspaceAndHotseatScrim scrim, float value) {
+ public void setValue(SysUiScrim scrim, float value) {
scrim.mSysUiAnimMultiplier = value;
scrim.reapplySysUiAlpha();
}
@@ -108,10 +105,6 @@
private static final int ALPHA_MASK_BITMAP_DP = 200;
private static final int ALPHA_MASK_WIDTH_DP = 2;
- private final Rect mHighlightRect = new Rect();
-
- private Workspace mWorkspace;
-
private boolean mDrawTopScrim, mDrawBottomScrim;
private final RectF mFinalMaskRect = new RectF();
@@ -127,9 +120,8 @@
private boolean mAnimateScrimOnNextDraw = false;
private float mSysUiAnimMultiplier = 1;
- public WorkspaceAndHotseatScrim(View view) {
+ public SysUiScrim(View view) {
super(view);
-
mMaskHeight = ResourceUtils.pxFromDp(ALPHA_MASK_BITMAP_DP,
view.getResources().getDisplayMetrics());
mTopScrim = Themes.getAttrDrawable(view.getContext(), R.attr.workspaceStatusBarScrim);
@@ -139,28 +131,10 @@
onExtractedColorsChanged(mWallpaperColorInfo);
}
- public void setWorkspace(Workspace workspace) {
- mWorkspace = workspace;
- }
-
+ /**
+ * Draw the top and bottom scrims
+ */
public void draw(Canvas canvas) {
- // Draw the background below children.
- if (mScrimAlpha > 0) {
- // Update the scroll position first to ensure scrim cutout is in the right place.
- mWorkspace.computeScrollWithoutInvalidation();
- CellLayout currCellLayout = mWorkspace.getCurrentDragOverlappingLayout();
- canvas.save();
- if (currCellLayout != null && currCellLayout != mLauncher.getHotseat()) {
- // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
- mLauncher.getDragLayer()
- .getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
- canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
- }
-
- super.draw(canvas);
- canvas.restore();
- }
-
if (!mHideSysUiScrim) {
if (mSysUiProgress <= 0) {
mAnimateScrimOnNextDraw = false;
@@ -247,6 +221,11 @@
super.onExtractedColorsChanged(wallpaperColorInfo);
}
+ /**
+ * Set the width and height of the view being scrimmed
+ * @param w
+ * @param h
+ */
public void setSize(int w, int h) {
if (mTopScrim != null) {
mTopScrim.setBounds(0, 0, w, h);
diff --git a/src/com/android/launcher3/graphics/WorkspaceDragScrim.java b/src/com/android/launcher3/graphics/WorkspaceDragScrim.java
new file mode 100644
index 0000000..d8dc563
--- /dev/null
+++ b/src/com/android/launcher3/graphics/WorkspaceDragScrim.java
@@ -0,0 +1,74 @@
+/*
+ * 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.graphics;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Workspace;
+
+/**
+ * Scrim drawn during SpringLoaded State (ie. Drag and Drop). Darkens the workspace except for
+ * the focused CellLayout.
+ */
+public class WorkspaceDragScrim extends Scrim {
+
+ private final Rect mHighlightRect = new Rect();
+
+ private Workspace mWorkspace;
+
+ public WorkspaceDragScrim(View view) {
+ super(view);
+ onExtractedColorsChanged(mWallpaperColorInfo);
+ }
+
+ /**
+ * Set the workspace that this scrim is acting on
+ * @param workspace
+ */
+ public void setWorkspace(Workspace workspace) {
+ mWorkspace = workspace;
+ mWorkspace.setWorkspaceDragScrim(this);
+ }
+
+ /**
+ * Cut out the focused paged of the Workspace and then draw the scrim
+ * @param canvas
+ */
+ public void draw(Canvas canvas) {
+ // Draw the background below children.
+ if (mScrimAlpha > 0) {
+ // Update the scroll position first to ensure scrim cutout is in the right place.
+ mWorkspace.computeScrollWithoutInvalidation();
+ CellLayout currCellLayout = mWorkspace.getCurrentDragOverlappingLayout();
+ canvas.save();
+ if (currCellLayout != null && currCellLayout != mLauncher.getHotseat()) {
+ // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
+ mLauncher.getDragLayer()
+ .getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
+ canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
+ }
+
+ super.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+}
diff --git a/src/com/android/launcher3/icons/ClockDrawableWrapper.java b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
index b7dd092..1bd252b 100644
--- a/src/com/android/launcher3/icons/ClockDrawableWrapper.java
+++ b/src/com/android/launcher3/icons/ClockDrawableWrapper.java
@@ -308,7 +308,7 @@
return new ClockConstantState(mInfo, isDisabled());
}
- private static class ClockConstantState extends MyConstantState {
+ private static class ClockConstantState extends FastBitmapConstantState {
private final ClockBitmapInfo mInfo;
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index a20bec2..61f2c2a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -31,7 +31,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
-import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -134,7 +133,7 @@
* Fetches high-res icon for the provided ItemInfo and updates the caller when done.
* @return a request ID that can be used to cancel the request.
*/
- public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+ public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
final ItemInfoWithIcon info) {
Preconditions.assertUIThread();
if (mPendingIconRequestCount <= 0) {
@@ -142,20 +141,18 @@
}
mPendingIconRequestCount ++;
- IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
- @Override
- public void run() {
- if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
- getTitleAndIcon(info, false);
- } else if (info instanceof PackageItemInfo) {
- getTitleAndIconForApp((PackageItemInfo) info, false);
- }
- MAIN_EXECUTOR.execute(() -> {
- caller.reapplyItemInfo(info);
- onEnd();
- });
- }
- };
+ HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
+ () -> {
+ if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+ getTitleAndIcon(info, false);
+ } else if (info instanceof PackageItemInfo) {
+ getTitleAndIconForApp((PackageItemInfo) info, false);
+ }
+ return info;
+ },
+ MAIN_EXECUTOR,
+ caller::reapplyItemInfo,
+ this::onIconRequestEnd);
Utilities.postAsyncCallback(mWorkerHandler, request);
return request;
}
@@ -260,14 +257,6 @@
}
/**
- * Fill in info with the icon and label for deep shortcut.
- */
- public synchronized CacheEntry getDeepShortcutTitleAndIcon(ShortcutInfo info) {
- return cacheLocked(ShortcutKey.fromInfo(info).componentName, info.getUserHandle(),
- () -> info, mShortcutCachingLogic, false, false);
- }
-
- /**
* Fill in {@param info} with the icon and label. If the
* corresponding activity is not found, it reverts to the package icon.
*/
@@ -295,7 +284,7 @@
/**
* Fill in {@param mWorkspaceItemInfo} with the icon and label for {@param info}
*/
- private synchronized void getTitleAndIcon(
+ public synchronized void getTitleAndIcon(
@NonNull ItemInfoWithIcon infoInOut,
@NonNull Supplier<LauncherActivityInfo> activityInfoProvider,
boolean usePkgIcon, boolean useLowResIcon) {
@@ -344,12 +333,6 @@
return super.getEntryFromDB(cacheKey, entry, lowRes);
}
- public static abstract class IconLoadRequest extends HandlerRunnable {
- IconLoadRequest(Handler handler, Runnable endRunnable) {
- super(handler, endRunnable);
- }
- }
-
/**
* Interface for receiving itemInfo with high-res icon.
*/
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
deleted file mode 100644
index 800598e..0000000
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.keyboard;
-
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-import android.widget.PopupMenu;
-import android.widget.PopupMenu.OnMenuItemClickListener;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.popup.PopupContainerWithArrow;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Handles showing a popup menu with available custom actions for a launcher icon.
- * This allows exposing various custom actions using keyboard shortcuts.
- */
-public class CustomActionsPopup implements OnMenuItemClickListener {
-
- private final Launcher mLauncher;
- private final LauncherAccessibilityDelegate mDelegate;
- private final View mIcon;
-
- public CustomActionsPopup(Launcher launcher, View icon) {
- mLauncher = launcher;
- mIcon = icon;
- PopupContainerWithArrow container = PopupContainerWithArrow.getOpen(launcher);
- if (container != null) {
- mDelegate = container.getAccessibilityDelegate();
- } else {
- mDelegate = launcher.getAccessibilityDelegate();
- }
- }
-
- private List<AccessibilityAction> getActionList() {
- if (mIcon == null || !(mIcon.getTag() instanceof ItemInfo)) {
- return Collections.EMPTY_LIST;
- }
-
- AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
- mDelegate.addSupportedActions(mIcon, info, true);
- List<AccessibilityAction> result = new ArrayList<>(info.getActionList());
- info.recycle();
- return result;
- }
-
- public boolean canShow() {
- return !getActionList().isEmpty();
- }
-
- public boolean show() {
- List<AccessibilityAction> actions = getActionList();
- if (actions.isEmpty()) {
- return false;
- }
-
- PopupMenu popup = new PopupMenu(mLauncher, mIcon);
- popup.setOnMenuItemClickListener(this);
- Menu menu = popup.getMenu();
- for (AccessibilityAction action : actions) {
- menu.add(Menu.NONE, action.getId(), Menu.NONE, action.getLabel());
- }
- popup.show();
- return true;
- }
-
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- return mDelegate.performAction(mIcon, (ItemInfo) mIcon.getTag(), menuItem.getItemId());
- }
-}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index c50189c..83003ff 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -16,18 +16,7 @@
package com.android.launcher3.keyboard;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.util.Property;
import android.view.View;
import android.view.View.OnFocusChangeListener;
@@ -36,205 +25,21 @@
/**
* A helper class to draw background of a focused view.
*/
-public abstract class FocusIndicatorHelper implements
- OnFocusChangeListener, AnimatorUpdateListener {
-
- private static final float MIN_VISIBLE_ALPHA = 0.2f;
- private static final long ANIM_DURATION = 150;
-
- public static final Property<FocusIndicatorHelper, Float> ALPHA =
- new Property<FocusIndicatorHelper, Float>(Float.TYPE, "alpha") {
- @Override
- public void set(FocusIndicatorHelper object, Float value) {
- object.setAlpha(value);
- }
-
- @Override
- public Float get(FocusIndicatorHelper object) {
- return object.mAlpha;
- }
- };
-
- public static final Property<FocusIndicatorHelper, Float> SHIFT =
- new Property<FocusIndicatorHelper, Float>(
- Float.TYPE, "shift") {
-
- @Override
- public void set(FocusIndicatorHelper object, Float value) {
- object.mShift = value;
- }
-
- @Override
- public Float get(FocusIndicatorHelper object) {
- return object.mShift;
- }
- };
-
- private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
- private static final Rect sTempRect1 = new Rect();
- private static final Rect sTempRect2 = new Rect();
-
- private final View mContainer;
- private final Paint mPaint;
- private final int mMaxAlpha;
-
- private final Rect mDirtyRect = new Rect();
- private boolean mIsDirty = false;
-
- private View mLastFocusedView;
-
- private View mCurrentView;
- private View mTargetView;
- /**
- * The fraction indicating the position of the focusRect between {@link #mCurrentView}
- * & {@link #mTargetView}
- */
- private float mShift;
-
- private ObjectAnimator mCurrentAnimation;
- private float mAlpha;
+public abstract class FocusIndicatorHelper extends ItemFocusIndicatorHelper<View>
+ implements OnFocusChangeListener {
public FocusIndicatorHelper(View container) {
- mContainer = container;
-
- mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- int color = container.getResources().getColor(R.color.focused_background);
- mMaxAlpha = Color.alpha(color);
- mPaint.setColor(0xFF000000 | color);
-
- setAlpha(0);
- mShift = 0;
- }
-
- protected void setAlpha(float alpha) {
- mAlpha = alpha;
- mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- invalidateDirty();
- }
-
- protected void invalidateDirty() {
- if (mIsDirty) {
- mContainer.invalidate(mDirtyRect);
- mIsDirty = false;
- }
-
- Rect newRect = getDrawRect();
- if (newRect != null) {
- mContainer.invalidate(newRect);
- }
- }
-
- public void draw(Canvas c) {
- if (mAlpha > 0) {
- Rect newRect = getDrawRect();
- if (newRect != null) {
- mDirtyRect.set(newRect);
- c.drawRect(mDirtyRect, mPaint);
- mIsDirty = true;
- }
- }
- }
-
- private Rect getDrawRect() {
- if (mCurrentView != null && mCurrentView.isAttachedToWindow()) {
- viewToRect(mCurrentView, sTempRect1);
-
- if (mShift > 0 && mTargetView != null) {
- viewToRect(mTargetView, sTempRect2);
- return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
- } else {
- return sTempRect1;
- }
- }
- return null;
+ super(container, container.getResources().getColor(R.color.focused_background));
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus) {
- endCurrentAnimation();
-
- if (mAlpha > MIN_VISIBLE_ALPHA) {
- mTargetView = v;
-
- mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(ALPHA, 1),
- PropertyValuesHolder.ofFloat(SHIFT, 1));
- mCurrentAnimation.addListener(new ViewSetListener(v, true));
- } else {
- setCurrentView(v);
-
- mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(ALPHA, 1));
- }
-
- mLastFocusedView = v;
- } else {
- if (mLastFocusedView == v) {
- mLastFocusedView = null;
- endCurrentAnimation();
- mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
- PropertyValuesHolder.ofFloat(ALPHA, 0));
- mCurrentAnimation.addListener(new ViewSetListener(null, false));
- }
- }
-
- // invalidate once
- invalidateDirty();
-
- mLastFocusedView = hasFocus ? v : null;
- if (mCurrentAnimation != null) {
- mCurrentAnimation.addUpdateListener(this);
- mCurrentAnimation.setDuration(ANIM_DURATION).start();
- }
+ changeFocus(v, hasFocus);
}
- protected void endCurrentAnimation() {
- if (mCurrentAnimation != null) {
- mCurrentAnimation.cancel();
- mCurrentAnimation = null;
- }
- }
-
- protected void setCurrentView(View v) {
- mCurrentView = v;
- mShift = 0;
- mTargetView = null;
- }
-
- /**
- * Gets the position of {@param v} relative to {@link #mContainer}.
- */
- public abstract void viewToRect(View v, Rect outRect);
-
- private class ViewSetListener extends AnimatorListenerAdapter {
- private final View mViewToSet;
- private final boolean mCallOnCancel;
- private boolean mCalled = false;
-
- public ViewSetListener(View v, boolean callOnCancel) {
- mViewToSet = v;
- mCallOnCancel = callOnCancel;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (!mCallOnCancel) {
- mCalled = true;
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCalled) {
- setCurrentView(mViewToSet);
- mCalled = true;
- }
- }
+ @Override
+ protected boolean shouldDraw(View item) {
+ return item.isAttachedToWindow();
}
/**
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
new file mode 100644
index 0000000..57fab2d
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -0,0 +1,252 @@
+/*
+ * 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.keyboard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.FloatProperty;
+import android.view.View;
+
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.Themes;
+
+/**
+ * A helper class to draw background of a focused item.
+ * @param <T> Item type
+ */
+public abstract class ItemFocusIndicatorHelper<T> implements AnimatorUpdateListener {
+
+ private static final float MIN_VISIBLE_ALPHA = 0.2f;
+ private static final long ANIM_DURATION = 150;
+
+ public static final FloatProperty<ItemFocusIndicatorHelper> ALPHA =
+ new FloatProperty<ItemFocusIndicatorHelper>("alpha") {
+
+ @Override
+ public void setValue(ItemFocusIndicatorHelper object, float value) {
+ object.setAlpha(value);
+ }
+
+ @Override
+ public Float get(ItemFocusIndicatorHelper object) {
+ return object.mAlpha;
+ }
+ };
+
+ public static final FloatProperty<ItemFocusIndicatorHelper> SHIFT =
+ new FloatProperty<ItemFocusIndicatorHelper>("shift") {
+
+ @Override
+ public void setValue(ItemFocusIndicatorHelper object, float value) {
+ object.mShift = value;
+ }
+
+ @Override
+ public Float get(ItemFocusIndicatorHelper object) {
+ return object.mShift;
+ }
+ };
+
+ private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+ private static final Rect sTempRect1 = new Rect();
+ private static final Rect sTempRect2 = new Rect();
+
+ private final View mContainer;
+ protected final Paint mPaint;
+ private final int mMaxAlpha;
+
+ private final Rect mDirtyRect = new Rect();
+ private boolean mIsDirty = false;
+
+ private T mLastFocusedItem;
+
+ private T mCurrentItem;
+ private T mTargetItem;
+ /**
+ * The fraction indicating the position of the focusRect between {@link #mCurrentItem}
+ * & {@link #mTargetItem}
+ */
+ private float mShift;
+
+ private ObjectAnimator mCurrentAnimation;
+ private float mAlpha;
+ private float mRadius;
+
+ public ItemFocusIndicatorHelper(View container, int color) {
+ mContainer = container;
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mMaxAlpha = Color.alpha(color);
+ mPaint.setColor(0xFF000000 | color);
+
+ setAlpha(0);
+ mShift = 0;
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ mRadius = Themes.getDialogCornerRadius(container.getContext());
+ }
+ }
+
+ protected void setAlpha(float alpha) {
+ mAlpha = alpha;
+ mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ invalidateDirty();
+ }
+
+ protected void invalidateDirty() {
+ if (mIsDirty) {
+ mContainer.invalidate(mDirtyRect);
+ mIsDirty = false;
+ }
+
+ Rect newRect = getDrawRect();
+ if (newRect != null) {
+ mContainer.invalidate(newRect);
+ }
+ }
+
+ /**
+ * Draws the indicator on the canvas
+ */
+ public void draw(Canvas c) {
+ if (mAlpha <= 0) return;
+
+ Rect newRect = getDrawRect();
+ if (newRect != null) {
+ mDirtyRect.set(newRect);
+ c.drawRoundRect((float) mDirtyRect.left, (float) mDirtyRect.top,
+ (float) mDirtyRect.right, (float) mDirtyRect.bottom,
+ mRadius, mRadius, mPaint);
+ mIsDirty = true;
+ }
+ }
+
+ private Rect getDrawRect() {
+ if (mCurrentItem != null && shouldDraw(mCurrentItem)) {
+ viewToRect(mCurrentItem, sTempRect1);
+
+ if (mShift > 0 && mTargetItem != null) {
+ viewToRect(mTargetItem, sTempRect2);
+ return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
+ } else {
+ return sTempRect1;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the provided item is valid
+ */
+ protected boolean shouldDraw(T item) {
+ return true;
+ }
+
+ protected void changeFocus(T item, boolean hasFocus) {
+ if (hasFocus) {
+ endCurrentAnimation();
+
+ if (mAlpha > MIN_VISIBLE_ALPHA) {
+ mTargetItem = item;
+
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 1),
+ PropertyValuesHolder.ofFloat(SHIFT, 1));
+ mCurrentAnimation.addListener(new ViewSetListener(item, true));
+ } else {
+ setCurrentItem(item);
+
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 1));
+ }
+
+ mLastFocusedItem = item;
+ } else {
+ if (mLastFocusedItem == item) {
+ mLastFocusedItem = null;
+ endCurrentAnimation();
+ mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+ PropertyValuesHolder.ofFloat(ALPHA, 0));
+ mCurrentAnimation.addListener(new ViewSetListener(null, false));
+ }
+ }
+
+ // invalidate once
+ invalidateDirty();
+
+ mLastFocusedItem = hasFocus ? item : null;
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.addUpdateListener(this);
+ mCurrentAnimation.setDuration(ANIM_DURATION).start();
+ }
+ }
+
+ protected void endCurrentAnimation() {
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+ }
+
+ protected void setCurrentItem(T item) {
+ mCurrentItem = item;
+ mShift = 0;
+ mTargetItem = null;
+ }
+
+ /**
+ * Gets the position of the item relative to {@link #mContainer}.
+ */
+ public abstract void viewToRect(T item, Rect outRect);
+
+ private class ViewSetListener extends AnimatorListenerAdapter {
+ private final T mItemToSet;
+ private final boolean mCallOnCancel;
+ private boolean mCalled = false;
+
+ ViewSetListener(T item, boolean callOnCancel) {
+ mItemToSet = item;
+ mCallOnCancel = callOnCancel;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (!mCallOnCancel) {
+ mCalled = true;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mCalled) {
+ setCurrentItem(mItemToSet);
+ mCalled = true;
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
new file mode 100644
index 0000000..a6c897f
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/KeyboardDragAndDropView.java
@@ -0,0 +1,342 @@
+/*
+ * 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.keyboard;
+
+import static android.app.Activity.DEFAULT_KEYS_SEARCH_LOCAL;
+
+import static com.android.launcher3.LauncherState.SPRING_LOADED;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.TextView;
+
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.util.Themes;
+
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.function.ToIntBiFunction;
+import java.util.function.ToIntFunction;
+
+/**
+ * A floating view to allow keyboard navigation across virtual nodes
+ */
+public class KeyboardDragAndDropView extends AbstractFloatingView
+ implements Insettable, StateListener<LauncherState> {
+
+ private static final long MINOR_AXIS_WEIGHT = 13;
+
+ private final ArrayList<Integer> mIntList = new ArrayList<>();
+ private final ArrayList<DragAndDropAccessibilityDelegate> mDelegates = new ArrayList<>();
+ private final ArrayList<VirtualNodeInfo> mNodes = new ArrayList<>();
+
+ private final Rect mTempRect = new Rect();
+ private final Rect mTempRect2 = new Rect();
+ private final AccessibilityNodeInfoCompat mTempNodeInfo = AccessibilityNodeInfoCompat.obtain();
+
+ private final RectFocusIndicator mFocusIndicator;
+
+ private final Launcher mLauncher;
+ private VirtualNodeInfo mCurrentSelection;
+
+
+ public KeyboardDragAndDropView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public KeyboardDragAndDropView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+ mFocusIndicator = new RectFocusIndicator(this);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ mLauncher.getDragLayer().removeView(this);
+ mLauncher.getStateManager().removeStateListener(this);
+ mLauncher.setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
+ mIsOpen = false;
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_DRAG_DROP_POPUP) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ // Consume all touch
+ return true;
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ setPadding(insets.left, insets.top, insets.right, insets.bottom);
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ if (toState != SPRING_LOADED) {
+ close(false);
+ }
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ if (mCurrentSelection != null) {
+ setCurrentSelection(mCurrentSelection);
+ }
+ }
+
+ private void setCurrentSelection(VirtualNodeInfo nodeInfo) {
+ mCurrentSelection = nodeInfo;
+ ((TextView) findViewById(R.id.label))
+ .setText(nodeInfo.populate(mTempNodeInfo).getContentDescription());
+
+ Rect bounds = new Rect();
+ mTempNodeInfo.getBoundsInParent(bounds);
+ View host = nodeInfo.delegate.getHost();
+ ViewParent parent = host.getParent();
+ if (parent instanceof PagedView) {
+ PagedView pv = (PagedView) parent;
+ int pageIndex = pv.indexOfChild(host);
+
+ pv.setCurrentPage(pageIndex);
+ bounds.offset(pv.getScrollX() - pv.getScrollForPage(pageIndex), 0);
+ }
+ float[] pos = new float[] {bounds.left, bounds.top, bounds.right, bounds.bottom};
+ Utilities.getDescendantCoordRelativeToAncestor(host, mLauncher.getDragLayer(), pos, true);
+
+ new RectF(pos[0], pos[1], pos[2], pos[3]).roundOut(bounds);
+ mFocusIndicator.changeFocus(bounds, true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mFocusIndicator.draw(canvas);
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ VirtualNodeInfo nodeInfo = getNextSelection(direction);
+ if (nodeInfo == null) {
+ return false;
+ }
+ setCurrentSelection(nodeInfo);
+ return true;
+ }
+
+ /**
+ * Focus finding logic:
+ * Collect all virtual nodes in reading order (used for forward and backwards).
+ * Then find the closest view by comparing the distances spatially. Since it is a move
+ * operation. consider all cell sizes to be approximately of the same size.
+ */
+ private VirtualNodeInfo getNextSelection(int direction) {
+ // Collect all virtual nodes
+ mDelegates.clear();
+ mNodes.clear();
+
+ Folder openFolder = Folder.getOpen(mLauncher);
+ PagedView pv = openFolder == null ? mLauncher.getWorkspace() : openFolder.getContent();
+ int count = pv.getPageCount();
+ for (int i = 0; i < count; i++) {
+ mDelegates.add(((CellLayout) pv.getChildAt(i)).getDragAndDropAccessibilityDelegate());
+ }
+ if (openFolder == null) {
+ mDelegates.add(pv.getNextPage() + 1,
+ mLauncher.getHotseat().getDragAndDropAccessibilityDelegate());
+ }
+ mDelegates.forEach(delegate -> {
+ mIntList.clear();
+ delegate.getVisibleVirtualViews(mIntList);
+ mIntList.forEach(id -> mNodes.add(new VirtualNodeInfo(delegate, id)));
+ });
+
+ if (mNodes.isEmpty()) {
+ return null;
+ }
+ int index = mNodes.indexOf(mCurrentSelection);
+ if (mCurrentSelection == null || index < 0) {
+ return null;
+ }
+ int totalNodes = mNodes.size();
+
+ final ToIntBiFunction<Rect, Rect> majorAxis;
+ final ToIntFunction<Rect> minorAxis;
+
+ switch (direction) {
+ case View.FOCUS_RIGHT:
+ majorAxis = (source, dest) -> dest.left - source.left;
+ minorAxis = Rect::centerY;
+ break;
+ case View.FOCUS_LEFT:
+ majorAxis = (source, dest) -> source.left - dest.left;
+ minorAxis = Rect::centerY;
+ break;
+ case View.FOCUS_UP:
+ majorAxis = (source, dest) -> source.top - dest.top;
+ minorAxis = Rect::centerX;
+ break;
+ case View.FOCUS_DOWN:
+ majorAxis = (source, dest) -> dest.top - source.top;
+ minorAxis = Rect::centerX;
+ break;
+ case View.FOCUS_FORWARD:
+ return mNodes.get((index + 1) % totalNodes);
+ case View.FOCUS_BACKWARD:
+ return mNodes.get((index + totalNodes - 1) % totalNodes);
+ default:
+ // Unknown direction
+ return null;
+ }
+ mCurrentSelection.populate(mTempNodeInfo).getBoundsInScreen(mTempRect);
+
+ float minWeight = Float.MAX_VALUE;
+ VirtualNodeInfo match = null;
+ for (int i = 0; i < totalNodes; i++) {
+ VirtualNodeInfo node = mNodes.get(i);
+ node.populate(mTempNodeInfo).getBoundsInScreen(mTempRect2);
+
+ int majorAxisWeight = majorAxis.applyAsInt(mTempRect, mTempRect2);
+ if (majorAxisWeight <= 0) {
+ continue;
+ }
+ int minorAxisWeight = minorAxis.applyAsInt(mTempRect2)
+ - minorAxis.applyAsInt(mTempRect);
+
+ float weight = majorAxisWeight * majorAxisWeight
+ + minorAxisWeight * minorAxisWeight * MINOR_AXIS_WEIGHT;
+ if (weight < minWeight) {
+ minWeight = weight;
+ match = node;
+ }
+ }
+ return match;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_ENTER && mCurrentSelection != null) {
+ mCurrentSelection.delegate.onPerformActionForVirtualView(
+ mCurrentSelection.id, AccessibilityNodeInfoCompat.ACTION_CLICK, null);
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ /**
+ * Shows the keyboard drag popup for the provided view
+ */
+ public void showForIcon(View icon, ItemInfo item, DragOptions dragOptions) {
+ mIsOpen = true;
+ mLauncher.getDragLayer().addView(this);
+ mLauncher.getStateManager().addStateListener(this);
+
+ // Find current selection
+ CellLayout currentParent = (CellLayout) icon.getParent().getParent();
+ float[] iconPos = new float[] {currentParent.getCellWidth() / 2,
+ currentParent.getCellHeight() / 2};
+ Utilities.getDescendantCoordRelativeToAncestor(icon, currentParent, iconPos, false);
+
+ ItemLongClickListener.beginDrag(icon, mLauncher, item, dragOptions);
+
+ DragAndDropAccessibilityDelegate dndDelegate =
+ currentParent.getDragAndDropAccessibilityDelegate();
+ setCurrentSelection(new VirtualNodeInfo(
+ dndDelegate, dndDelegate.getVirtualViewAt(iconPos[0], iconPos[1])));
+
+ mLauncher.setDefaultKeyMode(Activity.DEFAULT_KEYS_DISABLE);
+ requestFocus();
+ }
+
+ private static class VirtualNodeInfo {
+ public final DragAndDropAccessibilityDelegate delegate;
+ public final int id;
+
+ VirtualNodeInfo(DragAndDropAccessibilityDelegate delegate, int id) {
+ this.id = id;
+ this.delegate = delegate;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof VirtualNodeInfo)) {
+ return false;
+ }
+ VirtualNodeInfo that = (VirtualNodeInfo) o;
+ return id == that.id && delegate.equals(that.delegate);
+ }
+
+ public AccessibilityNodeInfoCompat populate(AccessibilityNodeInfoCompat nodeInfo) {
+ delegate.onPopulateNodeForVirtualView(id, nodeInfo);
+ return nodeInfo;
+ }
+
+ public void getBounds(AccessibilityNodeInfoCompat nodeInfo, Rect out) {
+ delegate.onPopulateNodeForVirtualView(id, nodeInfo);
+ nodeInfo.getBoundsInScreen(out);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, delegate);
+ }
+ }
+
+ private static class RectFocusIndicator extends ItemFocusIndicatorHelper<Rect> {
+
+ RectFocusIndicator(View container) {
+ super(container, Themes.getColorAccent(container.getContext()));
+ mPaint.setStrokeWidth(container.getResources()
+ .getDimension(R.dimen.keyboard_drag_stroke_width));
+ mPaint.setStyle(Style.STROKE);
+ }
+
+ @Override
+ public void viewToRect(Rect item, Rect outRect) {
+ outRect.set(item);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 6bc1ecb..cdd0bda 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -10,7 +10,6 @@
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.IOUtils;
import java.io.BufferedReader;
@@ -43,7 +42,7 @@
private static Handler sHandler = null;
private static File sLogsDirectory = null;
- public static final int LOG_DAYS = FeatureFlags.ENABLE_HYBRID_HOTSEAT.get() ? 10 : 4;
+ public static final int LOG_DAYS = 4;
public static void setDir(File logsDir) {
if (ENABLED) {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
deleted file mode 100644
index cd4f034..0000000
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.logging;
-
-import android.util.ArrayMap;
-import android.util.SparseArray;
-import android.view.View;
-
-import com.android.launcher3.ButtonDropTarget;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.InstantAppResolver;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-
-/**
- * Helper methods for logging.
- */
-public class LoggerUtils {
- private static final ArrayMap<Class, SparseArray<String>> sNameCache = new ArrayMap<>();
- private static final String UNKNOWN = "UNKNOWN";
- private static final int DEFAULT_PREDICTED_RANK = 10000;
- private static final String DELIMITER_DOT = "\\.";
-
- public static String getFieldName(int value, Class c) {
- SparseArray<String> cache;
- synchronized (sNameCache) {
- cache = sNameCache.get(c);
- if (cache == null) {
- cache = new SparseArray<>();
- for (Field f : c.getDeclaredFields()) {
- if (f.getType() == int.class && Modifier.isStatic(f.getModifiers())) {
- try {
- f.setAccessible(true);
- cache.put(f.getInt(null), f.getName());
- } catch (IllegalAccessException e) {
- // Ignore
- }
- }
- }
- sNameCache.put(c, cache);
- }
- }
- String result = cache.get(value);
- return result != null ? result : UNKNOWN;
- }
-
- public static Target newItemTarget(int itemType) {
- Target t = newTarget(Target.Type.ITEM);
- t.itemType = itemType;
- return t;
- }
-
- public static Target newItemTarget(View v, InstantAppResolver instantAppResolver) {
- return (v != null) && (v.getTag() instanceof ItemInfo)
- ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver)
- : newTarget(Target.Type.ITEM);
- }
-
- public static Target newItemTarget(ItemInfo info, InstantAppResolver instantAppResolver) {
- Target t = newTarget(Target.Type.ITEM);
- switch (info.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- t.itemType = (instantAppResolver != null && info instanceof AppInfo
- && instantAppResolver.isInstantApp(((AppInfo) info)))
- ? ItemType.WEB_APP
- : ItemType.APP_ICON;
- t.predictedRank = DEFAULT_PREDICTED_RANK;
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- t.itemType = ItemType.SHORTCUT;
- t.predictedRank = DEFAULT_PREDICTED_RANK;
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- t.itemType = ItemType.FOLDER_ICON;
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- t.itemType = ItemType.WIDGET;
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- t.itemType = ItemType.DEEPSHORTCUT;
- t.predictedRank = DEFAULT_PREDICTED_RANK;
- break;
- }
- return t;
- }
-
- public static Target newDropTarget(View v) {
- if (!(v instanceof ButtonDropTarget)) {
- return newTarget(Target.Type.CONTAINER);
- }
- if (v instanceof ButtonDropTarget) {
- return ((ButtonDropTarget) v).getDropTargetForLogging();
- }
- return newTarget(Target.Type.CONTROL);
- }
-
- public static Target newTarget(int targetType, TargetExtension extension) {
- Target t = new Target();
- t.type = targetType;
- t.extension = extension;
- return t;
- }
-
- public static Target newTarget(int targetType) {
- Target t = new Target();
- t.type = targetType;
- return t;
- }
-
- public static Target newControlTarget(int controlType) {
- Target t = newTarget(Target.Type.CONTROL);
- t.controlType = controlType;
- return t;
- }
-
- public static Target newContainerTarget(int containerType) {
- Target t = newTarget(Target.Type.CONTAINER);
- t.containerType = containerType;
- return t;
- }
-
- public static Action newAction(int type) {
- Action a = new Action();
- a.type = type;
- return a;
- }
-
- public static Action newCommandAction(int command) {
- Action a = newAction(Action.Type.COMMAND);
- a.command = command;
- return a;
- }
-
- public static Action newTouchAction(int touch) {
- Action a = newAction(Action.Type.TOUCH);
- a.touch = touch;
- return a;
- }
-
- public static LauncherEvent newLauncherEvent(Action action, Target... srcTargets) {
- LauncherEvent event = new LauncherEvent();
- event.srcTarget = srcTargets;
- event.action = action;
- return event;
- }
-
- /**
- * Creates LauncherEvent using Action and ArrayList of Targets
- */
- public static LauncherEvent newLauncherEvent(Action action, ArrayList<Target> targets) {
- Target[] targetsArray = new Target[targets.size()];
- targets.toArray(targetsArray);
- return newLauncherEvent(action, targetsArray);
- }
-
- /**
- * String conversion for only the helpful parts of {@link Object#toString()} method
- * @param stringToExtract "foo.bar.baz.MyObject@1234"
- * @return "MyObject@1234"
- */
- public static String extractObjectNameAndAddress(String stringToExtract) {
- String[] superStringParts = stringToExtract.split(DELIMITER_DOT);
- if (superStringParts.length == 0) {
- return "";
- }
- return superStringParts[superStringParts.length - 1];
- }
-}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index c4a4c98..c5ffff9 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -15,32 +15,30 @@
*/
package com.android.launcher3.logging;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_CLOSE_DOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_OPEN_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_OVERVIEW_GESTURE;
import android.content.Context;
+import android.view.View;
import androidx.annotation.Nullable;
+import androidx.slice.SliceItem;
import com.android.launcher3.R;
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.userevent.LauncherLogProto;
import com.android.launcher3.util.ResourceBasedOverride;
-import java.util.List;
-
/**
* Handles the user event logging in R+.
*
* <pre>
* All of the event ids are defined here.
- * Most of the methods are dummy methods for Launcher3
+ * Most of the methods are placeholder methods for Launcher3
* Actual call happens only for Launcher variant that implements QuickStep.
* </pre>
*/
@@ -53,41 +51,24 @@
public static final int LAUNCHER_STATE_ALLAPPS = 4;
public static final int LAUNCHER_STATE_UNCHANGED = 5;
+ private InstanceId mInstanceId;
/**
- * Returns proper launcher state enum for {@link StatsLogManager}(to be removed during
- * UserEventDispatcher cleanup)
- */
- public static int containerTypeToAtomState(int containerType) {
- switch (containerType) {
- case LauncherLogProto.ContainerType.ALLAPPS_VALUE:
- return LAUNCHER_STATE_ALLAPPS;
- case LauncherLogProto.ContainerType.OVERVIEW_VALUE:
- return LAUNCHER_STATE_OVERVIEW;
- case LauncherLogProto.ContainerType.WORKSPACE_VALUE:
- return LAUNCHER_STATE_HOME;
- case LauncherLogProto.ContainerType.APP_VALUE:
- return LAUNCHER_STATE_BACKGROUND;
- }
- return LAUNCHER_STATE_UNSPECIFIED;
- }
-
- /**
- * Returns event enum based on the two {@link ContainerType} transition information when swipe
+ * Returns event enum based on the two state transition information when swipe
* gesture happens(to be removed during UserEventDispatcher cleanup).
*/
- public static EventEnum getLauncherAtomEvent(int startContainerType,
- int targetContainerType, EventEnum fallbackEvent) {
- if (startContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()
- && targetContainerType == LauncherLogProto.ContainerType.WORKSPACE.getNumber()) {
+ public static EventEnum getLauncherAtomEvent(int startState,
+ int targetState, EventEnum fallbackEvent) {
+ if (startState == LAUNCHER_STATE_HOME
+ && targetState == LAUNCHER_STATE_HOME) {
return LAUNCHER_HOME_GESTURE;
- } else if (startContainerType != LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()
- && targetContainerType == LauncherLogProto.ContainerType.TASKSWITCHER.getNumber()) {
+ } else if (startState != LAUNCHER_STATE_OVERVIEW
+ && targetState == LAUNCHER_STATE_OVERVIEW) {
return LAUNCHER_OVERVIEW_GESTURE;
- } else if (startContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()
- && targetContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+ } else if (startState != LAUNCHER_STATE_ALLAPPS
+ && targetState == LAUNCHER_STATE_ALLAPPS) {
return LAUNCHER_ALLAPPS_OPEN_UP;
- } else if (startContainerType == LauncherLogProto.ContainerType.ALLAPPS.getNumber()
- && targetContainerType != LauncherLogProto.ContainerType.ALLAPPS.getNumber()) {
+ } else if (startState == LAUNCHER_STATE_ALLAPPS
+ && targetState != LAUNCHER_STATE_ALLAPPS) {
return LAUNCHER_ALLAPPS_CLOSE_DOWN;
}
return fallbackEvent; // TODO fix
@@ -119,9 +100,13 @@
@UiEvent(doc = "User dragged a launcher item")
LAUNCHER_ITEM_DRAG_STARTED(383),
- @UiEvent(doc = "A dragged launcher item is successfully dropped")
+ @UiEvent(doc = "A dragged launcher item is successfully dropped onto workspace, hotseat "
+ + "open folder etc")
LAUNCHER_ITEM_DROP_COMPLETED(385),
+ @UiEvent(doc = "A dragged launcher item is successfully dropped onto a folder icon.")
+ LAUNCHER_ITEM_DROP_COMPLETED_ON_FOLDER_ICON(697),
+
@UiEvent(doc = "A dragged launcher item is successfully dropped on another item "
+ "resulting in a new folder creation")
LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
@@ -321,8 +306,116 @@
@UiEvent(doc = "User tapped on image content in Overview Select mode.")
LAUNCHER_SELECT_MODE_IMAGE(627),
+ @UiEvent(doc = "Activity to add external item was started")
+ LAUNCHER_ADD_EXTERNAL_ITEM_START(641),
+
+ @UiEvent(doc = "Activity to add external item was cancelled")
+ LAUNCHER_ADD_EXTERNAL_ITEM_CANCELLED(642),
+
+ @UiEvent(doc = "Activity to add external item was backed out")
+ LAUNCHER_ADD_EXTERNAL_ITEM_BACK(643),
+
+ @UiEvent(doc = "Item was placed automatically in external item addition flow")
+ LAUNCHER_ADD_EXTERNAL_ITEM_PLACED_AUTOMATICALLY(644),
+
+ @UiEvent(doc = "Item was dragged in external item addition flow")
+ LAUNCHER_ADD_EXTERNAL_ITEM_DRAGGED(645),
+
+ @UiEvent(doc = "A folder was replaced by a single item")
+ LAUNCHER_FOLDER_CONVERTED_TO_ICON(646),
+
+ @UiEvent(doc = "A hotseat prediction item was pinned")
+ LAUNCHER_HOTSEAT_PREDICTION_PINNED(647),
+
+ @UiEvent(doc = "Undo event was tapped.")
+ LAUNCHER_UNDO(648),
+
+ @UiEvent(doc = "Task switcher clear all target was tapped.")
+ LAUNCHER_TASK_CLEAR_ALL(649),
+
+ @UiEvent(doc = "Task preview was long pressed.")
+ LAUNCHER_TASK_PREVIEW_LONGPRESS(650),
+
@UiEvent(doc = "User swiped down on workspace (triggering noti shade to open).")
- LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN(651);
+ LAUNCHER_SWIPE_DOWN_WORKSPACE_NOTISHADE_OPEN(651),
+
+ @UiEvent(doc = "Notification dismissed by swiping right.")
+ LAUNCHER_NOTIFICATION_DISMISSED(652),
+
+ @UiEvent(doc = "Current grid size is changed to 5.")
+ LAUNCHER_GRID_SIZE_5(662),
+
+ @UiEvent(doc = "Current grid size is changed to 4.")
+ LAUNCHER_GRID_SIZE_4(663),
+
+ @UiEvent(doc = "Current grid size is changed to 3.")
+ LAUNCHER_GRID_SIZE_3(664),
+
+ @UiEvent(doc = "Current grid size is changed to 2.")
+ LAUNCHER_GRID_SIZE_2(665),
+
+ @UiEvent(doc = "Launcher entered into AllApps state.")
+ LAUNCHER_ALLAPPS_ENTRY(692),
+
+ @UiEvent(doc = "Launcher exited from AllApps state.")
+ LAUNCHER_ALLAPPS_EXIT(693),
+
+ @UiEvent(doc = "User closed the AllApps keyboard.")
+ LAUNCHER_ALLAPPS_KEYBOARD_CLOSED(694),
+
+ @UiEvent(doc = "User switched to AllApps Main/Personal tab by swiping left.")
+ LAUNCHER_ALLAPPS_SWIPE_TO_PERSONAL_TAB(695),
+
+ @UiEvent(doc = "User switched to AllApps Work tab by swiping right.")
+ LAUNCHER_ALLAPPS_SWIPE_TO_WORK_TAB(696),
+
+ @UiEvent(doc = "Default event when dedicated UI event is not available for the user action"
+ + " on slice .")
+ LAUNCHER_SLICE_DEFAULT_ACTION(700),
+
+ @UiEvent(doc = "User toggled-on a Slice item.")
+ LAUNCHER_SLICE_TOGGLE_ON(701),
+
+ @UiEvent(doc = "User toggled-off a Slice item.")
+ LAUNCHER_SLICE_TOGGLE_OFF(702),
+
+ @UiEvent(doc = "User acted on a Slice item with a button.")
+ LAUNCHER_SLICE_BUTTON_ACTION(703),
+
+ @UiEvent(doc = "User acted on a Slice item with a slider.")
+ LAUNCHER_SLICE_SLIDER_ACTION(704),
+
+ @UiEvent(doc = "User tapped on the entire row of a Slice.")
+ LAUNCHER_SLICE_CONTENT_ACTION(705),
+
+ @UiEvent(doc = "User tapped on the see more button of a Slice.")
+ LAUNCHER_SLICE_SEE_MORE_ACTION(706),
+
+ @UiEvent(doc = "User selected from a selection row of Slice.")
+ LAUNCHER_SLICE_SELECTION_ACTION(707),
+
+ @UiEvent(doc = "IME is used for selecting the focused item on the AllApps screen.")
+ LAUNCHER_ALLAPPS_FOCUSED_ITEM_SELECTED_WITH_IME(718),
+
+ @UiEvent(doc = "User long-pressed on an AllApps item.")
+ LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED(719),
+
+ @UiEvent(doc = "Launcher entered into AllApps state with device search enabled.")
+ LAUNCHER_ALLAPPS_ENTRY_WITH_DEVICE_SEARCH(720),
+
+ @UiEvent(doc = "User switched to AllApps Main/Personal tab by tapping on it.")
+ LAUNCHER_ALLAPPS_TAP_ON_PERSONAL_TAB(721),
+
+ @UiEvent(doc = "User switched to AllApps Work tab by tapping on it.")
+ LAUNCHER_ALLAPPS_TAP_ON_WORK_TAB(722),
+
+ @UiEvent(doc = "All apps vertical fling started.")
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN(724),
+
+ @UiEvent(doc = "All apps vertical fling ended.")
+ LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END(725)
+ ;
+
// ADD MORE
private final int mId;
@@ -428,45 +521,56 @@
}
/**
+ * Sets logging fields from provided {@link SliceItem}.
+ */
+ default StatsLogger withSliceItem(SliceItem sliceItem) {
+ return this;
+ }
+
+ /**
* Builds the final message and logs it as {@link EventEnum}.
*/
default void log(EventEnum event) {
}
+
+ /**
+ * Builds the final message and logs it to two different atoms, one for
+ * event tracking and the other for jank tracking.
+ */
+ default void sendToInteractionJankMonitor(EventEnum event, View v) {
+ }
}
/**
* Returns new logger object.
*/
public StatsLogger logger() {
+ StatsLogger logger = createLogger();
+ if (mInstanceId != null) {
+ return logger.withInstanceId(mInstanceId);
+ }
+ return logger;
+ }
+
+ protected StatsLogger createLogger() {
return new StatsLogger() {
};
}
/**
+ * Sets InstanceId to every new {@link StatsLogger} object returned by {@link #logger()} when
+ * not-null.
+ */
+ public StatsLogManager withDefaultInstanceId(@Nullable InstanceId instanceId) {
+ this.mInstanceId = instanceId;
+ return this;
+ }
+
+ /**
* Creates a new instance of {@link StatsLogManager} based on provided context.
*/
public static StatsLogManager newInstance(Context context) {
- StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
+ return Overrides.getObject(StatsLogManager.class,
context.getApplicationContext(), R.string.stats_log_manager_class);
- return mgr;
- }
-
- /**
- * Log an event with ranked-choice information along with package. Does nothing if event.getId()
- * <= 0.
- *
- * @param rankingEvent an enum implementing EventEnum interface.
- * @param instanceId An identifier obtained from an InstanceIdSequence.
- * @param packageName the package name of the relevant app, if known (null otherwise).
- * @param position the position picked.
- */
- public void log(EventEnum rankingEvent, InstanceId instanceId, @Nullable String packageName,
- int position) {
- }
-
- /**
- * Logs impression of the current workspace with additional launcher events.
- */
- public void logSnapshot(List<EventEnum> additionalEvents) {
}
}
diff --git a/src/com/android/launcher3/logging/StatsLogUtils.java b/src/com/android/launcher3/logging/StatsLogUtils.java
deleted file mode 100644
index a5cc7ea..0000000
--- a/src/com/android/launcher3/logging/StatsLogUtils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.launcher3.logging;
-
-import android.view.View;
-import android.view.ViewParent;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-
-import java.util.ArrayList;
-
-public class StatsLogUtils {
- private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
-
- /**
- * Implemented by containers to provide a container source for a given child.
- */
- public interface LogContainerProvider {
-
- /**
- * Populates parent container targets for an item
- */
- void fillInLogContainerData(ItemInfo childInfo, Target child, ArrayList<Target> parents);
- }
-
- /**
- * Recursively finds the parent of the given child which implements IconLogInfoProvider
- */
- public static LogContainerProvider getLaunchProviderRecursive(@Nullable View v) {
- ViewParent parent;
- if (v != null) {
- parent = v.getParent();
- } else {
- return null;
- }
-
- // Optimization to only check up to 5 parents.
- int count = MAXIMUM_VIEW_HIERARCHY_LEVEL;
- while (parent != null && count-- > 0) {
- if (parent instanceof LogContainerProvider) {
- return (LogContainerProvider) parent;
- } else {
- parent = parent.getParent();
- }
- }
- return null;
- }
-}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
deleted file mode 100644
index e094cab..0000000
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.LoggerUtils.newAction;
-import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.logging.LoggerUtils.newControlTarget;
-import static com.android.launcher3.logging.LoggerUtils.newDropTarget;
-import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
-import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
-import static com.android.launcher3.logging.LoggerUtils.newTarget;
-import static com.android.launcher3.logging.LoggerUtils.newTouchAction;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
-
-import static java.util.Optional.ofNullable;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Process;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.DropTarget;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.InstantAppResolver;
-import com.android.launcher3.util.LogConfig;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import com.google.protobuf.InvalidProtocolBufferException;
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-
-import java.util.ArrayList;
-import java.util.UUID;
-
-/**
- * Manages the creation of {@link LauncherEvent}.
- * To debug this class, execute following command before side loading a new apk.
- * <p>
- * $ adb shell setprop log.tag.UserEvent VERBOSE
- */
-public class UserEventDispatcher implements ResourceBasedOverride {
-
- private static final String TAG = "UserEvent";
- private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
- private static final String UUID_STORAGE = "uuid";
-
- /**
- * A factory method for UserEventDispatcher
- */
- public static UserEventDispatcher newInstance(Context context) {
- SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
- String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
- if (uuidStr == null) {
- uuidStr = UUID.randomUUID().toString();
- sharedPrefs.edit().putString(UUID_STORAGE, uuidStr).apply();
- }
- UserEventDispatcher ued = Overrides.getObject(UserEventDispatcher.class,
- context.getApplicationContext(), R.string.user_event_dispatcher_class);
- ued.mUuidStr = uuidStr;
- ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
- return ued;
- }
-
-
- /**
- * Fills in the container data on the given event if the given view is not null.
- *
- * @return whether container data was added.
- */
- public boolean fillLogContainer(@Nullable View v, Target child,
- @Nullable ArrayList<Target> targets) {
- LogContainerProvider firstParent = StatsLogUtils.getLaunchProviderRecursive(v);
- if (v == null || !(v.getTag() instanceof ItemInfo) || firstParent == null) {
- return false;
- }
- final ItemInfo itemInfo = (ItemInfo) v.getTag();
- firstParent.fillInLogContainerData(itemInfo, child, targets);
- return true;
- }
-
- protected void onFillInLogContainerData(@NonNull ItemInfo itemInfo, @NonNull Target target,
- @NonNull ArrayList<Target> targets) {
- }
-
- private boolean mSessionStarted;
- private long mElapsedContainerMillis;
- private long mElapsedSessionMillis;
- private long mActionDurationMillis;
- private String mUuidStr;
- protected InstantAppResolver mInstantAppResolver;
- private boolean mAppOrTaskLaunch;
- private boolean mPreviousHomeGesture;
-
- // APP_ICON SHORTCUT WIDGET
- // --------------------------------------------------------------
- // packageNameHash required optional required
- // componentNameHash required required
- // intentHash required
- // --------------------------------------------------------------
-
- @Deprecated
- public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
- Target itemTarget = newItemTarget(v, mInstantAppResolver);
- Action action = newTouchAction(Action.Touch.TAP);
- ArrayList<Target> targets = makeTargetsList(itemTarget);
- if (fillLogContainer(v, itemTarget, targets)) {
- onFillInLogContainerData((ItemInfo) v.getTag(), itemTarget, targets);
- fillIntentInfo(itemTarget, intent, userHandle);
- }
- LauncherEvent event = newLauncherEvent(action, targets);
- dispatchUserEvent(event, intent);
- mAppOrTaskLaunch = true;
- }
-
- /**
- * Dummy method.
- */
- public void logActionTip(int actionType, int viewType) {
- }
-
- @Deprecated
- public void logTaskLaunchOrDismiss(int action, int direction, int taskIndex,
- ComponentKey componentKey) {
- LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE or FLING
- newTarget(Target.Type.ITEM));
- if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) {
- // Direction DOWN means the task was launched, UP means it was dismissed.
- event.action.dir = direction;
- }
- event.srcTarget[0].itemType = ItemType.TASK;
- event.srcTarget[0].pageIndex = taskIndex;
- fillComponentInfo(event.srcTarget[0], componentKey.componentName);
- dispatchUserEvent(event, null);
- mAppOrTaskLaunch = true;
- }
-
- protected void fillIntentInfo(Target target, Intent intent, @Nullable UserHandle userHandle) {
- target.intentHash = intent.hashCode();
- target.isWorkApp = userHandle != null && !userHandle.equals(Process.myUserHandle());
- fillComponentInfo(target, intent.getComponent());
- }
-
- private void fillComponentInfo(Target target, ComponentName cn) {
- if (cn != null) {
- target.packageNameHash = (mUuidStr + cn.getPackageName()).hashCode();
- target.componentHash = (mUuidStr + cn.flattenToString()).hashCode();
- }
- }
-
- public void logNotificationLaunch(View v, PendingIntent intent) {
- LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
- newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
- Target itemTarget = newItemTarget(v, mInstantAppResolver);
- ArrayList<Target> targets = makeTargetsList(itemTarget);
-
- if (fillLogContainer(v, itemTarget, targets)) {
- itemTarget.packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
- }
- dispatchUserEvent(event, null);
- }
-
- public void logActionCommand(int command, Target srcTarget) {
- logActionCommand(command, srcTarget, null);
- }
-
- public void logActionCommand(int command, int srcContainerType, int dstContainerType) {
- logActionCommand(command, newContainerTarget(srcContainerType),
- dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
- }
-
- public void logActionCommand(int command, int srcContainerType, int dstContainerType,
- int pageIndex) {
- Target srcTarget = newContainerTarget(srcContainerType);
- srcTarget.pageIndex = pageIndex;
- logActionCommand(command, srcTarget,
- dstContainerType >= 0 ? newContainerTarget(dstContainerType) : null);
- }
-
- public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
- LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
- if (command == Action.Command.STOP) {
- if (mAppOrTaskLaunch || !mSessionStarted) {
- mSessionStarted = false;
- return;
- }
- }
-
- if (dstTarget != null) {
- event.destTarget = new Target[1];
- event.destTarget[0] = dstTarget;
- event.action.isStateChange = true;
- }
- dispatchUserEvent(event, null);
- }
-
- /**
- * TODO: Make this function work when a container view is passed as the 2nd param.
- */
- public void logActionCommand(int command, View itemView, int srcContainerType) {
- LauncherEvent event = newLauncherEvent(newCommandAction(command),
- newItemTarget(itemView, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
-
- Target itemTarget = newItemTarget(itemView, mInstantAppResolver);
- ArrayList<Target> targets = makeTargetsList(itemTarget);
-
- if (fillLogContainer(itemView, itemTarget, targets)) {
- // TODO: Remove the following two lines once fillInLogContainerData can take in a
- // container view.
- itemTarget.type = Target.Type.CONTAINER;
- itemTarget.containerType = srcContainerType;
- }
- dispatchUserEvent(event, null);
- }
-
- public void logActionOnControl(int action, int controlType) {
- logActionOnControl(action, controlType, null);
- }
-
- public void logActionOnControl(int action, int controlType, int parentContainerType) {
- logActionOnControl(action, controlType, null, parentContainerType);
- }
-
- /**
- * Logs control action with proper parent hierarchy
- */
- public void logActionOnControl(int actionType, int controlType,
- @Nullable View controlInContainer, int... parentTypes) {
- Target control = newTarget(Target.Type.CONTROL);
- control.controlType = controlType;
- Action action = newAction(actionType);
-
- ArrayList<Target> targets = makeTargetsList(control);
- if (controlInContainer != null) {
- fillLogContainer(controlInContainer, control, targets);
- }
- for (int parentContainerType : parentTypes) {
- if (parentContainerType < 0) continue;
- targets.add(newContainerTarget(parentContainerType));
- }
- LauncherEvent event = newLauncherEvent(action, targets);
- if (actionType == Action.Touch.DRAGDROP) {
- event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
- }
- dispatchUserEvent(event, null);
- }
-
- public void logActionTapOutside(Target target) {
- LauncherEvent event = newLauncherEvent(newTouchAction(Action.Type.TOUCH),
- target);
- event.action.isOutside = true;
- dispatchUserEvent(event, null);
- }
-
- public void logActionBounceTip(int containerType) {
- LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
- newContainerTarget(containerType));
- event.srcTarget[0].tipType = TipType.BOUNCE;
- dispatchUserEvent(event, null);
- }
-
- public void logActionOnContainer(int action, int dir, int containerType) {
- logActionOnContainer(action, dir, containerType, 0);
- }
-
- public void logActionOnContainer(int action, int dir, int containerType, int pageIndex) {
- LauncherEvent event = newLauncherEvent(newTouchAction(action),
- newContainerTarget(containerType));
- event.action.dir = dir;
- event.srcTarget[0].pageIndex = pageIndex;
- dispatchUserEvent(event, null);
- }
-
- /**
- * Used primarily for swipe up and down when state changes when swipe up happens from the
- * navbar bezel, the {@param srcChildContainerType} is NAVBAR and
- * {@param srcParentContainerType} is either one of the two
- * (1) WORKSPACE: if the launcher is the foreground activity
- * (2) APP: if another app was the foreground activity
- */
- public void logStateChangeAction(int action, int dir, int downX, int downY,
- int srcChildTargetType, int srcParentContainerType, int dstContainerType,
- int pageIndex) {
- LauncherEvent event;
- if (srcChildTargetType == ItemType.TASK) {
- event = newLauncherEvent(newTouchAction(action),
- newItemTarget(srcChildTargetType),
- newContainerTarget(srcParentContainerType));
- } else {
- event = newLauncherEvent(newTouchAction(action),
- newContainerTarget(srcChildTargetType),
- newContainerTarget(srcParentContainerType));
- }
- event.destTarget = new Target[1];
- event.destTarget[0] = newContainerTarget(dstContainerType);
- event.action.dir = dir;
- event.action.isStateChange = true;
- event.srcTarget[0].pageIndex = pageIndex;
- event.srcTarget[0].spanX = downX;
- event.srcTarget[0].spanY = downY;
- dispatchUserEvent(event, null);
- resetElapsedContainerMillis("state changed");
- }
-
- public void logActionOnItem(int action, int dir, int itemType) {
- logActionOnItem(action, dir, itemType, null, null);
- }
-
- /**
- * Creates new {@link LauncherEvent} of ITEM target type with input arguments and dispatches it.
- *
- * @param touchAction ENUM value of {@link LauncherLogProto.Action.Touch} Action
- * @param dir ENUM value of {@link LauncherLogProto.Action.Direction} Action
- * @param itemType ENUM value of {@link LauncherLogProto.ItemType}
- * @param gridX Nullable X coordinate of item's position on the workspace grid
- * @param gridY Nullable Y coordinate of item's position on the workspace grid
- */
- public void logActionOnItem(int touchAction, int dir, int itemType,
- @Nullable Integer gridX, @Nullable Integer gridY) {
- Target itemTarget = newTarget(Target.Type.ITEM);
- itemTarget.itemType = itemType;
- ofNullable(gridX).ifPresent(value -> itemTarget.gridX = value);
- ofNullable(gridY).ifPresent(value -> itemTarget.gridY = value);
- LauncherEvent event = newLauncherEvent(newTouchAction(touchAction), itemTarget);
- event.action.dir = dir;
- dispatchUserEvent(event, null);
- }
-
- /**
- * Logs proto lite version of LauncherEvent object to clearcut.
- */
- public void logLauncherEvent(
- com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
-
- if (mPreviousHomeGesture) {
- mPreviousHomeGesture = false;
- }
- mAppOrTaskLaunch = false;
- launcherEvent.toBuilder()
- .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
- .setElapsedSessionMillis(
- SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
- try {
- dispatchUserEvent(LauncherEvent.parseFrom(launcherEvent.toByteArray()), null);
- } catch (InvalidProtocolBufferNanoException e) {
- throw new RuntimeException("Cannot convert LauncherEvent from Lite to Nano version.");
- }
- }
-
- public void logDeepShortcutsOpen(View icon) {
- ItemInfo info = (ItemInfo) icon.getTag();
- Target child = newItemTarget(info, mInstantAppResolver);
- ArrayList<Target> targets = makeTargetsList(child);
- fillLogContainer(icon, child, targets);
- dispatchUserEvent(newLauncherEvent(newTouchAction(Action.Touch.TAP), targets), null);
- resetElapsedContainerMillis("deep shortcut open");
- }
-
- public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
- Target srcChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
- ArrayList<Target> srcTargets = makeTargetsList(srcChild);
-
-
- Target destChild = newItemTarget(dragObj.originalDragInfo, mInstantAppResolver);
- ArrayList<Target> destTargets = makeTargetsList(destChild);
-
- dragObj.dragSource.fillInLogContainerData(dragObj.originalDragInfo, srcChild, srcTargets);
- if (dropTargetAsView instanceof LogContainerProvider) {
- ((LogContainerProvider) dropTargetAsView).fillInLogContainerData(dragObj.dragInfo,
- destChild, destTargets);
- }
- else {
- destTargets.add(newDropTarget(dropTargetAsView));
- }
- LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP), srcTargets);
- Target[] destTargetsArray = new Target[destTargets.size()];
- destTargets.toArray(destTargetsArray);
- event.destTarget = destTargetsArray;
-
- event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
- dispatchUserEvent(event, null);
- }
-
- public void logActionBack(boolean completed, int downX, int downY, boolean isButton,
- boolean gestureSwipeLeft, int containerType) {
- int actionTouch = isButton ? Action.Touch.TAP : Action.Touch.SWIPE;
- Action action = newCommandAction(actionTouch);
- action.command = Action.Command.BACK;
- action.dir = isButton ? Action.Direction.NONE :
- gestureSwipeLeft ? Action.Direction.LEFT : Action.Direction.RIGHT;
- Target target = newControlTarget(isButton ? ControlType.BACK_BUTTON :
- ControlType.BACK_GESTURE);
- target.spanX = downX;
- target.spanY = downY;
- target.cardinality = completed ? 1 : 0;
- LauncherEvent event = newLauncherEvent(action, target, newContainerTarget(containerType));
-
- dispatchUserEvent(event, null);
- }
-
- /**
- * Currently logs following containers: workspace, allapps, widget tray.
- */
- public final void resetElapsedContainerMillis(String reason) {
- mElapsedContainerMillis = SystemClock.uptimeMillis();
- if (!IS_VERBOSE) {
- return;
- }
- Log.d(TAG, "resetElapsedContainerMillis reason=" + reason);
-
- }
-
- public final void startSession() {
- mSessionStarted = true;
- mElapsedSessionMillis = SystemClock.uptimeMillis();
- mElapsedContainerMillis = SystemClock.uptimeMillis();
- }
-
- public final void setPreviousHomeGesture(boolean homeGesture) {
- mPreviousHomeGesture = homeGesture;
- }
-
- public final boolean isPreviousHomeGesture() {
- return mPreviousHomeGesture;
- }
-
- public final void resetActionDurationMillis() {
- mActionDurationMillis = SystemClock.uptimeMillis();
- }
-
- public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
- if (mPreviousHomeGesture) {
- mPreviousHomeGesture = false;
- }
- mAppOrTaskLaunch = false;
- ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
- ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
- if (!IS_VERBOSE) {
- return;
- }
- LauncherLogProto.LauncherEvent liteLauncherEvent;
- try {
- liteLauncherEvent =
- LauncherLogProto.LauncherEvent.parseFrom(MessageNano.toByteArray(ev));
- } catch (InvalidProtocolBufferException e) {
- throw new RuntimeException("Cannot parse LauncherEvent from Nano to Lite version");
- }
- Log.d(TAG, liteLauncherEvent.toString());
- }
-
- /**
- * Constructs an ArrayList with targets
- */
- public static ArrayList<Target> makeTargetsList(Target... targets) {
- ArrayList<Target> result = new ArrayList<>();
- for (Target target : targets) {
- result.add(target);
- }
- return result;
- }
-}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index c236fa6..56dbbd3 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -34,6 +34,7 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
+import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
@@ -132,7 +133,9 @@
continue;
}
} else {
- workspaceInfo.setInstallProgress((int) sessionInfo.getProgress());
+ workspaceInfo.setProgressLevel(
+ (int) (sessionInfo.getProgress() * 100),
+ PackageInstallInfo.STATUS_INSTALLING);
}
if (hasActivity) {
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index eb5d106..92b5885 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -21,11 +21,10 @@
import android.content.ComponentName;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
+import android.content.Intent;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.os.LocaleList;
-import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -37,7 +36,6 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -138,7 +136,7 @@
if (findAppInfo(info.componentName, info.user) != null) {
return;
}
- mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
+ mIconCache.getTitleAndIcon(info, activityInfo, false /* useLowResIcon */);
info.sectionName = mIndex.computeSectionName(info.title);
data.add(info);
@@ -146,11 +144,10 @@
}
public void addPromiseApp(Context context, PackageInstallInfo installInfo) {
- ApplicationInfo applicationInfo = new PackageManagerHelper(context)
- .getApplicationInfo(installInfo.packageName, installInfo.user, 0);
// only if not yet installed
- if (applicationInfo == null) {
- PromiseAppInfo info = new PromiseAppInfo(installInfo);
+ if (!new PackageManagerHelper(context)
+ .isAppInstalled(installInfo.packageName, installInfo.user)) {
+ AppInfo info = new AppInfo(installInfo);
mIconCache.getTitleAndIcon(info, info.usingLowResIcon());
info.sectionName = mIndex.computeSectionName(info.title);
@@ -159,24 +156,31 @@
}
}
- public PromiseAppInfo updatePromiseInstallInfo(PackageInstallInfo installInfo) {
- UserHandle user = Process.myUserHandle();
- for (int i=0; i < data.size(); i++) {
+ /** Updates the given PackageInstallInfo's associated AppInfo's installation info. */
+ public List<AppInfo> updatePromiseInstallInfo(PackageInstallInfo installInfo) {
+ List<AppInfo> updatedAppInfos = new ArrayList<>();
+ UserHandle user = installInfo.user;
+ for (int i = data.size() - 1; i >= 0; i--) {
final AppInfo appInfo = data.get(i);
final ComponentName tgtComp = appInfo.getTargetComponent();
if (tgtComp != null && tgtComp.getPackageName().equals(installInfo.packageName)
- && appInfo.user.equals(user)
- && appInfo instanceof PromiseAppInfo) {
- final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
- if (installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
- promiseAppInfo.level = installInfo.progress;
- return promiseAppInfo;
- } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
+ && appInfo.user.equals(user)) {
+ if (installInfo.state == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING
+ || installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+ if (appInfo.isAppStartable()
+ && installInfo.state == PackageInstallInfo.STATUS_INSTALLING) {
+ continue;
+ }
+ appInfo.setProgressLevel(installInfo);
+
+ updatedAppInfos.add(appInfo);
+ } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED
+ && !appInfo.isAppStartable()) {
removeApp(i);
}
}
}
- return null;
+ return updatedAppInfos;
}
private void removeApp(int index) {
@@ -197,11 +201,16 @@
/**
* Add the icons for the supplied apk called packageName.
*/
- public void addPackage(Context context, String packageName, UserHandle user) {
- for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class)
- .getActivityList(packageName, user)) {
+ public List<LauncherActivityInfo> addPackage(
+ Context context, String packageName, UserHandle user) {
+ List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, user);
+
+ for (LauncherActivityInfo info : activities) {
add(new AppInfo(context, info, user), info);
}
+
+ return activities;
}
/**
@@ -244,7 +253,8 @@
/**
* Add and remove icons for this package which has been updated.
*/
- public void updatePackage(Context context, String packageName, UserHandle user) {
+ public List<LauncherActivityInfo> updatePackage(
+ Context context, String packageName, UserHandle user) {
final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
.getActivityList(packageName, user);
if (matches.size() > 0) {
@@ -268,8 +278,14 @@
if (applicationInfo == null) {
add(new AppInfo(context, info, user), info);
} else {
- mIconCache.getTitleAndIcon(applicationInfo, info, true /* useLowResIcon */);
+ Intent launchIntent = AppInfo.makeLaunchIntent(info);
+
+ mIconCache.getTitleAndIcon(applicationInfo, info, false /* useLowResIcon */);
applicationInfo.sectionName = mIndex.computeSectionName(applicationInfo.title);
+ applicationInfo.setProgressLevel(
+ PackageManagerHelper.getLoadingProgress(info),
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+ applicationInfo.intent = launchIntent;
mDataChanged = true;
}
@@ -285,6 +301,8 @@
}
}
}
+
+ return matches;
}
/**
@@ -305,7 +323,7 @@
*
* @return the corresponding AppInfo or null
*/
- private @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
+ public @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
@NonNull UserHandle user) {
for (AppInfo info: data) {
if (componentName.equals(info.componentName) && user.equals(info.user)) {
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
deleted file mode 100644
index 906c2e0..0000000
--- a/src/com/android/launcher3/model/AppLaunchTracker.java
+++ /dev/null
@@ -1,60 +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.model;
-
-import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
-
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-/**
- * Callback for receiving various app launch events
- */
-public class AppLaunchTracker implements ResourceBasedOverride {
-
- /**
- * Derived from LauncherEvent proto.
- * TODO: Use proper descriptive constants
- */
- public static final String CONTAINER_DEFAULT = Integer.toString(ContainerType.WORKSPACE);
- public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
- public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
- public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
- public static final String CONTAINER_OVERVIEW = Integer.toString(ContainerType.OVERVIEW);
- public static final String CONTAINER_HOTSEAT = Integer.toString(ContainerType.HOTSEAT);
-
-
- public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
- forOverride(AppLaunchTracker.class, R.string.app_launch_tracker_class);
-
- public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
- @Nullable String container) { }
-
- public void onStartApp(ComponentName componentName, UserHandle user,
- @Nullable String container) { }
-
- public void onDismissApp(ComponentName componentName, UserHandle user,
- @Nullable String container){}
-
- public void onReturnedToHome() { }
-}
diff --git a/src/com/android/launcher3/model/BaseLoaderResults.java b/src/com/android/launcher3/model/BaseLoaderResults.java
index 8b0ef7b..5c85bab 100644
--- a/src/com/android/launcher3/model/BaseLoaderResults.java
+++ b/src/com/android/launcher3/model/BaseLoaderResults.java
@@ -17,7 +17,6 @@
package com.android.launcher3.model;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
-import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import android.util.Log;
@@ -27,6 +26,7 @@
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.PagedView;
import com.android.launcher3.model.BgDataModel.Callbacks;
+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;
@@ -76,18 +76,20 @@
ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
final IntArray orderedScreenIds = new IntArray();
+ ArrayList<FixedContainerItems> extraItems = new ArrayList<>();
synchronized (mBgDataModel) {
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
+ mBgDataModel.extraItems.forEach(extraItems::add);
mBgDataModel.lastBindId++;
mMyBindingId = mBgDataModel.lastBindId;
}
for (Callbacks cb : mCallbacksList) {
new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- workspaceItems, appWidgets, orderedScreenIds).bind();
+ workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
}
}
@@ -135,7 +137,7 @@
private final ArrayList<ItemInfo> mWorkspaceItems;
private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
private final IntArray mOrderedScreenIds;
-
+ private final ArrayList<FixedContainerItems> mExtraItems;
WorkspaceBinder(Callbacks callbacks,
Executor uiExecutor,
@@ -144,6 +146,7 @@
int myBindingId,
ArrayList<ItemInfo> workspaceItems,
ArrayList<LauncherAppWidgetInfo> appWidgets,
+ ArrayList<FixedContainerItems> extraItems,
IntArray orderedScreenIds) {
mCallbacks = callbacks;
mUiExecutor = uiExecutor;
@@ -152,6 +155,7 @@
mMyBindingId = myBindingId;
mWorkspaceItems = workspaceItems;
mAppWidgets = appWidgets;
+ mExtraItems = extraItems;
mOrderedScreenIds = orderedScreenIds;
}
@@ -198,15 +202,15 @@
// Load items on the current page.
bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
bindAppWidgets(currentAppWidgets, mainExecutor);
+ mExtraItems.forEach(item ->
+ executeCallbacksTask(c -> c.bindExtraContainerItems(item), mainExecutor));
- // Locate available spots for prediction using currentWorkspaceItems
- IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons);
- bindPredictedItems(gaps, mainExecutor);
// In case of validFirstPage, only bind the first screen, and defer binding the
// remaining screens after first onDraw (and an optional the fade animation whichever
// happens later).
// This ensures that the first screen is immediately visible (eg. during rotation)
// In case of !validFirstPage, bind all pages one after other.
+
final Executor deferredExecutor =
validFirstPage ? new ViewOnDrawExecutor() : mainExecutor;
@@ -253,11 +257,6 @@
}
}
- private void bindPredictedItems(IntArray ranks, final Executor executor) {
- ArrayList<AppInfo> items = new ArrayList<>(mBgDataModel.cachedPredictedItems);
- executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor);
- }
-
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
executor.execute(() -> {
if (mMyBindingId != mBgDataModel.lastBindId) {
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9013cba..ad553d5 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -22,15 +22,20 @@
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.model.BgDataModel.Callbacks;
+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.WorkspaceItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
/**
* Extension of {@link ModelUpdateTask} with some utility methods
@@ -88,11 +93,27 @@
return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
}
-
- public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
- if (!updatedShortcuts.isEmpty()) {
- scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts));
+ public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
+ // Bind workspace items
+ List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream()
+ .filter(info -> info.id != ItemInfo.NO_ID)
+ .collect(Collectors.toList());
+ if (!workspaceUpdates.isEmpty()) {
+ scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates));
}
+
+ // Bind extra items if any
+ allUpdates.stream()
+ .mapToInt(info -> info.container)
+ .distinct()
+ .mapToObj(mDataModel.extraItems::get)
+ .filter(Objects::nonNull)
+ .forEach(this::bindExtraContainerItems);
+ }
+
+ public void bindExtraContainerItems(FixedContainerItems item) {
+ FixedContainerItems copy = item.clone();
+ scheduleCallbackTask(c -> c.bindExtraContainerItems(copy));
}
public void bindDeepShortcuts(BgDataModel dataModel) {
@@ -102,8 +123,8 @@
}
public void bindUpdatedWidgets(BgDataModel dataModel) {
- final ArrayList<WidgetListRowEntry> widgets =
- dataModel.widgetsModel.getWidgetsList(mApp.getContext());
+ 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 9bef847..1d7d1a2 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -15,48 +15,57 @@
*/
package com.android.launcher3.model;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
+
import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
-import android.util.MutableInt;
-import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.ViewOnDrawExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.function.BiConsumer;
+import java.util.Set;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* All the data stored in-memory and managed by the LauncherModel
@@ -88,21 +97,9 @@
public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
/**
- * Map of ShortcutKey to the number of times it is pinned.
+ * Extra container based items
*/
- public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
-
- /**
- * List of all cached predicted items visible on home screen
- */
- public final ArrayList<AppInfo> cachedPredictedItems = new ArrayList<>();
-
- /**
- * @see Callbacks#FLAG_HAS_SHORTCUT_PERMISSION
- * @see Callbacks#FLAG_QUIET_MODE_ENABLED
- * @see Callbacks#FLAG_QUIET_MODE_CHANGE_PERMISSION
- */
- public int flags;
+ public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
/**
* Maps all launcher activities to counts of their shortcuts.
@@ -127,8 +124,8 @@
appWidgets.clear();
folders.clear();
itemsIdMap.clear();
- pinnedShortcutCounts.clear();
deepShortcutMap.clear();
+ extraItems.clear();
}
/**
@@ -181,6 +178,7 @@
}
public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
+ ArraySet<UserHandle> updatedDeepShortcuts = new ArraySet<>();
for (ItemInfo item : items) {
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -199,14 +197,7 @@
workspaceItems.remove(item);
break;
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
- // Decrement pinned shortcut count
- ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
- MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
- if ((count == null || --count.value == 0)
- && !InstallShortcutReceiver.getPendingShortcuts(context)
- .contains(pinnedShortcut)) {
- unpinShortcut(context, pinnedShortcut);
- }
+ updatedDeepShortcuts.add(item.user);
// Fall through.
}
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -220,6 +211,7 @@
}
itemsIdMap.remove(item.id);
}
+ updatedDeepShortcuts.forEach(user -> updateShortcutPinnedState(context, user));
}
public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
@@ -229,23 +221,7 @@
folders.put(item.id, (FolderInfo) item);
workspaceItems.add(item);
break;
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
- // Increment the count for the given shortcut
- ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
- MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
- if (count == null) {
- count = new MutableInt(1);
- pinnedShortcutCounts.put(pinnedShortcut, count);
- } else {
- count.value++;
- }
-
- // Since this is a new item, pin the shortcut in the system server.
- if (newItem && count.value == 1) {
- updatePinnedShortcuts(context, pinnedShortcut, List::add);
- }
- // Fall through
- }
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
@@ -270,36 +246,86 @@
appWidgets.add((LauncherAppWidgetInfo) item);
break;
}
+ if (newItem && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ updateShortcutPinnedState(context, item.user);
+ }
}
/**
- * Removes the given shortcut from the current list of pinned shortcuts.
- * (Runs on background thread)
+ * Updates the deep shortucts state in system to match out internal model, pinning any missing
+ * shortcuts and unpinning any extra shortcuts.
*/
- public void unpinShortcut(Context context, ShortcutKey key) {
- updatePinnedShortcuts(context, key, List::remove);
+ public void updateShortcutPinnedState(Context context) {
+ for (UserHandle user : UserCache.INSTANCE.get(context).getUserProfiles()) {
+ updateShortcutPinnedState(context, user);
+ }
}
- private void updatePinnedShortcuts(Context context, ShortcutKey key,
- BiConsumer<List<String>, String> idOp) {
+ /**
+ * Updates the deep shortucts state in system to match out internal model, pinning any missing
+ * shortcuts and unpinning any extra shortcuts.
+ */
+ public synchronized void updateShortcutPinnedState(Context context, UserHandle user) {
if (GO_DISABLE_WIDGETS) {
return;
}
- String packageName = key.componentName.getPackageName();
- String id = key.getId();
- UserHandle user = key.user;
- List<String> pinnedIds = new ShortcutRequest(context, user)
- .forPackage(packageName)
- .query(PINNED)
- .stream()
- .map(ShortcutInfo::getId)
- .collect(Collectors.toCollection(ArrayList::new));
- idOp.accept(pinnedIds, id);
- try {
- context.getSystemService(LauncherApps.class).pinShortcuts(packageName, pinnedIds, user);
- } catch (SecurityException | IllegalStateException e) {
- Log.w(TAG, "Failed to pin shortcut", e);
+
+ // Collect all system shortcuts
+ QueryResult result = new ShortcutRequest(context, user)
+ .query(PINNED | FLAG_GET_KEY_FIELDS_ONLY);
+ if (!result.wasSuccess()) {
+ return;
}
+ // Map of packageName to shortcutIds that are currently in the system
+ Map<String, Set<String>> systemMap = result.stream()
+ .collect(groupingBy(ShortcutInfo::getPackage,
+ mapping(ShortcutInfo::getId, Collectors.toSet())));
+
+ // Collect all model shortcuts
+ Stream.Builder<WorkspaceItemInfo> itemStream = Stream.builder();
+ forAllWorkspaceItemInfos(user, itemStream::accept);
+ // Map of packageName to shortcutIds that are currently in our model
+ Map<String, Set<String>> modelMap = Stream.concat(
+ // Model shortcuts
+ itemStream.build()
+ .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ .map(ShortcutKey::fromItemInfo),
+ // Pending shortcuts
+ ItemInstallQueue.INSTANCE.get(context).getPendingShortcuts(user))
+ .collect(groupingBy(ShortcutKey::getPackageName,
+ mapping(ShortcutKey::getId, Collectors.toSet())));
+
+ // Check for diff
+ for (Map.Entry<String, Set<String>> entry : modelMap.entrySet()) {
+ Set<String> modelShortcuts = entry.getValue();
+ Set<String> systemShortcuts = systemMap.remove(entry.getKey());
+ if (systemShortcuts == null) {
+ systemShortcuts = Collections.emptySet();
+ }
+
+ // Do not use .equals as it can vary based on the type of set
+ if (systemShortcuts.size() != modelShortcuts.size()
+ || !systemShortcuts.containsAll(modelShortcuts)) {
+ // Update system state for this package
+ try {
+ context.getSystemService(LauncherApps.class).pinShortcuts(
+ entry.getKey(), new ArrayList<>(modelShortcuts), user);
+ } catch (SecurityException | IllegalStateException e) {
+ Log.w(TAG, "Failed to pin shortcut", e);
+ }
+ }
+ }
+
+ // If there are any extra pinned shortcuts, remove them
+ systemMap.keySet().forEach(packageName -> {
+ // Update system state
+ try {
+ context.getSystemService(LauncherApps.class).pinShortcuts(
+ packageName, Collections.emptyList(), user);
+ } catch (SecurityException | IllegalStateException e) {
+ Log.w(TAG, "Failed to unpin shortcut", e);
+ }
+ });
}
/**
@@ -348,6 +374,69 @@
}
}
+ /**
+ * Returns a list containing all workspace items including widgets.
+ */
+ public synchronized ArrayList<ItemInfo> getAllWorkspaceItems() {
+ ArrayList<ItemInfo> items = new ArrayList<>(workspaceItems.size() + appWidgets.size());
+ items.addAll(workspaceItems);
+ items.addAll(appWidgets);
+ return items;
+ }
+
+ /**
+ * Calls the provided {@code op} for all workspaceItems in the in-memory model (both persisted
+ * items and dynamic/predicted items for the provided {@code userHandle}.
+ * Note the call is not synchronized over the model, that should be handled by the called.
+ */
+ public void forAllWorkspaceItemInfos(UserHandle userHandle, Consumer<WorkspaceItemInfo> op) {
+ for (ItemInfo info : itemsIdMap) {
+ if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
+ op.accept((WorkspaceItemInfo) info);
+ }
+ }
+
+ for (int i = extraItems.size() - 1; i >= 0; i--) {
+ for (ItemInfo info : extraItems.valueAt(i).items) {
+ if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
+ op.accept((WorkspaceItemInfo) info);
+ }
+ }
+ }
+ }
+
+ /**
+ * An object containing items corresponding to a fixed container
+ */
+ public static class FixedContainerItems {
+
+ 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);
+ });
+ }
+ }
+
+
public interface Callbacks {
// If the launcher has permission to access deep shortcuts.
int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
@@ -369,21 +458,25 @@
void preAddApps();
void bindAppsAdded(IntArray newScreens,
ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
- void bindPromiseAppProgressUpdated(PromiseAppInfo app);
- void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
+
+ /**
+ * Binds updated incremental download progress
+ */
+ void bindIncrementalDownloadProgressUpdated(AppInfo app);
+ void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated);
void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
void bindRestoreItemsChange(HashSet<ItemInfo> updates);
void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
- void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets);
+ void bindAllWidgets(List<WidgetsListBaseEntry> widgets);
void onPageBoundSynchronously(int page);
void executeOnNextDraw(ViewOnDrawExecutor executor);
void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
- void bindAllApplications(AppInfo[] apps, int flags);
-
/**
- * Binds predicted appInfos at at available prediction slots.
+ * Binds extra item provided any external source
*/
- void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks);
+ default void bindExtraContainerItems(FixedContainerItems item) { }
+
+ void bindAllApplications(AppInfo[] apps, int flags);
}
}
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 8e6b064..f644d49 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -21,7 +21,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import java.util.ArrayList;
@@ -48,23 +47,18 @@
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IconCache iconCache = app.getIconCache();
-
-
ArrayList<WorkspaceItemInfo> updatedShortcuts = new ArrayList<>();
synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && isValidShortcut(si) && cn != null
- && mPackages.contains(cn.getPackageName())) {
- iconCache.getTitleAndIcon(si, si.usingLowResIcon());
- updatedShortcuts.add(si);
- }
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ ComponentName cn = si.getTargetComponent();
+ if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && isValidShortcut(si) && cn != null
+ && mPackages.contains(cn.getPackageName())) {
+ iconCache.getTitleAndIcon(si, si.usingLowResIcon());
+ updatedShortcuts.add(si);
}
- }
+ });
apps.updateIconsAndLabels(mPackages, mUser);
}
bindUpdatedWorkspaceItems(updatedShortcuts);
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index 5112304..70d1b48 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -17,25 +17,30 @@
import static android.os.Process.myUserHandle;
+import static com.android.launcher3.pm.InstallSessionHelper.getUserHandle;
+
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.UserHandle;
import android.util.Log;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* Helper class to send broadcasts to package installers that have:
@@ -61,26 +66,10 @@
private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
- private final MultiHashMap<String, String> mPackagesForInstaller;
+ private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage;
public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
- mPackagesForInstaller = getPackagesForInstaller(sessionInfoForPackage);
- }
-
- /**
- * @return Map where the key is the package name of the installer, and the value is a list
- * of packages with active sessions for that installer.
- */
- private MultiHashMap<String, String> getPackagesForInstaller(
- HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
- MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
- for (Map.Entry<PackageUserKey, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
- if (myUserHandle().equals(entry.getKey().mUser)) {
- packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
- entry.getKey().mPackageName);
- }
- }
- return packagesForInstaller;
+ mSessionInfoForPackage = sessionInfoForPackage;
}
/**
@@ -88,9 +77,15 @@
* first screen.
*/
public void sendBroadcasts(Context context, List<ItemInfo> firstScreenItems) {
- for (Map.Entry<String, ArrayList<String>> entry : mPackagesForInstaller.entrySet()) {
- sendBroadcastToInstaller(context, entry.getKey(), entry.getValue(), firstScreenItems);
- }
+ UserHandle myUser = myUserHandle();
+ mSessionInfoForPackage
+ .values()
+ .stream()
+ .filter(info -> myUser.equals(getUserHandle(info)))
+ .collect(groupingBy(SessionInfo::getInstallerPackageName,
+ mapping(SessionInfo::getAppPackageName, Collectors.toSet())))
+ .forEach((installer, packages) ->
+ sendBroadcastToInstaller(context, installer, packages, firstScreenItems));
}
/**
@@ -99,7 +94,7 @@
* @param firstScreenItems List of items on the first screen.
*/
private void sendBroadcastToInstaller(Context context, String installerPackageName,
- List<String> packages, List<ItemInfo> firstScreenItems) {
+ Set<String> packages, List<ItemInfo> firstScreenItems) {
Set<String> folderItems = new HashSet<>();
Set<String> workspaceItems = new HashSet<>();
Set<String> hotseatItems = new HashSet<>();
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index e8a52bd..804e72e 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -25,7 +25,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
@@ -40,6 +39,7 @@
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.ArrayList;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 79467d3..c6c0791 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -39,7 +39,6 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
@@ -48,6 +47,7 @@
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.ArrayList;
diff --git a/src/com/android/launcher3/model/ItemInstallQueue.java b/src/com/android/launcher3/model/ItemInstallQueue.java
new file mode 100644
index 0000000..df8367f
--- /dev/null
+++ b/src/com/android/launcher3/model/ItemInstallQueue.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
+
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
+import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings.Favorites;
+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.shortcuts.ShortcutKey;
+import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.PersistedItemArray;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Class to maintain a queue of pending items to be added to the workspace.
+ */
+public class ItemInstallQueue {
+
+ public static final int FLAG_ACTIVITY_PAUSED = 1;
+ public static final int FLAG_LOADER_RUNNING = 2;
+ public static final int FLAG_DRAG_AND_DROP = 4;
+
+ private static final String TAG = "InstallShortcutReceiver";
+
+ // The set of shortcuts that are pending install
+ private static final String APPS_PENDING_INSTALL = "apps_to_install";
+
+ public static final int NEW_SHORTCUT_BOUNCE_DURATION = 450;
+ public static final int NEW_SHORTCUT_STAGGER_DELAY = 85;
+
+ public static MainThreadInitializedObject<ItemInstallQueue> INSTANCE =
+ new MainThreadInitializedObject<>(ItemInstallQueue::new);
+
+ private final PersistedItemArray<PendingInstallShortcutInfo> mStorage =
+ new PersistedItemArray<>(APPS_PENDING_INSTALL);
+ private final Context mContext;
+
+ // Determines whether to defer installing shortcuts immediately until
+ // processAllPendingInstalls() is called.
+ private int mInstallQueueDisabledFlags = 0;
+
+ // Only accessed on worker thread
+ private List<PendingInstallShortcutInfo> mItems;
+
+ private ItemInstallQueue(Context context) {
+ mContext = context;
+ }
+
+ @WorkerThread
+ private void ensureQueueLoaded() {
+ Preconditions.assertWorkerThread();
+ if (mItems == null) {
+ mItems = mStorage.read(mContext, this::decode);
+ }
+ }
+
+ @WorkerThread
+ private void addToQueue(PendingInstallShortcutInfo info) {
+ ensureQueueLoaded();
+ if (!mItems.contains(info)) {
+ mItems.add(info);
+ mStorage.write(mContext, mItems);
+ }
+ }
+
+ @WorkerThread
+ private void flushQueueInBackground() {
+ Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+ if (launcher == null) {
+ // Launcher not loaded
+ return;
+ }
+ ensureQueueLoaded();
+ if (mItems.isEmpty()) {
+ return;
+ }
+
+ List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
+ .map(info -> info.getItemInfo(mContext))
+ .collect(Collectors.toList());
+
+ // Add the items and clear queue
+ if (!installQueue.isEmpty()) {
+ launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
+ }
+ mItems.clear();
+ mStorage.getFile(mContext).delete();
+ }
+
+ /**
+ * Removes previously added items from the queue.
+ */
+ @WorkerThread
+ public void removeFromInstallQueue(HashSet<String> packageNames, UserHandle user) {
+ if (packageNames.isEmpty()) {
+ return;
+ }
+ ensureQueueLoaded();
+ if (mItems.removeIf(item ->
+ item.user.equals(user) && packageNames.contains(getIntentPackage(item.intent)))) {
+ mStorage.write(mContext, mItems);
+ }
+ }
+
+ /**
+ * Adds an item to the install queue
+ */
+ public void queueItem(ShortcutInfo info) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(info));
+ }
+
+ /**
+ * Adds an item to the install queue
+ */
+ public void queueItem(AppWidgetProviderInfo info, int widgetId) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, widgetId));
+ }
+
+ /**
+ * Adds an item to the install queue
+ */
+ public void queueItem(String packageName, UserHandle userHandle) {
+ queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));
+ }
+
+ /**
+ * Returns a stream of all pending shortcuts in the queue
+ */
+ @WorkerThread
+ public Stream<ShortcutKey> getPendingShortcuts(UserHandle user) {
+ ensureQueueLoaded();
+ return mItems.stream()
+ .filter(item -> item.itemType == ITEM_TYPE_DEEP_SHORTCUT && user.equals(item.user))
+ .map(item -> ShortcutKey.fromIntent(item.intent, user));
+ }
+
+ private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {
+ // Queue the item up for adding if launcher has not loaded properly yet
+ MODEL_EXECUTOR.post(() -> addToQueue(info));
+ flushInstallQueue();
+ }
+
+ /**
+ * Pauses the push-to-model flow until unpaused. All items are held in the queue and
+ * not added to the model.
+ */
+ public void pauseModelPush(int flag) {
+ mInstallQueueDisabledFlags |= flag;
+ }
+
+ /**
+ * Adds all the queue items to the model if the use is completely resumed.
+ */
+ public void resumeModelPush(int flag) {
+ mInstallQueueDisabledFlags &= ~flag;
+ flushInstallQueue();
+ }
+
+ private void flushInstallQueue() {
+ if (mInstallQueueDisabledFlags != 0) {
+ return;
+ }
+ MODEL_EXECUTOR.post(this::flushQueueInBackground);
+ }
+
+ private static class PendingInstallShortcutInfo extends ItemInfo {
+
+ final Intent intent;
+
+ @Nullable ShortcutInfo shortcutInfo;
+ @Nullable AppWidgetProviderInfo providerInfo;
+
+ /**
+ * Initializes a PendingInstallShortcutInfo to represent a pending launcher target.
+ */
+ public PendingInstallShortcutInfo(String packageName, UserHandle userHandle) {
+ itemType = Favorites.ITEM_TYPE_APPLICATION;
+ intent = new Intent().setPackage(packageName);
+ user = userHandle;
+ }
+
+ /**
+ * Initializes a PendingInstallShortcutInfo to represent a deep shortcut.
+ */
+ public PendingInstallShortcutInfo(ShortcutInfo info) {
+ itemType = Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+ intent = ShortcutKey.makeIntent(info);
+ user = info.getUserHandle();
+
+ shortcutInfo = info;
+ }
+
+ /**
+ * Initializes a PendingInstallShortcutInfo to represent an app widget.
+ */
+ public PendingInstallShortcutInfo(AppWidgetProviderInfo info, int widgetId) {
+ itemType = Favorites.ITEM_TYPE_APPWIDGET;
+ intent = new Intent()
+ .setComponent(info.provider)
+ .putExtra(EXTRA_APPWIDGET_ID, widgetId);
+ user = info.getProfile();
+
+ providerInfo = info;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return intent;
+ }
+
+ public Pair<ItemInfo, Object> getItemInfo(Context context) {
+ switch (itemType) {
+ case ITEM_TYPE_APPLICATION: {
+ String packageName = intent.getPackage();
+ List<LauncherActivityInfo> laiList =
+ context.getSystemService(LauncherApps.class)
+ .getActivityList(packageName, user);
+
+ final WorkspaceItemInfo si = new WorkspaceItemInfo();
+ si.user = user;
+ si.itemType = ITEM_TYPE_APPLICATION;
+
+ LauncherActivityInfo lai;
+ boolean usePackageIcon = laiList.isEmpty();
+ if (usePackageIcon) {
+ lai = null;
+ si.intent = makeLaunchIntent(new ComponentName(packageName, ""))
+ .setPackage(packageName);
+ si.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+ } else {
+ lai = laiList.get(0);
+ si.intent = makeLaunchIntent(lai);
+ }
+ LauncherAppState.getInstance(context).getIconCache()
+ .getTitleAndIcon(si, () -> lai, usePackageIcon, false);
+ return Pair.create(si, null);
+ }
+ case ITEM_TYPE_DEEP_SHORTCUT: {
+ WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(shortcutInfo, context);
+ LauncherAppState.getInstance(context).getIconCache()
+ .getShortcutIcon(itemInfo, shortcutInfo);
+ return Pair.create(itemInfo, shortcutInfo);
+ }
+ case ITEM_TYPE_APPWIDGET: {
+ LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
+ .fromProviderInfo(context, providerInfo);
+ LauncherAppWidgetInfo widgetInfo = new LauncherAppWidgetInfo(
+ intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0),
+ info.provider);
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ widgetInfo.minSpanX = info.minSpanX;
+ widgetInfo.minSpanY = info.minSpanY;
+ widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
+ widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
+ widgetInfo.user = user;
+ return Pair.create(widgetInfo, providerInfo);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof PendingInstallShortcutInfo) {
+ PendingInstallShortcutInfo other = (PendingInstallShortcutInfo) obj;
+
+ boolean userMatches = user.equals(other.user);
+ boolean itemTypeMatches = itemType == other.itemType;
+ boolean intentMatches = intent.toUri(0).equals(other.intent.toUri(0));
+ boolean shortcutInfoMatches = shortcutInfo == null
+ ? other.shortcutInfo == null
+ : other.shortcutInfo != null
+ && shortcutInfo.getId().equals(other.shortcutInfo.getId())
+ && shortcutInfo.getPackage().equals(other.shortcutInfo.getPackage());
+ boolean providerInfoMatches = providerInfo == null
+ ? other.providerInfo == null
+ : other.providerInfo != null
+ && providerInfo.provider.equals(other.providerInfo.provider);
+
+ return userMatches
+ && itemTypeMatches
+ && intentMatches
+ && shortcutInfoMatches
+ && providerInfoMatches;
+ }
+ return false;
+ }
+ }
+
+ private static String getIntentPackage(Intent intent) {
+ return intent.getComponent() == null
+ ? intent.getPackage() : intent.getComponent().getPackageName();
+ }
+
+ private PendingInstallShortcutInfo decode(int itemType, UserHandle user, Intent intent) {
+ switch (itemType) {
+ case Favorites.ITEM_TYPE_APPLICATION:
+ return new PendingInstallShortcutInfo(intent.getPackage(), user);
+ case Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
+ List<ShortcutInfo> si = ShortcutKey.fromIntent(intent, user)
+ .buildRequest(mContext)
+ .query(ShortcutRequest.ALL);
+ if (si.isEmpty()) {
+ return null;
+ } else {
+ return new PendingInstallShortcutInfo(si.get(0));
+ }
+ }
+ case Favorites.ITEM_TYPE_APPWIDGET: {
+ int widgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, 0);
+ AppWidgetProviderInfo info =
+ AppWidgetManager.getInstance(mContext).getAppWidgetInfo(widgetId);
+ if (info == null || !info.provider.equals(intent.getComponent())
+ || !info.getProfile().equals(user)) {
+ return null;
+ }
+ return new PendingInstallShortcutInfo(info, widgetId);
+ }
+ default:
+ Log.e(TAG, "Unknown item type");
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 165d1ea..19d9af9 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -35,11 +35,13 @@
import android.util.Log;
import android.util.LongSparseArray;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
@@ -65,7 +67,7 @@
private static final String TAG = "LoaderCursor";
- public final LongSparseArray<UserHandle> allUsers;
+ private final LongSparseArray<UserHandle> allUsers;
private final Uri mContentUri;
private final Context mContext;
@@ -92,6 +94,9 @@
private final int restoredIndex;
private final int intentIndex;
+ @Nullable
+ private LauncherActivityInfo mActivityInfo;
+
// Properties loaded per iteration
public long serialNumber;
public UserHandle user;
@@ -132,6 +137,8 @@
public boolean moveToNext() {
boolean result = super.moveToNext();
if (result) {
+ mActivityInfo = null;
+
// Load common properties.
itemType = getInt(itemTypeIndex);
container = getInt(containerIndex);
@@ -245,6 +252,10 @@
return info;
}
+ public LauncherActivityInfo getLauncherActivityInfo() {
+ return mActivityInfo;
+ }
+
/**
* Make an WorkspaceItemInfo object for a shortcut that is an application.
*/
@@ -264,25 +275,25 @@
Intent newIntent = new Intent(Intent.ACTION_MAIN, null);
newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
newIntent.setComponent(componentName);
- LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
+ mActivityInfo = mContext.getSystemService(LauncherApps.class)
.resolveActivity(newIntent, user);
- if ((lai == null) && !allowMissingTarget) {
+ if ((mActivityInfo == null) && !allowMissingTarget) {
Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
return null;
}
final WorkspaceItemInfo info = new WorkspaceItemInfo();
- info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ info.itemType = Favorites.ITEM_TYPE_APPLICATION;
info.user = user;
info.intent = newIntent;
- mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
+ mIconCache.getTitleAndIcon(info, mActivityInfo, useLowResIcon);
if (mIconCache.isDefaultIcon(info.bitmap, user)) {
loadIcon(info);
}
- if (lai != null) {
- AppInfo.updateRuntimeFlagsForActivityTarget(info, lai);
+ if (mActivityInfo != null) {
+ AppInfo.updateRuntimeFlagsForActivityTarget(info, mActivityInfo);
}
// from the db
@@ -445,7 +456,8 @@
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.
- screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
+ int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 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 d62f8d5..f6c7c06 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -41,17 +41,15 @@
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
-import android.util.MutableInt;
import android.util.TimingLogger;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
@@ -71,6 +69,7 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -85,10 +84,10 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.LooperIdleLock;
-import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.ArrayList;
@@ -97,6 +96,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CancellationException;
/**
@@ -112,6 +112,7 @@
protected final LauncherAppState mApp;
private final AllAppsList mBgAllAppsList;
protected final BgDataModel mBgDataModel;
+ private final ModelDelegate mModelDelegate;
private FirstScreenBroadcast mFirstScreenBroadcast;
@@ -126,15 +127,20 @@
private final UserManagerState mUserManagerState = new UserManagerState();
- protected Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap;
+ protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
private boolean mStopped;
+ private final Set<PackageUserKey> mPendingPackages = new HashSet<>();
+ private boolean mItemsDeleted = false;
+ private String mDbName;
+
public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
- LoaderResults results) {
+ ModelDelegate modelDelegate, LoaderResults results) {
mApp = app;
mBgAllAppsList = bgAllAppsList;
mBgDataModel = dataModel;
+ mModelDelegate = modelDelegate;
mResults = results;
mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
@@ -162,12 +168,7 @@
private void sendFirstScreenActiveInstallsBroadcast() {
ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
-
- ArrayList<ItemInfo> allItems = new ArrayList<>();
- synchronized (mBgDataModel) {
- allItems.addAll(mBgDataModel.workspaceItems);
- allItems.addAll(mBgDataModel.appWidgets);
- }
+ ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();
// Screen set is never empty
final int firstScreen = mBgDataModel.collectWorkspaceScreens().get(0);
@@ -189,13 +190,23 @@
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts);
- loadCachedPredictions();
logger.addSplit("loadWorkspace");
+ // Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
+ // sanitizeData should not be invoked if the workspace is loaded from a db different
+ // from the main db as defined in the invariant device profile.
+ // (e.g. both grid preview and minimal device mode uses a different db)
+ if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
+ verifyNotStopped();
+ sanitizeData();
+ logger.addSplit("sanitizeData");
+ }
+
verifyNotStopped();
mResults.bindWorkspace();
logger.addSplit("bindWorkspace");
+ mModelDelegate.workspaceLoadComplete();
// Notify the installer packages of packages with active installs on the first screen.
sendFirstScreenActiveInstallsBroadcast();
logger.addSplit("sendFirstScreenActiveInstallsBroadcast");
@@ -277,6 +288,7 @@
updateHandler.finish();
logger.addSplit("finish icon update");
+ mModelDelegate.modelLoadComplete();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
@@ -304,7 +316,7 @@
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
final boolean isSafeMode = pmHelper.isSafeMode();
final boolean isSdCardReady = Utilities.isBootCompleted();
- final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
+ final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false;
try {
@@ -333,6 +345,7 @@
synchronized (mBgDataModel) {
mBgDataModel.clear();
+ mPendingPackages.clear();
final HashMap<PackageUserKey, SessionInfo> installingPkgs =
mSessionHelper.getActiveSessions();
@@ -345,7 +358,9 @@
final LoaderCursor c = new LoaderCursor(
contentResolver.query(contentUri, null, selection, null, null), contentUri,
mApp, mUserManagerState);
-
+ final Bundle extras = c.getExtras();
+ mDbName = extras == null
+ ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
try {
final int appWidgetIdIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.APPWIDGET_ID);
@@ -360,15 +375,12 @@
final int optionsIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.OPTIONS);
- final LongSparseArray<UserHandle> allUsers = c.allUsers;
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
mUserManagerState.init(mUserCache, mUserManager);
for (UserHandle user : mUserCache.getUserProfiles()) {
long serialNo = mUserCache.getSerialNumberForUser(user);
- allUsers.put(serialNo, user);
-
boolean userUnlocked = mUserManager.isUserUnlocked(user);
// We can only query for shortcuts when the user is unlocked.
@@ -392,6 +404,7 @@
WorkspaceItemInfo info;
LauncherAppWidgetInfo appWidgetInfo;
+ LauncherAppWidgetProviderInfo widgetProviderInfo;
Intent intent;
String targetPkg;
@@ -419,16 +432,6 @@
ComponentName cn = intent.getComponent();
targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
- if (allUsers.indexOfValue(c.user) < 0) {
- if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Legacy shortcuts are only allowed for current users");
- continue;
- } else if (c.restoreFlag != 0) {
- // Don't restore items for other profiles.
- c.markDeleted("Restore from other profiles not supported");
- continue;
- }
- }
if (TextUtils.isEmpty(targetPkg) &&
c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
c.markDeleted("Only legacy shortcuts can have null package");
@@ -498,7 +501,7 @@
// SdCard is not ready yet. Package might get available,
// once it is ready.
Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
- pendingPackages.addToList(c.user, targetPkg);
+ mPendingPackages.add(new PackageUserKey(targetPkg, c.user));
// Add the icon on the workspace anyway.
allowMissingTarget = true;
} else {
@@ -588,15 +591,27 @@
if (isSafeMode && !isSystemApp(context, intent)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
}
+ LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
+ if (activityInfo != null) {
+ info.setProgressLevel(
+ PackageManagerHelper
+ .getLoadingProgress(activityInfo),
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+ }
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
tempPackageKey.update(targetPkg, c.user);
SessionInfo si = installingPkgs.get(tempPackageKey);
- if (si == null) {
- info.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
- } else {
- info.setInstallProgress((int) (si.getProgress() * 100));
- }
+ if (si == null) {
+ info.runtimeStatusFlags &=
+ ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+ } else if (activityInfo == null) {
+ int installProgress = (int) (si.getProgress() * 100);
+
+ info.setProgressLevel(
+ installProgress,
+ PackageInstallInfo.STATUS_INSTALLING);
+ }
}
c.checkAndAddItem(info, mBgDataModel);
@@ -652,12 +667,13 @@
final boolean wasProviderReady = !c.hasRestoreFlag(
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
- if (mWidgetProvidersMap == null) {
- mWidgetProvidersMap = WidgetManagerHelper.getAllProvidersMap(
- context);
+ ComponentKey providerKey = new ComponentKey(component, c.user);
+ if (!mWidgetProvidersMap.containsKey(providerKey)) {
+ mWidgetProvidersMap.put(providerKey,
+ widgetHelper.findProvider(component, c.user));
}
- final AppWidgetProviderInfo provider = mWidgetProvidersMap.get(
- new ComponentKey(component, c.user));
+ final AppWidgetProviderInfo provider =
+ mWidgetProvidersMap.get(providerKey);
final boolean isProviderReady = isValidProvider(provider);
if (!isSafeMode && !customWidget &&
@@ -731,6 +747,19 @@
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
continue;
}
+ widgetProviderInfo =
+ widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
+ if (widgetProviderInfo != null
+ && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
+ || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
+ // This can happen when display size changes.
+ c.markDeleted("Widget removed, min sizes not met: "
+ + "span=" + appWidgetInfo.spanX + "x"
+ + appWidgetInfo.spanY + " minSpan="
+ + widgetProviderInfo.minSpanX + "x"
+ + widgetProviderInfo.minSpanY);
+ continue;
+ }
if (!c.isOnWorkspaceOrHotseat()) {
c.markDeleted("Widget found where container != " +
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
@@ -772,43 +801,17 @@
IOUtils.closeSilently(c);
}
+ // Load delegate items
+ mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);
+
// Break early if we've stopped loading
if (mStopped) {
mBgDataModel.clear();
return;
}
- if (contentUri == LauncherSettings.Favorites.CONTENT_URI) {
- // Remove dead items
- if (c.commitDeleted()) {
- // Remove any empty folder
- int[] deletedFolderIds = LauncherSettings.Settings
- .call(contentResolver,
- LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
- .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
- for (int folderId : deletedFolderIds) {
- mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
- mBgDataModel.folders.remove(folderId);
- mBgDataModel.itemsIdMap.remove(folderId);
- }
-
- // Remove any ghost widgets
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
- }
-
- // Unpin shortcuts that don't exist on the workspace.
- HashSet<ShortcutKey> pendingShortcuts =
- InstallShortcutReceiver.getPendingShortcuts(context);
- for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
- MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
- if ((numTimesPinned == null || numTimesPinned.value == 0)
- && !pendingShortcuts.contains(key)) {
- // Shortcut is pinned but doesn't exist on the workspace; unpin it.
- mBgDataModel.unpinShortcut(context, key);
- }
- }
- }
+ // Remove dead items
+ mItemsDeleted = c.commitDeleted();
// Sort the folder items, update ranks, and make sure all preview items are high res.
FolderGridOrganizer verifier =
@@ -834,13 +837,6 @@
}
c.commitRestoredItems();
- if (!isSdCardReady && !pendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(mApp, pendingPackages),
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
- null,
- MODEL_EXECUTOR.getHandler());
- }
}
}
@@ -865,21 +861,37 @@
}
}
- @WorkerThread
- private void loadCachedPredictions() {
- synchronized (mBgDataModel) {
- List<ComponentKey> componentKeys =
- mApp.getPredictionModel().getPredictionComponentKeys();
- List<LauncherActivityInfo> l;
- mBgDataModel.cachedPredictedItems.clear();
- for (ComponentKey key : componentKeys) {
- l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
- if (l.size() == 0) continue;
- AppInfo info = new AppInfo(l.get(0), key.user,
- mUserManagerState.isUserQuiet(key.user));
- mBgDataModel.cachedPredictedItems.add(info);
- mIconCache.getTitleAndIcon(info, false);
+ private void sanitizeData() {
+ Context context = mApp.getContext();
+ ContentResolver contentResolver = context.getContentResolver();
+ if (mItemsDeleted) {
+ // Remove any empty folder
+ int[] deletedFolderIds = LauncherSettings.Settings
+ .call(contentResolver,
+ LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+ .getIntArray(LauncherSettings.Settings.EXTRA_VALUE);
+ synchronized (mBgDataModel) {
+ for (int folderId : deletedFolderIds) {
+ mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+ mBgDataModel.folders.remove(folderId);
+ mBgDataModel.itemsIdMap.remove(folderId);
+ }
}
+
+ }
+ // Remove any ghost widgets
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
+
+ // Update pinned state of model shortcuts
+ mBgDataModel.updateShortcutPinnedState(context);
+
+ if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
+ context.registerReceiver(
+ new SdCardAvailableReceiver(mApp, mPendingPackages),
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
+ null,
+ MODEL_EXECUTOR.getHandler());
}
}
@@ -914,14 +926,6 @@
PackageInstallInfo.fromInstallingState(info));
}
}
- for (AppInfo item : mBgDataModel.cachedPredictedItems) {
- List<LauncherActivityInfo> l = mLauncherApps.getActivityList(
- item.componentName.getPackageName(), item.user);
- for (LauncherActivityInfo info : l) {
- boolean quietMode = mUserManagerState.isUserQuiet(item.user);
- mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info);
- }
- }
mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
mUserManagerState.isAnyProfileQuietModeEnabled());
diff --git a/src/com/android/launcher3/model/ModelDelegate.java b/src/com/android/launcher3/model/ModelDelegate.java
new file mode 100644
index 0000000..13ec1ec
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelDelegate.java
@@ -0,0 +1,101 @@
+/*
+ * 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.launcher3.model;
+
+import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
+
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Map;
+
+/**
+ * Class to extend LauncherModel functionality to provide extra data
+ */
+public class ModelDelegate implements ResourceBasedOverride {
+
+ /**
+ * Creates and initializes a new instance of the delegate
+ */
+ public static ModelDelegate newInstance(
+ Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel) {
+ ModelDelegate delegate = Overrides.getObject(
+ ModelDelegate.class, context, R.string.model_delegate_class);
+
+ delegate.mApp = app;
+ delegate.mAppsList = appsList;
+ delegate.mDataModel = dataModel;
+ return delegate;
+ }
+
+ protected LauncherAppState mApp;
+ protected AllAppsList mAppsList;
+ protected BgDataModel mDataModel;
+
+ public ModelDelegate() { }
+
+ /**
+ * Called periodically to validate and update any data
+ */
+ @WorkerThread
+ public void validateData() {
+ if (hasShortcutsPermission(mApp.getContext())
+ != mAppsList.hasShortcutHostPermission()) {
+ mApp.getModel().forceReload();
+ }
+ }
+
+ /**
+ * Load delegate items if any in the data model
+ */
+ @WorkerThread
+ public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { }
+
+ /**
+ * Called during loader after workspace loading is complete
+ */
+ @WorkerThread
+ public void workspaceLoadComplete() { }
+
+ /**
+ * Called at the end of model load task
+ */
+ @WorkerThread
+ public void modelLoadComplete() { }
+
+ /**
+ * Called when the delegate is no loner needed
+ */
+ @WorkerThread
+ public void destroy() { }
+
+ /**
+ * Add data to a dumpsys request for Launcher (e.g. for bug reports).
+ *
+ * @see com.android.launcher3.Launcher#dump(java.lang.String, java.io.FileDescriptor,
+ * java.io.PrintWriter, java.lang.String[])
+ **/
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { }
+}
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 4efeba5..9b5fac8 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -15,17 +15,29 @@
*/
package com.android.launcher3.model;
+import static com.android.launcher3.Utilities.isValidExtraType;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Process;
+import android.util.Log;
+
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+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.util.IntArray;
import com.android.launcher3.util.IntSet;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.stream.IntStream;
/**
@@ -33,6 +45,8 @@
*/
public class ModelUtils {
+ private static final String TAG = "ModelUtils";
+
/**
* Filters the set of items who are directly or indirectly (via another container) on the
* specified screen.
@@ -42,13 +56,7 @@
ArrayList<T> currentScreenItems,
ArrayList<T> otherScreenItems) {
// Purge any null ItemInfos
- Iterator<T> iter = allWorkspaceItems.iterator();
- while (iter.hasNext()) {
- ItemInfo i = iter.next();
- if (i == null) {
- iter.remove();
- }
- }
+ allWorkspaceItems.removeIf(Objects::isNull);
// Order the set of items by their containers first, this allows use to walk through the
// list sequentially, build up a list of containers that are in the specified screen,
// as well as all items in those containers.
@@ -125,4 +133,52 @@
IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add);
return result;
}
+
+
+ /**
+ * Creates a workspace item info for the legacy shortcut intent
+ */
+ @SuppressWarnings("deprecation")
+ public static WorkspaceItemInfo fromLegacyShortcutIntent(Context context, Intent data) {
+ if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class)
+ || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.class))
+ || !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) {
+
+ Log.e(TAG, "Invalid install shortcut intent");
+ return null;
+ }
+
+ Intent launchIntent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
+ String label = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
+ if (launchIntent == null || label == null) {
+ Log.e(TAG, "Invalid install shortcut intent");
+ return null;
+ }
+
+ final WorkspaceItemInfo info = new WorkspaceItemInfo();
+ info.user = Process.myUserHandle();
+
+ BitmapInfo iconInfo = null;
+ try (LauncherIcons li = LauncherIcons.obtain(context)) {
+ Bitmap bitmap = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
+ if (bitmap != null) {
+ iconInfo = li.createIconBitmap(bitmap);
+ } else {
+ info.iconResource = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
+ if (info.iconResource != null) {
+ iconInfo = li.createIconBitmap(info.iconResource);
+ }
+ }
+ }
+
+ if (iconInfo == null) {
+ Log.e(TAG, "Invalid icon by the app");
+ return null;
+ }
+ info.bitmap = iconInfo;
+ info.contentDescription = info.title = Utilities.trim(label);
+ info.intent = launchIntent;
+ return info;
+ }
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 2c99df7..312435d 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -28,7 +28,6 @@
import android.util.Log;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
@@ -43,6 +42,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
import java.util.ArrayList;
import java.util.Arrays;
@@ -51,6 +51,7 @@
import java.util.concurrent.Executor;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
/**
* Class for handling model updates.
@@ -259,7 +260,9 @@
* Removes all the items from the database matching {@param matcher}.
*/
public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
- deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
+ deleteItemsFromDatabase(StreamSupport.stream(mBgDataModel.itemsIdMap.spliterator(), false)
+ .filter(matcher::matchesInfo)
+ .collect(Collectors.toList()));
}
/**
diff --git a/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
new file mode 100644
index 0000000..c0dc34a
--- /dev/null
+++ b/src/com/android/launcher3/model/PackageIncrementalDownloadUpdatedTask.java
@@ -0,0 +1,77 @@
+/*
+ * 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.launcher3.model;
+
+import android.os.UserHandle;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Handles updates due to incremental download progress updates.
+ */
+public class PackageIncrementalDownloadUpdatedTask extends BaseModelUpdateTask {
+
+ private final UserHandle mUser;
+ private final int mProgress;
+ private final String mPackageName;
+
+ public PackageIncrementalDownloadUpdatedTask(
+ String packageName, UserHandle user, 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) {
+ PackageInstallInfo downloadInfo = new PackageInstallInfo(
+ mPackageName,
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING,
+ mProgress,
+ mUser);
+
+ synchronized (appsList) {
+ List<AppInfo> updatedAppInfos = appsList.updatePromiseInstallInfo(downloadInfo);
+ if (!updatedAppInfos.isEmpty()) {
+ for (AppInfo appInfo : updatedAppInfos) {
+ appInfo.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+ scheduleCallbackTask(
+ c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+ }
+ }
+ bindApplicationsIfNeeded();
+ }
+
+ final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
+ synchronized (dataModel) {
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ if (mPackageName.equals(si.getTargetPackage())) {
+ si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
+ si.setProgressLevel(downloadInfo);
+ updatedWorkspaceItems.add(si);
+ }
+ });
+ }
+ bindUpdatedWorkspaceItems(updatedWorkspaceItems);
+ }
+}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 203f1ca..9889a80 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -15,21 +15,19 @@
*/
package com.android.launcher3.model;
-import android.content.ComponentName;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.data.AppInfo;
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.PromiseAppInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.InstantAppResolver;
import java.util.HashSet;
+import java.util.List;
/**
* Handles changes due to a sessions updates for a currently installing app.
@@ -61,30 +59,30 @@
}
synchronized (apps) {
- PromiseAppInfo updated = apps.updatePromiseInstallInfo(mInstallInfo);
- if (updated != null) {
- scheduleCallbackTask(c -> c.bindPromiseAppProgressUpdated(updated));
+ List<AppInfo> updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo);
+ if (!updatedAppInfos.isEmpty()) {
+ for (AppInfo appInfo : updatedAppInfos) {
+ scheduleCallbackTask(c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
+ }
}
bindApplicationsIfNeeded();
}
synchronized (dataModel) {
final HashSet<ItemInfo> updates = new HashSet<>();
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (si.hasPromiseIconUi() && (cn != null)
- && mInstallInfo.packageName.equals(cn.getPackageName())) {
- si.setInstallProgress(mInstallInfo.progress);
- if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
- // Mark this info as broken.
- si.status &= ~WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE;
- }
- updates.add(si);
+ dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
+ if (si.hasPromiseIconUi()
+ && mInstallInfo.packageName.equals(si.getTargetPackage())) {
+ int installProgress = mInstallInfo.progress;
+
+ si.setProgressLevel(installProgress, PackageInstallInfo.STATUS_INSTALLING);
+ if (mInstallInfo.state == PackageInstallInfo.STATUS_FAILED) {
+ // Mark this info as broken.
+ si.runtimeStatusFlags &= ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
}
+ updates.add(si);
}
- }
+ });
for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
@@ -94,12 +92,7 @@
}
if (!updates.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindRestoreItemsChange(updates);
- }
- });
+ scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
}
}
}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 7cd467e..7bfa3ef 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -22,18 +22,16 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
-import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
@@ -42,10 +40,11 @@
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.pm.PackageInstallInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.FlagOp;
-import com.android.launcher3.util.IntSparseArrayMap;
+import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
@@ -54,6 +53,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -92,10 +92,13 @@
final String[] packages = mPackages;
final int N = packages.length;
- FlagOp flagOp = FlagOp.NO_OP;
+ final FlagOp flagOp;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
- ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
+ final ItemInfoMatcher matcher = mOp == OP_USER_AVAILABILITY_CHANGE
+ ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
+ : ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet<ComponentName> removedComponents = new HashSet<>();
+ final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
switch (mOp) {
case OP_ADD: {
@@ -105,12 +108,8 @@
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
appsList.removePackage(packages[i], mUser);
}
- appsList.addPackage(context, packages[i], mUser);
-
- // Automatically add homescreen icon for work profile apps for below O device.
- if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
- SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
- }
+ activitiesLists.put(
+ packages[i], appsList.addPackage(context, packages[i], mUser));
}
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
@@ -121,7 +120,8 @@
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
- appsList.updatePackage(context, packages[i], mUser);
+ activitiesLists.put(
+ packages[i], appsList.updatePackage(context, packages[i], mUser));
app.getWidgetCache().removePackage(packages[i], mUser);
}
}
@@ -158,19 +158,22 @@
flagOp = ums.isUserQuiet(mUser)
? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER)
: FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
- // We want to update all packages for this user.
- matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp);
// We are not synchronizing here, as int operations are atomic
appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break;
}
+ default:
+ flagOp = FlagOp.NO_OP;
+ break;
}
bindApplicationsIfNeeded();
- final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
+ final IntSet removedShortcuts = new IntSet();
+ // Shortcuts to keep even if the corresponding app was removed
+ final IntSet forceKeepShortcuts = new IntSet();
// Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
@@ -180,118 +183,128 @@
// For system apps, package manager send OP_UPDATE when an app is enabled.
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
- if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) info;
- boolean infoUpdated = false;
- boolean shortcutUpdated = false;
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
- // Update shortcuts which use iconResource.
- if ((si.iconResource != null)
- && packageSet.contains(si.iconResource.packageName)) {
- LauncherIcons li = LauncherIcons.obtain(context);
- BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
- li.recycle();
- if (iconInfo != null) {
- si.bitmap = iconInfo;
+ boolean infoUpdated = false;
+ boolean shortcutUpdated = false;
+
+ // Update shortcuts which use iconResource.
+ if ((si.iconResource != null)
+ && packageSet.contains(si.iconResource.packageName)) {
+ LauncherIcons li = LauncherIcons.obtain(context);
+ BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
+ li.recycle();
+ if (iconInfo != null) {
+ si.bitmap = iconInfo;
+ infoUpdated = true;
+ }
+ }
+
+ ComponentName cn = si.getTargetComponent();
+ if (cn != null && matcher.matches(si, cn)) {
+ String packageName = cn.getPackageName();
+
+ if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
+ forceKeepShortcuts.add(si.id);
+ if (mOp == OP_REMOVE) {
+ return;
+ }
+ }
+
+ if (si.isPromise() && isNewApkAvailable) {
+ boolean isTargetValid = true;
+ if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ List<ShortcutInfo> shortcut =
+ new ShortcutRequest(context, mUser)
+ .forPackage(cn.getPackageName(),
+ si.getDeepShortcutId())
+ .query(ShortcutRequest.PINNED);
+ if (shortcut.isEmpty()) {
+ isTargetValid = false;
+ } else {
+ si.updateFromDeepShortcutInfo(shortcut.get(0), context);
+ infoUpdated = true;
+ }
+ } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
+ isTargetValid = context.getSystemService(LauncherApps.class)
+ .isActivityEnabled(cn, mUser);
+ }
+ if (!isTargetValid && si.hasStatusFlag(
+ FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
+ if (updateWorkspaceItemIntent(context, si, packageName)) {
+ infoUpdated = true;
+ } else if (si.hasPromiseIconUi()) {
+ removedShortcuts.add(si.id);
+ return;
+ }
+ } else if (!isTargetValid) {
+ removedShortcuts.add(si.id);
+ FileLog.e(TAG, "Restored shortcut no longer valid "
+ + si.getIntent());
+ return;
+ } else {
+ si.status = WorkspaceItemInfo.DEFAULT;
+ infoUpdated = true;
+ }
+ } else if (isNewApkAvailable && removedComponents.contains(cn)) {
+ if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
}
}
- ComponentName cn = si.getTargetComponent();
- if (cn != null && matcher.matches(si, cn)) {
- String packageName = cn.getPackageName();
-
- if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
- removedShortcuts.put(si.id, false);
- if (mOp == OP_REMOVE) {
- continue;
- }
- }
-
- if (si.isPromise() && isNewApkAvailable) {
- boolean isTargetValid = true;
- if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- List<ShortcutInfo> shortcut =
- new ShortcutRequest(context, mUser)
- .forPackage(cn.getPackageName(),
- si.getDeepShortcutId())
- .query(ShortcutRequest.PINNED);
- if (shortcut.isEmpty()) {
- isTargetValid = false;
- } else {
- si.updateFromDeepShortcutInfo(shortcut.get(0), context);
- infoUpdated = true;
- }
- } else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
- isTargetValid = context.getSystemService(LauncherApps.class)
- .isActivityEnabled(cn, mUser);
- }
- if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
- if (updateWorkspaceItemIntent(context, si, packageName)) {
- infoUpdated = true;
- } else if (si.hasPromiseIconUi()) {
- removedShortcuts.put(si.id, true);
- continue;
- }
- } else if (!isTargetValid) {
- removedShortcuts.put(si.id, true);
- FileLog.e(TAG, "Restored shortcut no longer valid "
- + si.getIntent());
- continue;
- } else {
- si.status = WorkspaceItemInfo.DEFAULT;
- infoUpdated = true;
- }
- } else if (isNewApkAvailable && removedComponents.contains(cn)) {
- if (updateWorkspaceItemIntent(context, si, packageName)) {
- infoUpdated = true;
- }
- }
-
- if (isNewApkAvailable &&
- si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
+ if (isNewApkAvailable) {
+ List<LauncherActivityInfo> activities = activitiesLists.get(
+ packageName);
+ si.setProgressLevel(
+ activities == null || activities.isEmpty()
+ ? 100
+ : PackageManagerHelper.getLoadingProgress(
+ activities.get(0)),
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
+ if (si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
}
-
- int oldRuntimeFlags = si.runtimeStatusFlags;
- si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
- if (si.runtimeStatusFlags != oldRuntimeFlags) {
- shortcutUpdated = true;
- }
}
- if (infoUpdated || shortcutUpdated) {
- updatedWorkspaceItems.add(si);
+ int oldRuntimeFlags = si.runtimeStatusFlags;
+ si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
+ if (si.runtimeStatusFlags != oldRuntimeFlags) {
+ shortcutUpdated = true;
}
- if (infoUpdated) {
- getModelWriter().updateItemInDatabase(si);
- }
- } else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
- LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
- if (mUser.equals(widgetInfo.user)
- && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
- && packageSet.contains(widgetInfo.providerName.getPackageName())) {
- widgetInfo.restoreStatus &=
- ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
- ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+ }
- // adding this flag ensures that launcher shows 'click to setup'
- // if the widget has a config activity. In case there is no config
- // activity, it will be marked as 'restored' during bind.
- widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ if (infoUpdated || shortcutUpdated) {
+ updatedWorkspaceItems.add(si);
+ }
+ if (infoUpdated && si.id != ItemInfo.NO_ID) {
+ getModelWriter().updateItemInDatabase(si);
+ }
+ });
- widgets.add(widgetInfo);
- getModelWriter().updateItemInDatabase(widgetInfo);
- }
+ for (LauncherAppWidgetInfo widgetInfo : dataModel.appWidgets) {
+ if (mUser.equals(widgetInfo.user)
+ && widgetInfo.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+ && packageSet.contains(widgetInfo.providerName.getPackageName())) {
+ widgetInfo.restoreStatus &=
+ ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY
+ & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+
+ // adding this flag ensures that launcher shows 'click to setup'
+ // if the widget has a config activity. In case there is no config
+ // activity, it will be marked as 'restored' during bind.
+ widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
+ widgets.add(widgetInfo);
+ getModelWriter().updateItemInDatabase(widgetInfo);
}
}
}
bindUpdatedWorkspaceItems(updatedWorkspaceItems);
if (!removedShortcuts.isEmpty()) {
- deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts));
}
if (!widgets.isEmpty()) {
@@ -319,14 +332,15 @@
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser)
.or(ItemInfoMatcher.ofComponents(removedComponents, mUser))
- .and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
+ .and(ItemInfoMatcher.ofItemIds(forceKeepShortcuts).negate());
deleteAndBindComponentsRemoved(removeMatch);
// Remove any queued items from the install queue
- InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
+ ItemInstallQueue.INSTANCE.get(context)
+ .removeFromInstallQueue(removedPackages, mUser);
}
- if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
+ if (mOp == OP_ADD) {
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < N; i++) {
@@ -342,6 +356,11 @@
*/
private boolean updateWorkspaceItemIntent(Context context,
WorkspaceItemInfo si, String packageName) {
+ if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ // Do not update intent for deep shortcuts as they contain additional information
+ // about the shortcut.
+ return false;
+ }
// Try to find the best match activity.
Intent intent = new PackageManagerHelper(context).getAppLaunchIntent(packageName, mUser);
if (intent != null) {
diff --git a/src/com/android/launcher3/model/PredictionModel.java b/src/com/android/launcher3/model/PredictionModel.java
deleted file mode 100644
index cb3903d..0000000
--- a/src/com/android/launcher3/model/PredictionModel.java
+++ /dev/null
@@ -1,136 +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.launcher3.model;
-
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.UserHandle;
-
-import androidx.annotation.AnyThread;
-import androidx.annotation.WorkerThread;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.ResourceBasedOverride;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Model Helper for app predictions
- */
-public class PredictionModel implements ResourceBasedOverride {
-
- private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
- private static final int MAX_CACHE_ITEMS = 5;
-
- protected Context mContext;
- private SharedPreferences mDevicePrefs;
- private UserCache mUserCache;
-
-
- /**
- * Retrieve instance of this object that can be overridden in runtime based on the build
- * variant of the application.
- */
- public static PredictionModel newInstance(Context context) {
- PredictionModel model = Overrides.getObject(PredictionModel.class, context,
- R.string.prediction_model_class);
- model.init(context);
- return model;
- }
-
- protected void init(Context context) {
- mContext = context;
- mDevicePrefs = Utilities.getDevicePrefs(mContext);
- mUserCache = UserCache.INSTANCE.get(mContext);
-
- }
-
- /**
- * Formats and stores a list of component key in device preferences.
- */
- @AnyThread
- public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
- MODEL_EXECUTOR.execute(() -> {
- LauncherAppState appState = LauncherAppState.getInstance(mContext);
- StringBuilder builder = new StringBuilder();
- int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
- for (int i = 0; i < count; i++) {
- builder.append(serializeComponentKeyToString(componentKeys.get(i)));
- builder.append("\n");
- }
- if (componentKeys.isEmpty() /* should invalidate loader items */) {
- appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
- @Override
- public void execute(LauncherAppState app, BgDataModel model, AllAppsList apps) {
- model.cachedPredictedItems.clear();
- }
- });
- }
- mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
- });
- }
-
- /**
- * parses and returns ComponentKeys saved by
- * {@link PredictionModel#cachePredictionComponentKeys(List)}
- */
- @WorkerThread
- public List<ComponentKey> getPredictionComponentKeys() {
- Preconditions.assertWorkerThread();
- ArrayList<ComponentKey> items = new ArrayList<>();
- String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
- for (String line : cachedBlob.split("\n")) {
- ComponentKey key = getComponentKeyFromSerializedString(line);
- if (key != null) {
- items.add(key);
- }
-
- }
- return items;
- }
-
- private String serializeComponentKeyToString(ComponentKey componentKey) {
- long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user);
- return componentKey.componentName.flattenToString() + "#" + userSerialNumber;
- }
-
- private ComponentKey getComponentKeyFromSerializedString(String str) {
- int sep = str.indexOf('#');
- if (sep < 0 || (sep + 1) >= str.length()) {
- return null;
- }
- ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep));
- if (componentName == null) {
- return null;
- }
- try {
- long serialNumber = Long.parseLong(str.substring(sep + 1));
- UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber);
- return userHandle != null ? new ComponentKey(componentName, userHandle) : null;
- } catch (NumberFormatException ex) {
- return null;
- }
- }
-}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index eb3cb52..3798575 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -24,12 +24,11 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Map.Entry;
+import java.util.Set;
/**
* Helper class to re-query app status when SD-card becomes available.
@@ -42,10 +41,9 @@
private final LauncherModel mModel;
private final Context mContext;
- private final MultiHashMap<UserHandle, String> mPackages;
+ private final Set<PackageUserKey> mPackages;
- public SdCardAvailableReceiver(LauncherAppState app,
- MultiHashMap<UserHandle, String> packages) {
+ public SdCardAvailableReceiver(LauncherAppState app, Set<PackageUserKey> packages) {
mModel = app.getModel();
mContext = app.getContext();
mPackages = packages;
@@ -55,19 +53,17 @@
public void onReceive(Context context, Intent intent) {
final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
- for (Entry<UserHandle, ArrayList<String>> entry : mPackages.entrySet()) {
- UserHandle user = entry.getKey();
+ for (PackageUserKey puk : mPackages) {
+ UserHandle user = puk.mUser;
final ArrayList<String> packagesRemoved = new ArrayList<>();
final ArrayList<String> packagesUnavailable = new ArrayList<>();
- for (String pkg : new HashSet<>(entry.getValue())) {
- if (!launcherApps.isPackageEnabled(pkg, user)) {
- if (pmHelper.isAppOnSdcard(pkg, user)) {
- packagesUnavailable.add(pkg);
- } else {
- packagesRemoved.add(pkg);
- }
+ if (!launcherApps.isPackageEnabled(puk.mPackageName, user)) {
+ if (pmHelper.isAppOnSdcard(puk.mPackageName, user)) {
+ packagesUnavailable.add(puk.mPackageName);
+ } else {
+ packagesRemoved.add(puk.mPackageName);
}
}
if (!packagesRemoved.isEmpty()) {
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index 1cbe5c2..4296d32 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -21,16 +21,17 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
/**
* Handles changes due to shortcut manager updates (deep shortcut changes)
@@ -54,54 +55,61 @@
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
final Context context = app.getContext();
// Find WorkspaceItemInfo's that have changed on the workspace.
- HashSet<ShortcutKey> removedKeys = new HashSet<>();
- MultiHashMap<ShortcutKey, WorkspaceItemInfo> keyToShortcutInfo = new MultiHashMap<>();
- HashSet<String> allIds = new HashSet<>();
+ ArrayList<WorkspaceItemInfo> matchingWorkspaceItems = new ArrayList<>();
- for (ItemInfo itemInfo : dataModel.itemsIdMap) {
- if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
- if (mPackageName.equals(si.getIntent().getPackage()) && si.user.equals(mUser)) {
- keyToShortcutInfo.addToList(ShortcutKey.fromItemInfo(si), si);
- allIds.add(si.getDeepShortcutId());
+ synchronized (dataModel) {
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ if ((si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && mPackageName.equals(si.getIntent().getPackage())) {
+ matchingWorkspaceItems.add(si);
}
- }
+ });
}
- final ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
- if (!keyToShortcutInfo.isEmpty()) {
+ if (!matchingWorkspaceItems.isEmpty()) {
+ if (mShortcuts.isEmpty()) {
+ // Verify that the app is indeed installed.
+ if (!new PackageManagerHelper(app.getContext())
+ .isAppInstalled(mPackageName, mUser)) {
+ // App is not installed, ignoring package events
+ return;
+ }
+ }
// Update the workspace to reflect the changes to updated shortcuts residing on it.
+ List<String> allLauncherKnownIds = matchingWorkspaceItems.stream()
+ .map(WorkspaceItemInfo::getDeepShortcutId)
+ .distinct()
+ .collect(Collectors.toList());
List<ShortcutInfo> shortcuts = new ShortcutRequest(context, mUser)
- .forPackage(mPackageName, new ArrayList<>(allIds))
+ .forPackage(mPackageName, allLauncherKnownIds)
.query(ShortcutRequest.ALL);
+
+ Set<String> nonPinnedIds = new HashSet<>(allLauncherKnownIds);
+ ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
for (ShortcutInfo fullDetails : shortcuts) {
- ShortcutKey key = ShortcutKey.fromInfo(fullDetails);
- List<WorkspaceItemInfo> workspaceItemInfos = keyToShortcutInfo.remove(key);
if (!fullDetails.isPinned()) {
- // The shortcut was previously pinned but is no longer, so remove it from
- // the workspace and our pinned shortcut counts.
- // Note that we put this check here, after querying for full details,
- // because there's a possible race condition between pinning and
- // receiving this callback.
- removedKeys.add(key);
continue;
}
- for (final WorkspaceItemInfo workspaceItemInfo : workspaceItemInfos) {
- workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
- app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
- updatedWorkspaceItemInfos.add(workspaceItemInfo);
- }
+
+ String sid = fullDetails.getId();
+ nonPinnedIds.remove(sid);
+ matchingWorkspaceItems
+ .stream()
+ .filter(itemInfo -> sid.equals(itemInfo.getDeepShortcutId()))
+ .forEach(workspaceItemInfo -> {
+ workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context);
+ app.getIconCache().getShortcutIcon(workspaceItemInfo, fullDetails);
+ updatedWorkspaceItemInfos.add(workspaceItemInfo);
+ });
}
- }
- // If there are still entries in keyToShortcutInfo, that means that
- // the corresponding shortcuts weren't passed in onShortcutsChanged(). This
- // means they were cleared, so we remove and unpin them now.
- removedKeys.addAll(keyToShortcutInfo.keySet());
-
- bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
- if (!keyToShortcutInfo.isEmpty()) {
- deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(removedKeys));
+ bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
+ if (!nonPinnedIds.isEmpty()) {
+ deleteAndBindComponentsRemoved(ItemInfoMatcher.ofShortcutKeys(
+ nonPinnedIds.stream()
+ .map(id -> new ShortcutKey(mPackageName, mUser, id))
+ .collect(Collectors.toSet())));
+ }
}
if (mUpdateIdMap) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 7ec884f..5048e13 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -23,7 +23,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -73,27 +72,27 @@
ArrayList<WorkspaceItemInfo> updatedWorkspaceItemInfos = new ArrayList<>();
HashSet<ShortcutKey> removedKeys = new HashSet<>();
- for (ItemInfo itemInfo : dataModel.itemsIdMap) {
- if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
- && mUser.equals(itemInfo.user)) {
- WorkspaceItemInfo si = (WorkspaceItemInfo) itemInfo;
- if (mIsUserUnlocked) {
- ShortcutKey key = ShortcutKey.fromItemInfo(si);
- ShortcutInfo shortcut = pinnedShortcuts.get(key);
- // We couldn't verify the shortcut during loader. If its no longer available
- // (probably due to clear data), delete the workspace item as well
- if (shortcut == null) {
- removedKeys.add(key);
- continue;
+ synchronized (dataModel) {
+ dataModel.forAllWorkspaceItemInfos(mUser, si -> {
+ if (si.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (mIsUserUnlocked) {
+ ShortcutKey key = ShortcutKey.fromItemInfo(si);
+ ShortcutInfo shortcut = pinnedShortcuts.get(key);
+ // We couldn't verify the shortcut during loader. If its no longer available
+ // (probably due to clear data), delete the workspace item as well
+ if (shortcut == null) {
+ removedKeys.add(key);
+ return;
+ }
+ si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
+ si.updateFromDeepShortcutInfo(shortcut, context);
+ app.getIconCache().getShortcutIcon(si, shortcut);
+ } else {
+ si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
- si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
- si.updateFromDeepShortcutInfo(shortcut, context);
- app.getIconCache().getShortcutIcon(si, shortcut);
- } else {
- si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
+ updatedWorkspaceItemInfos.add(si);
}
- updatedWorkspaceItemInfos.add(si);
- }
+ });
}
bindUpdatedWorkspaceItems(updatedWorkspaceItemInfos);
if (!removedKeys.isEmpty()) {
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
index 37c089e..97071bb 100644
--- a/src/com/android/launcher3/model/WidgetItem.java
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -4,11 +4,11 @@
import android.content.pm.PackageManager;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
/**
* An wrapper over various items displayed in a widget picker,
@@ -43,4 +43,20 @@
activityInfo = info;
spanX = spanY = 1;
}
+
+ /**
+ * Returns {@code true} if this {@link WidgetItem} has the same type as the given
+ * {@code otherItem}.
+ *
+ * For example, both items are widgets or both items are shortcuts.
+ */
+ public boolean hasSameType(WidgetItem otherItem) {
+ if (widgetInfo != null && otherItem.widgetInfo != null) {
+ return true;
+ }
+ if (activityInfo != null && otherItem.activityInfo != null) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/model/data/AppInfo.java b/src/com/android/launcher3/model/data/AppInfo.java
index b17b062..7f70bad 100644
--- a/src/com/android/launcher3/model/data/AppInfo.java
+++ b/src/com/android/launcher3/model/data/AppInfo.java
@@ -28,10 +28,13 @@
import android.os.UserHandle;
import android.os.UserManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities;
+import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
@@ -91,8 +94,6 @@
componentName = info.componentName;
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
- user = info.user;
- runtimeStatusFlags = info.runtimeStatusFlags;
}
@VisibleForTesting
@@ -104,13 +105,38 @@
this.intent = intent;
}
+ public AppInfo(@NonNull PackageInstallInfo installInfo) {
+ componentName = installInfo.componentName;
+ intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(componentName)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ setProgressLevel(installInfo);
+ user = installInfo.user;
+ }
+
@Override
protected String dumpProperties() {
return super.dumpProperties() + " componentName=" + componentName;
}
public WorkspaceItemInfo makeWorkspaceItem() {
- return new WorkspaceItemInfo(this);
+ WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(this);
+
+ if ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ // We need to update the component name when the apk is installed
+ workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
+ // Since the user is manually placing it on homescreen, it should not be auto-removed
+ // later
+ workspaceItemInfo.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
+ workspaceItemInfo.status |= FLAG_INSTALL_SESSION_ACTIVE;
+ }
+ if ((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+ workspaceItemInfo.runtimeStatusFlags |= FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+ }
+
+ return workspaceItemInfo;
}
public ComponentKey toComponentKey() {
@@ -129,6 +155,12 @@
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
+ @Nullable
+ @Override
+ public ComponentName getTargetComponent() {
+ return componentName;
+ }
+
public static void updateRuntimeFlagsForActivityTarget(
ItemInfoWithIcon info, LauncherActivityInfo lai) {
ApplicationInfo appInfo = lai.getApplicationInfo();
@@ -138,12 +170,16 @@
info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
- if (Utilities.ATLEAST_OREO
- && appInfo.targetSdkVersion >= Build.VERSION_CODES.O
+ if (appInfo.targetSdkVersion >= Build.VERSION_CODES.O
&& Process.myUserHandle().equals(lai.getUser())) {
// The icon for a non-primary user is badged, hence it's not exactly an adaptive icon.
info.runtimeStatusFlags |= FLAG_ADAPTIVE_ICON;
}
+
+ // Sets the progress level, installation and incremental download flags.
+ info.setProgressLevel(
+ PackageManagerHelper.getLoadingProgress(lai),
+ PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
}
@Override
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 41ccbd7..cd2ef35 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -20,15 +20,9 @@
import static androidx.core.util.Preconditions.checkNotNull;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.logger.LauncherAtom.Attribute.EMPTY_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL;
import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
import android.os.Process;
@@ -40,16 +34,15 @@
import com.android.launcher3.folder.FolderNameInfos;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.Attribute;
+import com.android.launcher3.logger.LauncherAtom.FolderIcon;
import com.android.launcher3.logger.LauncherAtom.FromState;
import com.android.launcher3.logger.LauncherAtom.ToState;
import com.android.launcher3.model.ModelWriter;
-import com.android.launcher3.userevent.LauncherLogProto;
-import com.android.launcher3.userevent.LauncherLogProto.Target;
-import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
-import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.OptionalInt;
import java.util.stream.IntStream;
@@ -147,9 +140,16 @@
* @param item
*/
public void remove(WorkspaceItemInfo item, boolean animate) {
- contents.remove(item);
+ removeAll(Collections.singletonList(item), animate);
+ }
+
+ /**
+ * Remove all matching app or shortcut. Does not change the DB.
+ */
+ public void removeAll(List<WorkspaceItemInfo> items, boolean animate) {
+ contents.removeAll(items);
for (int i = 0; i < mListeners.size(); i++) {
- mListeners.get(i).onRemove(item);
+ mListeners.get(i).onRemove(items);
}
itemsChanged(animate);
}
@@ -176,9 +176,9 @@
}
public interface FolderListener {
- public void onAdd(WorkspaceItemInfo item, int rank);
- public void onRemove(WorkspaceItemInfo item);
- public void onItemsChanged(boolean animate);
+ void onAdd(WorkspaceItemInfo item, int rank);
+ void onRemove(List<WorkspaceItemInfo> item);
+ void onItemsChanged(boolean animate);
}
public boolean hasOption(int optionFlag) {
@@ -209,8 +209,13 @@
@Override
public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
+ FolderIcon.Builder folderIcon = FolderIcon.newBuilder()
+ .setCardinality(contents.size());
+ if (LabelState.SUGGESTED.equals(getLabelState())) {
+ folderIcon.setLabelInfo(title.toString());
+ }
return getDefaultItemInfoBuilder()
- .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
+ .setFolderIcon(folderIcon)
.setRank(rank)
.setAttribute(getLabelState().mLogAttribute)
.setContainerInfo(getContainerInfo())
@@ -359,113 +364,4 @@
}
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
}
-
- /**
- * Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
- *
- * @deprecated This method is used only for validation purpose and soon will be removed.
- */
- @Deprecated
- public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent(FromState fromState,
- ToState toState) {
- return LauncherLogProto.LauncherEvent.newBuilder()
- .setAction(LauncherLogProto.Action
- .newBuilder()
- .setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
- .addSrcTarget(Target
- .newBuilder()
- .setType(Target.Type.ITEM)
- .setItemType(LauncherLogProto.ItemType.EDITTEXT)
- .setFromFolderLabelState(convertFolderLabelState(fromState))
- .setToFolderLabelState(convertFolderLabelState(toState)))
- .addSrcTarget(Target.newBuilder()
- .setType(Target.Type.CONTAINER)
- .setContainerType(LauncherLogProto.ContainerType.FOLDER)
- .setPageIndex(screenId)
- .setGridX(cellX)
- .setGridY(cellY)
- .setCardinality(contents.size()))
- .addSrcTarget(newParentContainerTarget())
- .build();
- }
-
- /**
- * @deprecated This method is used only for validation purpose and soon will be removed.
- */
- @Deprecated
- private Target.Builder newParentContainerTarget() {
- Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
- switch (container) {
- case CONTAINER_HOTSEAT:
- return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
- case CONTAINER_DESKTOP:
- return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
- default:
- throw new AssertionError(String
- .format("Expected container to be either %s or %s but found %s.",
- CONTAINER_HOTSEAT,
- CONTAINER_DESKTOP,
- container));
- }
- }
-
- /**
- * @deprecated This method is used only for validation purpose and soon will be removed.
- */
- @Deprecated
- private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
- switch (fromState) {
- case FROM_EMPTY:
- return FROM_EMPTY;
- case FROM_SUGGESTED:
- return FROM_SUGGESTED;
- case FROM_CUSTOM:
- return FROM_CUSTOM;
- default:
- return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
- }
- }
-
- /**
- * @deprecated This method is used only for validation purpose and soon will be removed.
- */
- @Deprecated
- private static ToFolderLabelState convertFolderLabelState(ToState toState) {
- switch (toState) {
- case UNCHANGED:
- return ToFolderLabelState.UNCHANGED;
- case TO_SUGGESTION0:
- return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
- case TO_SUGGESTION1_WITH_VALID_PRIMARY:
- return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
- case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
- return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
- case TO_SUGGESTION2_WITH_VALID_PRIMARY:
- return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
- case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
- return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
- case TO_SUGGESTION3_WITH_VALID_PRIMARY:
- return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
- case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
- return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
- case TO_EMPTY_WITH_VALID_PRIMARY:
- return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
- case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
- return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
- return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
- case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
- return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
- case TO_CUSTOM_WITH_VALID_PRIMARY:
- return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
- case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
- return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
- return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
- case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
- return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
- default:
- return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
- }
- }
}
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index b9b3d38..e54f1e7 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -24,15 +24,16 @@
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_TASKFOREGROUND;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
+import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
+import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
import android.content.ComponentName;
import android.content.ContentValues;
@@ -51,9 +52,10 @@
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;
-import com.android.launcher3.logger.LauncherAtom.TaskForegroundContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.util.ContentWriter;
@@ -182,6 +184,24 @@
return Optional.ofNullable(getIntent()).map(Intent::getComponent).orElse(mComponentName);
}
+ /**
+ * Returns this item's package name.
+ *
+ * Prioritizes the component package name, then uses the intent package name as a fallback.
+ * This ensures deep shortcuts are supported.
+ */
+ @Nullable
+ public String getTargetPackage() {
+ ComponentName component = getTargetComponent();
+ Intent intent = getIntent();
+
+ return component != null
+ ? component.getPackageName()
+ : intent != null
+ ? intent.getPackage()
+ : null;
+ }
+
public void writeToValues(ContentWriter writer) {
writer.put(LauncherSettings.Favorites.ITEM_TYPE, itemType)
.put(LauncherSettings.Favorites.CONTAINER, container)
@@ -225,7 +245,7 @@
protected String dumpProperties() {
return "id=" + id
+ " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
- + " container=" + LauncherSettings.Favorites.containerToString(container)
+ + " container=" + getContainerInfo()
+ " targetComponent=" + getTargetComponent()
+ " screen=" + screenId
+ " cell(" + cellX + "," + cellY + ")"
@@ -259,12 +279,6 @@
}
/**
- * Can be overridden by inherited classes to fill in {@link LauncherAtom.ItemInfo}
- */
- public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
- }
-
- /**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
public LauncherAtom.ItemInfo buildProto() {
@@ -287,6 +301,18 @@
.orElse(LauncherAtom.Application.newBuilder()));
break;
case ITEM_TYPE_DEEP_SHORTCUT:
+ itemBuilder
+ .setShortcut(nullableComponent
+ .map(component -> {
+ Shortcut.Builder lsb = Shortcut.newBuilder()
+ .setShortcutName(component.flattenToShortString());
+ Optional.ofNullable(getIntent())
+ .map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
+ .ifPresent(lsb::setShortcutId);
+ return lsb;
+ })
+ .orElse(LauncherAtom.Shortcut.newBuilder()));
+ break;
case ITEM_TYPE_SHORTCUT:
itemBuilder
.setShortcut(nullableComponent
@@ -346,7 +372,10 @@
return itemBuilder;
}
- protected ContainerInfo getContainerInfo() {
+ /**
+ * Returns {@link ContainerInfo} used when logging this item.
+ */
+ public ContainerInfo getContainerInfo() {
switch (container) {
case CONTAINER_HOTSEAT:
return ContainerInfo.newBuilder()
@@ -394,17 +423,23 @@
return ContainerInfo.newBuilder()
.setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
.build();
- case CONTAINER_TASKFOREGROUND:
+ case EXTENDED_CONTAINERS:
return ContainerInfo.newBuilder()
- .setTaskForegroundContainer(TaskForegroundContainer.getDefaultInstance())
+ .setExtendedContainers(getExtendedContainer())
.build();
-
-
}
return ContainerInfo.getDefaultInstance();
}
/**
+ * Returns non-AOSP container wrapped by {@link ExtendedContainers} object. Should be overridden
+ * by build variants.
+ */
+ protected ExtendedContainers getExtendedContainer() {
+ return ExtendedContainers.getDefaultInstance();
+ }
+
+ /**
* Returns shallow copy of the object.
*/
public ItemInfo makeShallowCopy() {
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index d95f94f..d95e708 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -16,7 +16,15 @@
package com.android.launcher3.model.data;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.Nullable;
+
import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.pm.PackageInstallInfo;
+import com.android.launcher3.util.PackageManagerHelper;
/**
* Represents an ItemInfo which also holds an icon.
@@ -88,17 +96,42 @@
public static final int FLAG_ICON_BADGED = 1 << 9;
/**
+ * The icon is being installed. If {@link WorkspaceItemInfo#FLAG_RESTORED_ICON} or
+ * {@link WorkspaceItemInfo#FLAG_AUTOINSTALL_ICON} is set, then the icon is either being
+ * installed or is in a broken state.
+ */
+ public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 10;
+
+ /**
+ * This icon is still being downloaded.
+ */
+ public static final int FLAG_INCREMENTAL_DOWNLOAD_ACTIVE = 1 << 11;
+
+ public static final int FLAG_SHOW_DOWNLOAD_PROGRESS_MASK = FLAG_INSTALL_SESSION_ACTIVE
+ | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+
+ /**
* Status associated with the system state of the underlying item. This is calculated every
* time a new info is created and not persisted on the disk.
*/
public int runtimeStatusFlags = 0;
+ /**
+ * The download progress of the package that this shortcut represents. For legacy apps, this
+ * will always be the installation progress. For apps that support incremental downloads, this
+ * will only match be the installation progress until the app is installed, then this will the
+ * total download progress.
+ */
+ private int mProgressLevel = 100;
+
protected ItemInfoWithIcon() { }
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
bitmap = info.bitmap;
+ mProgressLevel = info.mProgressLevel;
runtimeStatusFlags = info.runtimeStatusFlags;
+ user = info.user;
}
@Override
@@ -114,6 +147,72 @@
}
/**
+ * Returns whether the app this shortcut represents is able to be started. For legacy apps,
+ * this returns whether it is fully installed. For apps that support incremental downloads,
+ * this returns whether the app is either fully downloaded or has installed and is downloading
+ * incrementally.
+ */
+ public boolean isAppStartable() {
+ return ((runtimeStatusFlags & FLAG_INSTALL_SESSION_ACTIVE) == 0)
+ && (((runtimeStatusFlags & FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0)
+ || mProgressLevel == 100);
+ }
+
+ /**
+ * Returns the download progress for the app this shortcut represents. If this app is not yet
+ * installed or does not support incremental downloads, this will return the installation
+ * progress.
+ */
+ public int getProgressLevel() {
+ if ((runtimeStatusFlags & FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+ return mProgressLevel;
+ }
+ return 100;
+ }
+
+ /**
+ * Sets the download progress for the app this shortcut represents. If this app is not yet
+ * installed or does not support incremental downloads, this will set
+ * {@code FLAG_INSTALL_SESSION_ACTIVE}. If this app is downloading incrementally, this will
+ * set {@code FLAG_INCREMENTAL_DOWNLOAD_ACTIVE}. Otherwise, this will remove both flags.
+ */
+ public void setProgressLevel(PackageInstallInfo installInfo) {
+ setProgressLevel(installInfo.progress, installInfo.state);
+ }
+
+ /**
+ * Sets the download progress for the app this shortcut represents.
+ */
+ public void setProgressLevel(int progress, int status) {
+ if (status == PackageInstallInfo.STATUS_INSTALLING) {
+ mProgressLevel = progress;
+ runtimeStatusFlags = progress < 100
+ ? runtimeStatusFlags | FLAG_INSTALL_SESSION_ACTIVE
+ : runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+ } else if (status == PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING) {
+ mProgressLevel = progress;
+ runtimeStatusFlags = runtimeStatusFlags & ~FLAG_INSTALL_SESSION_ACTIVE;
+ runtimeStatusFlags = progress < 100
+ ? runtimeStatusFlags | FLAG_INCREMENTAL_DOWNLOAD_ACTIVE
+ : runtimeStatusFlags & ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+ } else {
+ mProgressLevel = status == PackageInstallInfo.STATUS_INSTALLED ? 100 : 0;
+ runtimeStatusFlags &= ~FLAG_INSTALL_SESSION_ACTIVE;
+ runtimeStatusFlags &= ~FLAG_INCREMENTAL_DOWNLOAD_ACTIVE;
+ }
+ }
+
+ /** Creates an intent to that launches the app store at this app's page. */
+ @Nullable
+ public Intent getMarketIntent(Context context) {
+ ComponentName componentName = getTargetComponent();
+
+ return componentName != null
+ ? new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName())
+ : null;
+ }
+
+ /**
* @return a copy of this
*/
public abstract ItemInfoWithIcon clone();
diff --git a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
index b0d19a6..c04b7f0 100644
--- a/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/model/data/LauncherAppWidgetInfo.java
@@ -26,7 +26,6 @@
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.util.ContentWriter;
/**
@@ -195,13 +194,4 @@
public final boolean hasOptionFlag(int option) {
return (options & option) != 0;
}
-
- @Override
- public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
- builder.setWidget(LauncherAtom.Widget.newBuilder()
- .setSpanX(spanX)
- .setSpanY(spanY)
- .setComponentName(providerName.toString())
- .setPackageName(providerName.getPackageName()));
- }
}
diff --git a/src/com/android/launcher3/model/data/PackageItemInfo.java b/src/com/android/launcher3/model/data/PackageItemInfo.java
index b70d0d4..7617d7e 100644
--- a/src/com/android/launcher3/model/data/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/data/PackageItemInfo.java
@@ -60,6 +60,6 @@
@Override
public int hashCode() {
- return Objects.hash(packageName);
+ return Objects.hash(packageName, user);
}
}
diff --git a/src/com/android/launcher3/model/data/PromiseAppInfo.java b/src/com/android/launcher3/model/data/PromiseAppInfo.java
deleted file mode 100644
index b6231ed..0000000
--- a/src/com/android/launcher3/model/data/PromiseAppInfo.java
+++ /dev/null
@@ -1,54 +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.model.data;
-
-import android.content.Context;
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-
-import com.android.launcher3.pm.PackageInstallInfo;
-import com.android.launcher3.util.PackageManagerHelper;
-
-public class PromiseAppInfo extends AppInfo {
-
- public int level = 0;
-
- public PromiseAppInfo(@NonNull PackageInstallInfo installInfo) {
- componentName = installInfo.componentName;
- intent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_LAUNCHER)
- .setComponent(componentName)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
-
- @Override
- public WorkspaceItemInfo makeWorkspaceItem() {
- WorkspaceItemInfo shortcut = new WorkspaceItemInfo(this);
- shortcut.setInstallProgress(level);
- // We need to update the component name when the apk is installed
- shortcut.status |= WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON;
- // Since the user is manually placing it on homescreen, it should not be auto-removed later
- shortcut.status |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
- return shortcut;
- }
-
- public Intent getMarketIntent(Context context) {
- return new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName());
- }
-}
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
new file mode 100644
index 0000000..b3057d5
--- /dev/null
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -0,0 +1,137 @@
+/*
+ * 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.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Process;
+import android.os.UserHandle;
+
+import androidx.annotation.Nullable;
+
+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 {
+
+ 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;
+
+ private final String mFallbackPackageName;
+ private int mFlags = 0;
+ private final Icon mIcon;
+
+ // If true title does not contain any personal info and eligible for logging.
+ private final boolean mIsPersonalTitle;
+ private Intent mIntent;
+
+ private PendingIntent mPendingIntent;
+
+ public SearchActionItemInfo(Icon icon, String packageName, UserHandle user,
+ CharSequence title, boolean isPersonalTitle) {
+ mIsPersonalTitle = isPersonalTitle;
+ this.user = user == null ? Process.myUserHandle() : user;
+ this.title = title;
+ this.container = EXTENDED_CONTAINERS;
+ mFallbackPackageName = packageName;
+ mIcon = icon;
+ }
+
+ public SearchActionItemInfo(SearchActionItemInfo info) {
+ super(info);
+ mIcon = info.mIcon;
+ mFallbackPackageName = info.mFallbackPackageName;
+ mFlags = info.mFlags;
+ title = info.title;
+ this.container = EXTENDED_CONTAINERS;
+ this.mIsPersonalTitle = info.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();
+ }
+}
diff --git a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
index a7bf1f3..690e904 100644
--- a/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
+++ b/src/com/android/launcher3/model/data/WorkspaceItemInfo.java
@@ -58,20 +58,14 @@
public static final int FLAG_AUTOINSTALL_ICON = 1 << 1;
/**
- * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
- * is set, then the icon is either being installed or is in a broken state.
- */
- public static final int FLAG_INSTALL_SESSION_ACTIVE = 1 << 2;
-
- /**
* Indicates that the widget restore has started.
*/
- public static final int FLAG_RESTORE_STARTED = 1 << 3;
+ public static final int FLAG_RESTORE_STARTED = 1 << 2;
/**
* Web UI supported.
*/
- public static final int FLAG_SUPPORTS_WEB_UI = 1 << 4;
+ public static final int FLAG_SUPPORTS_WEB_UI = 1 << 3;
/**
* The intent used to start the application.
@@ -98,11 +92,6 @@
*/
@NonNull private String[] personKeys = Utilities.EMPTY_STRING_ARRAY;
- /**
- * The installation progress [0-100] of the package that this shortcut represents.
- */
- private int mInstallProgress;
-
public WorkspaceItemInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -114,7 +103,6 @@
intent = new Intent(info.intent);
iconResource = info.iconResource;
status = info.status;
- mInstallProgress = info.mInstallProgress;
personKeys = info.personKeys.clone();
}
@@ -168,15 +156,6 @@
return isPromise() && !hasStatusFlag(FLAG_SUPPORTS_WEB_UI);
}
- public int getInstallProgress() {
- return mInstallProgress;
- }
-
- public void setInstallProgress(int progress) {
- mInstallProgress = progress;
- status |= FLAG_INSTALL_SESSION_ACTIVE;
- }
-
public void updateFromDeepShortcutInfo(ShortcutInfo shortcutInfo, Context context) {
// {@link ShortcutInfo#getActivity} can change during an update. Recreate the intent
intent = ShortcutKey.makeIntent(shortcutInfo);
@@ -216,7 +195,7 @@
if (cn == null && (itemType == Favorites.ITEM_TYPE_SHORTCUT || hasStatusFlag(
FLAG_SUPPORTS_WEB_UI | FLAG_AUTOINSTALL_ICON | FLAG_RESTORED_ICON))) {
// Legacy shortcuts and promise icons with web UI may not have a componentName but just
- // a packageName. In that case create a dummy componentName instead of adding additional
+ // a packageName. In that case create a empty componentName instead of adding additional
// check everywhere.
String pkg = intent.getPackage();
return pkg == null ? null : new ComponentName(pkg, IconCache.EMPTY_CLASS_NAME);
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 835f72d..80eeb22 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -106,7 +106,6 @@
view, 0, 0, view.getWidth(), view.getHeight()).toBundle();
try {
intent.send(null, 0, null, null, null, null, activityOptions);
- launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
launcher.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_NOTIFICATION_LAUNCH_TAP);
} catch (PendingIntent.CanceledException e) {
diff --git a/src/com/android/launcher3/notification/NotificationKeyData.java b/src/com/android/launcher3/notification/NotificationKeyData.java
index a1917ec..1dda3df 100644
--- a/src/com/android/launcher3/notification/NotificationKeyData.java
+++ b/src/com/android/launcher3/notification/NotificationKeyData.java
@@ -30,9 +30,9 @@
/**
* The key data associated with the notification, used to determine what to include
- * in dots and dummy popup views before they are populated.
+ * in dots and stub popup views before they are populated.
*
- * @see NotificationInfo for the full data used when populating the dummy views.
+ * @see NotificationInfo for the full data used when populating the stub views.
*/
public class NotificationKeyData {
public final String notificationKey;
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 059ad18..2905dc3 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,9 +16,9 @@
package com.android.launcher3.notification;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.annotation.TargetApi;
import android.app.Notification;
@@ -37,8 +37,8 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.SecureSettingsObserver;
import java.util.ArrayList;
import java.util.Arrays;
@@ -81,7 +81,8 @@
/** The last notification key that was dismissed from launcher UI */
private String mLastKeyDismissedByLauncher;
- private SecureSettingsObserver mNotificationDotsObserver;
+ private SettingsCache mSettingsCache;
+ private SettingsCache.OnChangeListener mNotificationSettingsChangedListener;
public NotificationListener() {
mWorkerHandler = new Handler(MODEL_EXECUTOR.getLooper(), this::handleWorkerMessage);
@@ -207,10 +208,12 @@
super.onListenerConnected();
sIsConnected = true;
- mNotificationDotsObserver =
- newNotificationSettingsObserver(this, this::onNotificationSettingsChanged);
- mNotificationDotsObserver.register();
- mNotificationDotsObserver.dispatchOnChange();
+ // Register an observer to rebind the notification listener when dots are re-enabled.
+ mSettingsCache = SettingsCache.INSTANCE.get(this);
+ mNotificationSettingsChangedListener = this::onNotificationSettingsChanged;
+ mSettingsCache.register(NOTIFICATION_BADGING_URI,
+ mNotificationSettingsChangedListener);
+ mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI);
onNotificationFullRefresh();
}
@@ -229,7 +232,7 @@
public void onListenerDisconnected() {
super.onListenerDisconnected();
sIsConnected = false;
- mNotificationDotsObserver.unregister();
+ mSettingsCache.unregister(NOTIFICATION_BADGING_URI, mNotificationSettingsChangedListener);
onNotificationFullRefresh();
}
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index b03aa9c..9b06523 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -17,6 +17,7 @@
package com.android.launcher3.notification;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NOTIFICATION_DISMISSED;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -41,7 +42,6 @@
import com.android.launcher3.touch.BaseSwipeDetector;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Themes;
/**
@@ -137,7 +137,7 @@
setOnClickListener(mNotificationInfo);
}
setContentTranslation(0);
- // Add a dummy ItemInfo so that logging populates the correct container and item types
+ // Add a stub ItemInfo so that logging populates the correct container and item types
// instead of DEFAULT_CONTAINERTYPE and DEFAULT_ITEMTYPE, respectively.
setTag(NOTIFICATION_ITEM_INFO);
if (animate) {
@@ -168,10 +168,7 @@
Launcher launcher = Launcher.getLauncher(getContext());
launcher.getPopupDataProvider().cancelNotification(
mNotificationInfo.notificationKey);
- launcher.getUserEventDispatcher().logActionOnItem(
- LauncherLogProto.Action.Touch.SWIPE,
- LauncherLogProto.Action.Direction.RIGHT, // Assume all swipes are right for logging.
- LauncherLogProto.ItemType.NOTIFICATION);
+ launcher.getStatsLogManager().logger().log(LAUNCHER_NOTIFICATION_DISMISSED);
}
// SingleAxisSwipeDetector.Listener's
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 408796f..6189dc9 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -268,7 +268,9 @@
} else {
lp.leftMargin = lp.rightMargin = 0;
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
+ lp.bottomMargin = grid.isTaskbarPresent
+ ? grid.workspacePadding.bottom + insets.bottom
+ : 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 901d27f..0091af1 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -17,6 +17,7 @@
package com.android.launcher3.pm;
import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -31,17 +32,19 @@
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
+import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
-import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Preconditions;
import java.util.ArrayList;
import java.util.HashMap;
@@ -64,27 +67,27 @@
private final LauncherApps mLauncherApps;
private final Context mAppContext;
- private final IntSet mPromiseIconIds;
private final PackageInstaller mInstaller;
private final HashMap<String, Boolean> mSessionVerifiedMap = new HashMap<>();
+ private IntSet mPromiseIconIds;
+
public InstallSessionHelper(Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
mAppContext = context.getApplicationContext();
mLauncherApps = context.getSystemService(LauncherApps.class);
+ }
+ @WorkerThread
+ private IntSet getPromiseIconIds() {
+ Preconditions.assertWorkerThread();
+ if (mPromiseIconIds != null) {
+ return mPromiseIconIds;
+ }
mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
- getPrefs(context).getString(PROMISE_ICON_IDS, "")));
+ getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
- cleanUpPromiseIconIds();
- }
-
- public static UserHandle getUserHandle(SessionInfo info) {
- return Utilities.ATLEAST_Q ? info.getUser() : Process.myUserHandle();
- }
-
- protected void cleanUpPromiseIconIds() {
IntArray existingIds = new IntArray();
for (SessionInfo info : getActiveSessions().values()) {
existingIds.add(info.getSessionId());
@@ -99,6 +102,7 @@
for (int i = idsToRemove.size() - 1; i >= 0; --i) {
mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
}
+ return mPromiseIconIds;
}
public HashMap<PackageUserKey, SessionInfo> getActiveSessions() {
@@ -125,7 +129,7 @@
private void updatePromiseIconPrefs() {
getPrefs(mAppContext).edit()
- .putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
+ .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
.apply();
}
@@ -167,7 +171,7 @@
* Attempt to restore workspace layout if the session is triggered due to device restore.
*/
public boolean restoreDbIfApplicable(@NonNull final SessionInfo info) {
- if (!Utilities.ATLEAST_OREO || !FeatureFlags.ENABLE_DATABASE_RESTORE.get()) {
+ if (!FeatureFlags.ENABLE_DATABASE_RESTORE.get()) {
return false;
}
if (isRestore(info)) {
@@ -183,13 +187,15 @@
return info.getInstallReason() == PackageManager.INSTALL_REASON_DEVICE_RESTORE;
}
+ @WorkerThread
public boolean promiseIconAddedForId(int sessionId) {
- return mPromiseIconIds.contains(sessionId);
+ return getPromiseIconIds().contains(sessionId);
}
+ @WorkerThread
public void removePromiseIconId(int sessionId) {
- if (mPromiseIconIds.contains(sessionId)) {
- mPromiseIconIds.getArray().removeValue(sessionId);
+ if (promiseIconAddedForId(sessionId)) {
+ getPromiseIconIds().getArray().removeValue(sessionId);
updatePromiseIconPrefs();
}
}
@@ -202,30 +208,32 @@
* - The app is not already installed
* - A promise icon for the session has not already been created
*/
+ @WorkerThread
void tryQueuePromiseAppIcon(PackageInstaller.SessionInfo sessionInfo) {
- if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
+ if (FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
&& SessionCommitReceiver.isEnabled(mAppContext)
&& verify(sessionInfo) != null
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
&& sessionInfo.getAppIcon() != null
&& !TextUtils.isEmpty(sessionInfo.getAppLabel())
- && !mPromiseIconIds.contains(sessionInfo.getSessionId())
- && new PackageManagerHelper(mAppContext).getApplicationInfo(
- sessionInfo.getAppPackageName(), getUserHandle(sessionInfo), 0) == null) {
- SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
- mPromiseIconIds.add(sessionInfo.getSessionId());
+ && !promiseIconAddedForId(sessionInfo.getSessionId())
+ && !new PackageManagerHelper(mAppContext).isAppInstalled(
+ sessionInfo.getAppPackageName(), getUserHandle(sessionInfo))) {
+ ItemInstallQueue.INSTANCE.get(mAppContext)
+ .queueItem(sessionInfo.getAppPackageName(), getUserHandle(sessionInfo));
+
+ getPromiseIconIds().add(sessionInfo.getSessionId());
updatePromiseIconPrefs();
}
}
- public InstallSessionTracker registerInstallTracker(
- InstallSessionTracker.Callback callback, LooperExecutor executor) {
+ public InstallSessionTracker registerInstallTracker(InstallSessionTracker.Callback callback) {
InstallSessionTracker tracker = new InstallSessionTracker(this, callback);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
- mInstaller.registerSessionCallback(tracker, executor.getHandler());
+ mInstaller.registerSessionCallback(tracker, MODEL_EXECUTOR.getHandler());
} else {
- mLauncherApps.registerPackageInstallerSessionCallback(executor, tracker);
+ mLauncherApps.registerPackageInstallerSessionCallback(MODEL_EXECUTOR, tracker);
}
return tracker;
}
@@ -237,4 +245,8 @@
mLauncherApps.unregisterPackageInstallerSessionCallback(tracker);
}
}
+
+ public static UserHandle getUserHandle(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 eb3ca73..b0b907a 100644
--- a/src/com/android/launcher3/pm/InstallSessionTracker.java
+++ b/src/com/android/launcher3/pm/InstallSessionTracker.java
@@ -24,8 +24,11 @@
import android.os.UserHandle;
import android.util.SparseArray;
+import androidx.annotation.WorkerThread;
+
import com.android.launcher3.util.PackageUserKey;
+@WorkerThread
public class InstallSessionTracker extends PackageInstaller.SessionCallback {
// Lazily initialized
diff --git a/src/com/android/launcher3/pm/PackageInstallInfo.java b/src/com/android/launcher3/pm/PackageInstallInfo.java
index 7997d16..fad904f 100644
--- a/src/com/android/launcher3/pm/PackageInstallInfo.java
+++ b/src/com/android/launcher3/pm/PackageInstallInfo.java
@@ -25,7 +25,8 @@
public static final int STATUS_INSTALLED = 0;
public static final int STATUS_INSTALLING = 1;
- public static final int STATUS_FAILED = 2;
+ public static final int STATUS_INSTALLED_DOWNLOADING = 2;
+ public static final int STATUS_FAILED = 3;
public final ComponentName componentName;
public final String packageName;
@@ -56,5 +57,4 @@
public static PackageInstallInfo fromState(int state, String packageName, UserHandle user) {
return new PackageInstallInfo(packageName, state, 0 /* progress */, user);
}
-
}
diff --git a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
index 40d7031..7af14c6 100644
--- a/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/pm/ShortcutConfigActivityInfo.java
@@ -27,7 +27,6 @@
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Process;
@@ -39,13 +38,13 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -175,37 +174,23 @@
List<ShortcutConfigActivityInfo> result = new ArrayList<>();
UserHandle myUser = Process.myUserHandle();
- if (Utilities.ATLEAST_OREO) {
- final List<UserHandle> users;
- final String packageName;
- if (packageUser == null) {
- users = UserCache.INSTANCE.get(context).getUserProfiles();
- packageName = null;
- } else {
- users = new ArrayList<>(1);
- users.add(packageUser.mUser);
- packageName = packageUser.mPackageName;
- }
- LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- for (UserHandle user : users) {
- boolean ignoreTargetSdk = myUser.equals(user);
- for (LauncherActivityInfo activityInfo :
- launcherApps.getShortcutConfigActivityList(packageName, user)) {
- if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
- >= Build.VERSION_CODES.O) {
- result.add(new ShortcutConfigActivityInfoVO(activityInfo));
- }
- }
- }
+ final List<UserHandle> users;
+ final String packageName;
+ if (packageUser == null) {
+ users = UserCache.INSTANCE.get(context).getUserProfiles();
+ packageName = null;
} else {
- if (packageUser == null || packageUser.mUser.equals(myUser)) {
- Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
- if (packageUser != null) {
- intent.setPackage(packageUser.mPackageName);
- }
- for (ResolveInfo info :
- context.getPackageManager().queryIntentActivities(intent, 0)) {
- result.add(new ShortcutConfigActivityInfoVL(info.activityInfo));
+ users = Collections.singletonList(packageUser.mUser);
+ packageName = packageUser.mPackageName;
+ }
+ LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
+ for (UserHandle user : users) {
+ boolean ignoreTargetSdk = myUser.equals(user);
+ for (LauncherActivityInfo activityInfo :
+ launcherApps.getShortcutConfigActivityList(packageName, user)) {
+ if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion
+ >= Build.VERSION_CODES.O) {
+ result.add(new ShortcutConfigActivityInfoVO(activityInfo));
}
}
}
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 2d7d6b0..5ade22b 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -60,6 +60,9 @@
private void onUsersChanged(Intent intent) {
enableAndResetCache();
mUserChangeListeners.forEach(Runnable::run);
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
+ }
}
/**
@@ -104,9 +107,6 @@
mUsers = null;
mUserToSerialMap = null;
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work profile removed", new Exception());
- }
}
}
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index d5b32fc..5a34d2a 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -26,11 +26,8 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.CornerPathEffect;
import android.graphics.Outline;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.drawable.ShapeDrawable;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.Gravity;
@@ -40,6 +37,8 @@
import android.view.ViewOutlineProvider;
import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.InsettableFrameLayout;
@@ -49,7 +48,6 @@
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
@@ -70,7 +68,11 @@
protected final T mLauncher;
protected final boolean mIsRtl;
- private final int mArrowOffset;
+ private final int mArrowOffsetVertical;
+ private final int mArrowOffsetHorizontal;
+ private final int mArrowWidth;
+ private final int mArrowHeight;
+ private final int mArrowPointRadius;
private final View mArrow;
protected boolean mIsLeftAligned;
@@ -82,6 +84,8 @@
private final Rect mStartRect = new Rect();
private final Rect mEndRect = new Rect();
+ private Runnable mOnCloseCallback = () -> { };
+
public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
@@ -99,11 +103,14 @@
// Initialize arrow view
final Resources resources = getResources();
- final int arrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
- final int arrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
+ mArrowWidth = resources.getDimensionPixelSize(R.dimen.popup_arrow_width);
+ mArrowHeight = resources.getDimensionPixelSize(R.dimen.popup_arrow_height);
mArrow = new View(context);
- mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
- mArrowOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+ mArrow.setLayoutParams(new DragLayer.LayoutParams(mArrowWidth, mArrowHeight));
+ mArrowOffsetVertical = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+ mArrowOffsetHorizontal = resources.getDimensionPixelSize(
+ R.dimen.popup_arrow_horizontal_center_offset) - (mArrowWidth / 2);
+ mArrowPointRadius = resources.getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
}
public ArrowPopup(Context context, AttributeSet attrs) {
@@ -151,75 +158,78 @@
* @param viewsToFlip number of views from the top to to flip in case of reverse order
*/
protected void reorderAndShow(int viewsToFlip) {
+ setupForDisplay();
+ boolean reverseOrder = mIsAboveIcon;
+ if (reverseOrder) {
+ reverseOrder(viewsToFlip);
+ }
+ onInflationComplete(reverseOrder);
+ addArrow();
+ animateOpen();
+ }
+
+ /**
+ * Shows the popup at the desired location.
+ */
+ protected void show() {
+ setupForDisplay();
+ onInflationComplete(false);
+ addArrow();
+ animateOpen();
+ }
+
+ private void setupForDisplay() {
setVisibility(View.INVISIBLE);
mIsOpen = true;
getPopupContainer().addView(this);
orientAboutObject();
+ }
- boolean reverseOrder = mIsAboveIcon;
- if (reverseOrder) {
- int count = getChildCount();
- ArrayList<View> allViews = new ArrayList<>(count);
- for (int i = 0; i < count; i++) {
- if (i == viewsToFlip) {
- Collections.reverse(allViews);
- }
- allViews.add(getChildAt(i));
+ private void reverseOrder(int viewsToFlip) {
+ int count = getChildCount();
+ ArrayList<View> allViews = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ if (i == viewsToFlip) {
+ Collections.reverse(allViews);
}
- Collections.reverse(allViews);
- removeAllViews();
- for (int i = 0; i < count; i++) {
- addView(allViews.get(i));
- }
-
- orientAboutObject();
+ allViews.add(getChildAt(i));
}
- onInflationComplete(reverseOrder);
+ Collections.reverse(allViews);
+ removeAllViews();
+ for (int i = 0; i < count; i++) {
+ addView(allViews.get(i));
+ }
- // Add the arrow.
- final Resources res = getResources();
- final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
- ? R.dimen.popup_arrow_horizontal_center_start
- : R.dimen.popup_arrow_horizontal_center_end);
- final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
- getPopupContainer().addView(mArrow);
- DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
+ orientAboutObject();
+ }
+
+ private int getArrowLeft() {
if (mIsLeftAligned) {
- mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
- } else {
- mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
+ return mArrowOffsetHorizontal;
}
+ return getMeasuredWidth() - mArrowOffsetHorizontal - mArrowWidth;
+ }
+
+ private void addArrow() {
+ getPopupContainer().addView(mArrow);
+ mArrow.setX(getX() + getArrowLeft());
if (Gravity.isVertical(mGravity)) {
// This is only true if there wasn't room for the container next to the icon,
// so we centered it instead. In that case we don't want to showDefaultOptions the arrow.
mArrow.setVisibility(INVISIBLE);
} else {
- ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
- arrowLp.width, arrowLp.height, !mIsAboveIcon));
- Paint arrowPaint = arrowDrawable.getPaint();
- arrowPaint.setColor(Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
- // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
- int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
- arrowPaint.setPathEffect(new CornerPathEffect(radius));
- mArrow.setBackground(arrowDrawable);
- // Clip off the part of the arrow that is underneath the popup.
- if (mIsAboveIcon) {
- mArrow.setClipBounds(new Rect(0, -mArrowOffset, arrowLp.width, arrowLp.height));
- } else {
- mArrow.setClipBounds(new Rect(0, 0, arrowLp.width, arrowLp.height + mArrowOffset));
- }
+ mArrow.setBackground(new RoundedArrowDrawable(
+ mArrowWidth, mArrowHeight, mArrowPointRadius,
+ mOutlineRadius, getMeasuredWidth(), getMeasuredHeight(),
+ mArrowOffsetHorizontal, -mArrowOffsetVertical,
+ !mIsAboveIcon, mIsLeftAligned,
+ Themes.getAttrColor(getContext(), R.attr.popupColorPrimary)));
mArrow.setElevation(getElevation());
}
- mArrow.setPivotX(arrowLp.width / 2);
- mArrow.setPivotY(mIsAboveIcon ? arrowLp.height : 0);
-
- animateOpen();
- }
-
- protected boolean isAlignedWithStart() {
- return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+ mArrow.setPivotX(mArrowWidth / 2.0f);
+ mArrow.setPivotY(mIsAboveIcon ? mArrowHeight : 0);
}
/**
@@ -252,8 +262,9 @@
*/
private void orientAboutObject(boolean allowAlignLeft, boolean allowAlignRight) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+
int width = getMeasuredWidth();
- int extraVerticalSpace = mArrow.getLayoutParams().height + mArrowOffset
+ int extraVerticalSpace = mArrowHeight + mArrowOffsetVertical
+ getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
int height = getMeasuredHeight() + extraVerticalSpace;
@@ -269,22 +280,7 @@
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
int iconWidth = mTempRect.width();
- Resources resources = getResources();
- int xOffset;
- if (isAlignedWithStart()) {
- // Aligning with the shortcut icon.
- int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
- int shortcutPaddingStart = resources.getDimensionPixelSize(
- R.dimen.popup_padding_start);
- xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
- } else {
- // Aligning with the drag handle.
- int shortcutDragHandleWidth = resources.getDimensionPixelSize(
- R.dimen.deep_shortcut_drag_handle_size);
- int shortcutPaddingEnd = resources.getDimensionPixelSize(
- R.dimen.popup_padding_end);
- xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
- }
+ int xOffset = iconWidth / 2 - mArrowOffsetHorizontal - mArrowWidth / 2;
x += mIsLeftAligned ? xOffset : -xOffset;
// Check whether we can still align as we originally wanted, now that we've calculated x.
@@ -353,12 +349,14 @@
FrameLayout.LayoutParams arrowLp = (FrameLayout.LayoutParams) mArrow.getLayoutParams();
if (mIsAboveIcon) {
arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
- lp.bottomMargin = getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
- arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrowOffset - insets.bottom;
+ lp.bottomMargin =
+ getPopupContainer().getHeight() - y - getMeasuredHeight() - insets.top;
+ arrowLp.bottomMargin =
+ lp.bottomMargin - arrowLp.height - mArrowOffsetVertical - insets.bottom;
} else {
arrowLp.gravity = lp.gravity = Gravity.TOP;
lp.topMargin = y + insets.top;
- arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffset;
+ arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrowOffsetVertical;
}
}
@@ -507,22 +505,13 @@
protected void onCreateCloseAnimation(AnimatorSet anim) { }
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
- Resources res = getResources();
- int arrowCenterX = res.getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
- R.dimen.popup_arrow_horizontal_center_start:
- R.dimen.popup_arrow_horizontal_center_end);
- int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
- float arrowCornerRadius = res.getDimension(R.dimen.popup_arrow_corner_radius);
- if (!mIsLeftAligned) {
- arrowCenterX = getMeasuredWidth() - arrowCenterX;
- }
+ int arrowLeft = getArrowLeft();
int arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
- mStartRect.set(arrowCenterX - halfArrowWidth, arrowCenterY, arrowCenterX + halfArrowWidth,
- arrowCenterY);
+ mStartRect.set(arrowLeft, arrowCenterY, arrowLeft + mArrowWidth, arrowCenterY);
- return new RoundedRectRevealOutlineProvider
- (arrowCornerRadius, mOutlineRadius, mStartRect, mEndRect);
+ return new RoundedRectRevealOutlineProvider(
+ mArrowPointRadius, mOutlineRadius, mStartRect, mEndRect);
}
/**
@@ -537,6 +526,14 @@
mDeferContainerRemoval = false;
getPopupContainer().removeView(this);
getPopupContainer().removeView(mArrow);
+ mOnCloseCallback.run();
+ }
+
+ /**
+ * Callback to be called when the popup is closed
+ */
+ public void setOnCloseCallback(@NonNull Runnable callback) {
+ mOnCloseCallback = callback;
}
protected BaseDragLayer getPopupContainer() {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 614cf14..a1ba747 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -19,13 +19,8 @@
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.animation.AnimatorSet;
@@ -52,7 +47,6 @@
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.dot.DotInfo;
@@ -148,17 +142,6 @@
return (type & TYPE_ACTION_POPUP) != 0;
}
- @Override
- public void logActionCommand(int command) {
- mLauncher.getUserEventDispatcher().logActionCommand(
- command, mOriginalIcon, getLogContainerType());
- }
-
- @Override
- public int getLogContainerType() {
- return ContainerType.DEEPSHORTCUTS;
- }
-
public OnClickListener getItemClickListener() {
return (view) -> {
mLauncher.getItemOnClickListener().onClick(view);
@@ -175,8 +158,7 @@
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
BaseDragLayer dl = getPopupContainer();
if (!dl.isEventOverView(this, ev)) {
- mLauncher.getUserEventDispatcher().logActionTapOutside(
- newContainerTarget(ContainerType.DEEPSHORTCUTS));
+ // TODO: add WW log if want to log if tap closed deep shortcut container.
close(true);
// We let touches on the original icon go through so that users can launch
@@ -224,6 +206,7 @@
.filter(Objects::nonNull)
.collect(Collectors.toList()));
launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
+ container.requestFocus();
return container;
}
@@ -400,9 +383,7 @@
} else if (view instanceof ImageView) {
// Only the system shortcut icon shows on a gray background header.
info.setIconAndContentDescriptionFor((ImageView) view);
- if (Utilities.ATLEAST_OREO) {
- view.setTooltipText(view.getContentDescription());
- }
+ view.setTooltipText(view.getContentDescription());
}
view.setTag(info);
view.setOnClickListener(info);
@@ -452,7 +433,9 @@
// Make sure we keep the original icon hidden while it is being dragged.
mOriginalIcon.setVisibility(INVISIBLE);
} else {
- mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
+ // TODO: add WW logging if want to add logging for long press on popup
+ // container.
+ // mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
if (!mIsAboveIcon) {
// Show the icon but keep the text hidden.
mOriginalIcon.setVisibility(VISIBLE);
@@ -499,18 +482,6 @@
}
@Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- if (childInfo == NOTIFICATION_ITEM_INFO) {
- child.itemType = ItemType.NOTIFICATION;
- } else {
- child.itemType = ItemType.DEEPSHORTCUT;
- child.rank = childInfo.rank;
- }
- parents.add(newContainerTarget(ContainerType.DEEPSHORTCUTS));
- }
-
- @Override
protected void onCreateCloseAnimation(AnimatorSet anim) {
// Animate original icon's text back in.
anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
@@ -630,6 +601,17 @@
}
/**
+ * Dismisses the popup if it is no longer valid
+ */
+ public static void dismissInvalidPopup(BaseDraggingActivity activity) {
+ PopupContainerWithArrow popup = getOpen(activity);
+ if (popup != null && (!popup.mOriginalIcon.isAttachedToWindow()
+ || !canShow(popup.mOriginalIcon, (ItemInfo) popup.mOriginalIcon.getTag()))) {
+ popup.animateClose();
+ }
+ }
+
+ /**
* Handler to control drag-and-drop for popup items
*/
public interface PopupItemDragHandler extends OnLongClickListener, OnTouchListener { }
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 5a5f668..7780894 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -31,14 +31,13 @@
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.ShortcutUtil;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -59,8 +58,11 @@
private HashMap<ComponentKey, Integer> mDeepShortcutMap = new HashMap<>();
/** Maps packages to their DotInfo's . */
private Map<PackageUserKey, DotInfo> mPackageUserToDotInfos = new HashMap<>();
- /** Maps packages to their Widgets */
- private ArrayList<WidgetListRowEntry> mAllWidgets = new ArrayList<>();
+
+ /** All installed widgets. */
+ private List<WidgetsListBaseEntry> mAllWidgets = List.of();
+ /** Widgets that can be recommended to the users. */
+ private List<ItemInfo> mRecommendedWidgets = List.of();
private PopupDataChangeListener mChangeListener = PopupDataChangeListener.INSTANCE;
@@ -188,7 +190,16 @@
notificationListener.cancelNotificationFromLauncher(notificationKey);
}
- public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
+ /**
+ * Sets a list of recommended widgets ordered by their order of appearance in the widgets
+ * recommendation UI.
+ */
+ public void setRecommendedWidgets(List<ItemInfo> recommendedWidgets) {
+ mRecommendedWidgets = recommendedWidgets;
+ mChangeListener.onRecommendedWidgetsBound();
+ }
+
+ public void setAllWidgets(List<WidgetsListBaseEntry> allWidgets) {
mAllWidgets = allWidgets;
mChangeListener.onWidgetsBound();
}
@@ -197,25 +208,32 @@
mChangeListener = listener == null ? PopupDataChangeListener.INSTANCE : listener;
}
- public ArrayList<WidgetListRowEntry> getAllWidgets() {
+ public List<WidgetsListBaseEntry> getAllWidgets() {
return mAllWidgets;
}
+ /** Returns a list of recommended widgets. */
+ public List<WidgetItem> getRecommendedWidgets() {
+ HashMap<ComponentKey, WidgetItem> allWidgetItems = new HashMap<>();
+ mAllWidgets.stream()
+ .filter(entry -> entry instanceof WidgetsListContentEntry)
+ .forEach(entry -> ((WidgetsListContentEntry) entry).mWidgets
+ .forEach(widget -> allWidgetItems.put(
+ new ComponentKey(widget.componentName, widget.user), widget)));
+ return mRecommendedWidgets.stream()
+ .map(recommendedWidget -> allWidgetItems.get(
+ new ComponentKey(recommendedWidget.getTargetComponent(),
+ recommendedWidget.user)))
+ .collect(Collectors.toList());
+ }
+
public List<WidgetItem> getWidgetsForPackageUser(PackageUserKey packageUserKey) {
- for (WidgetListRowEntry entry : mAllWidgets) {
- if (entry.pkgItem.packageName.equals(packageUserKey.mPackageName)) {
- ArrayList<WidgetItem> widgets = new ArrayList<>(entry.widgets);
- // Remove widgets not associated with the correct user.
- Iterator<WidgetItem> iterator = widgets.iterator();
- while (iterator.hasNext()) {
- if (!iterator.next().user.equals(packageUserKey.mUser)) {
- iterator.remove();
- }
- }
- return widgets.isEmpty() ? null : widgets;
- }
- }
- return null;
+ return mAllWidgets.stream()
+ .filter(row -> row instanceof WidgetsListContentEntry
+ && row.mPkgItem.packageName.equals(packageUserKey.mPackageName))
+ .flatMap(row -> ((WidgetsListContentEntry) row).mWidgets.stream())
+ .filter(widget -> packageUserKey.mUser.equals(widget.user))
+ .collect(Collectors.toList());
}
/**
@@ -253,5 +271,8 @@
default void trimNotifications(Map<PackageUserKey, DotInfo> updatedDots) { }
default void onWidgetsBound() { }
+
+ /** A callback to get notified when recommended widgets are bound. */
+ default void onRecommendedWidgetsBound() { }
}
}
diff --git a/src/com/android/launcher3/popup/RemoteActionShortcut.java b/src/com/android/launcher3/popup/RemoteActionShortcut.java
index 61829c0..7c393ad 100644
--- a/src/com/android/launcher3/popup/RemoteActionShortcut.java
+++ b/src/com/android/launcher3/popup/RemoteActionShortcut.java
@@ -37,7 +37,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
@TargetApi(Build.VERSION_CODES.Q)
public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
@@ -107,9 +106,6 @@
Toast.LENGTH_SHORT)
.show();
}
-
- mTarget.getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.Touch.TAP,
- LauncherLogProto.ControlType.REMOTE_ACTION_SHORTCUT, view);
}
@Override
diff --git a/src/com/android/launcher3/popup/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
new file mode 100644
index 0000000..e662d5c
--- /dev/null
+++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
@@ -0,0 +1,161 @@
+/*
+ * 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.popup;
+
+import static java.lang.Math.atan;
+import static java.lang.Math.cos;
+import static java.lang.Math.sin;
+import static java.lang.Math.toDegrees;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+
+/**
+ * A drawable for a very specific purpose. Used for the caret arrow on a rounded rectangle popup
+ * bubble.
+ * Draws a triangle with one rounded tip, the opposite edge is clipped by the body of the popup
+ * so there is no overlap when drawing them together.
+ */
+public class RoundedArrowDrawable extends Drawable {
+
+ private final Path mPath;
+ private final Paint mPaint;
+
+ /**
+ * Default constructor.
+ *
+ * @param width of the arrow.
+ * @param height of the arrow.
+ * @param radius of the tip of the arrow.
+ * @param popupRadius of the rect to clip this by.
+ * @param popupWidth of the rect to clip this by.
+ * @param popupHeight of the rect to clip this by.
+ * @param arrowOffsetX from the edge of the popup to the arrow.
+ * @param arrowOffsetY how much the arrow will overlap the popup.
+ * @param isPointingUp or not.
+ * @param leftAligned or false for right aligned.
+ * @param color to draw the triangle.
+ */
+ public RoundedArrowDrawable(float width, float height, float radius, float popupRadius,
+ float popupWidth, float popupHeight,
+ float arrowOffsetX, float arrowOffsetY, boolean isPointingUp, boolean leftAligned,
+ int color) {
+ mPath = new Path();
+ mPaint = new Paint();
+ mPaint.setColor(color);
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setAntiAlias(true);
+
+ // Make the drawable with the triangle pointing down and positioned on the left..
+ addDownPointingRoundedTriangleToPath(width, height, radius, mPath);
+ clipPopupBodyFromPath(popupRadius, popupWidth, popupHeight, arrowOffsetX, arrowOffsetY,
+ mPath);
+
+ // ... then flip it horizontal or vertical based on where it will be used.
+ Matrix pathTransform = new Matrix();
+ pathTransform.setScale(
+ leftAligned ? 1 : -1, isPointingUp ? -1 : 1, width * 0.5f, height * 0.5f);
+ mPath.transform(pathTransform);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public void getOutline(Outline outline) {
+ outline.setPath(mPath);
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ mPaint.setAlpha(i);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ }
+
+ private static void addDownPointingRoundedTriangleToPath(float width, float height,
+ float radius, Path path) {
+ // Calculated for the arrow pointing down, will be flipped later if needed.
+
+ // Theta is half of the angle inside the triangle tip
+ float tanTheta = width / (2.0f * height);
+ float theta = (float) atan(tanTheta);
+
+ // Some trigonometry to find the center of the circle for the rounded tip
+ float roundedPointCenterY = (float) (height - (radius / sin(theta)));
+
+ // p is the distance along the triangle side to the intersection with the point circle
+ float p = radius / tanTheta;
+ float lineRoundPointIntersectFromCenter = (float) (p * sin(theta));
+ float lineRoundPointIntersectFromTop = (float) (height - (p * cos(theta)));
+
+ float centerX = width / 2.0f;
+ float thetaDeg = (float) toDegrees(theta);
+
+ path.reset();
+ path.moveTo(0, 0);
+ // Draw the top
+ path.lineTo(width, 0);
+ // Draw the right side up to the circle intersection
+ path.lineTo(
+ centerX + lineRoundPointIntersectFromCenter,
+ lineRoundPointIntersectFromTop);
+ // Draw the rounded point
+ path.arcTo(
+ centerX - radius,
+ roundedPointCenterY - radius,
+ centerX + radius,
+ roundedPointCenterY + radius,
+ thetaDeg,
+ 180 - (2 * thetaDeg),
+ false);
+ // Draw the left edge to close
+ path.lineTo(0, 0);
+ path.close();
+ }
+
+ private static void clipPopupBodyFromPath(float popupRadius, float popupWidth,
+ float popupHeight, float arrowOffsetX, float arrowOffsetY, Path path) {
+ // Make a path that is used to clip the triangle, this represents the body of the popup
+ Path clipPiece = new Path();
+ clipPiece.addRoundRect(
+ 0, 0, popupWidth, popupHeight,
+ popupRadius, popupRadius, Path.Direction.CW);
+ // clipping is performed as if the arrow is pointing down and positioned on the left, the
+ // resulting path will be flipped as needed later.
+ // The extra 0.5 in the vertical offset is to close the gap between this anti-aliased object
+ // and the anti-aliased body of the popup.
+ clipPiece.offset(-arrowOffsetX, -popupHeight + arrowOffsetY - 0.5f);
+ path.op(clipPiece, Path.Op.DIFFERENCE);
+ }
+}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index fd292a3..577fe4a 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -21,8 +21,6 @@
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
@@ -99,7 +97,7 @@
final List<WidgetItem> widgets =
launcher.getPopupDataProvider().getWidgetsForPackageUser(new PackageUserKey(
itemInfo.getTargetComponent().getPackageName(), itemInfo.user));
- if (widgets == null) {
+ if (widgets.isEmpty()) {
return null;
}
return new Widgets(launcher, itemInfo);
@@ -117,8 +115,6 @@
(WidgetsBottomSheet) mTarget.getLayoutInflater().inflate(
R.layout.widgets_bottom_sheet, mTarget.getDragLayer(), false);
widgetsBottomSheet.populateAndShow(mItemInfo);
- mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.WIDGETS_BUTTON, view);
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP);
}
@@ -139,8 +135,6 @@
Rect sourceBounds = mTarget.getViewBounds(view);
new PackageManagerHelper(mTarget).startDetailsActivityForInfo(
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
- mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.APPINFO_TARGET, view);
mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo)
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP);
}
@@ -174,7 +168,7 @@
public void onClick(View view) {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
mItemInfo.getTargetComponent().getPackageName());
- mTarget.startActivitySafely(view, intent, mItemInfo, null);
+ mTarget.startActivitySafely(view, intent, mItemInfo);
AbstractFloatingView.closeAllOpenViews(mTarget);
}
}
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 289e0d8..a191df4 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -20,6 +20,8 @@
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER;
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
import android.app.Activity;
import android.app.Fragment;
import android.app.SearchManager;
@@ -34,6 +36,7 @@
import android.os.Bundle;
import android.provider.Settings;
import android.util.AttributeSet;
+import android.util.SizeF;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -50,6 +53,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.FragmentWithPreview;
+import java.util.ArrayList;
+
/**
* A frame layout which contains a QSB. This internally uses fragment to bind the view, which
* allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
@@ -294,12 +299,16 @@
InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
Bundle opts = new Bundle();
- Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(getContext(),
- idp.numColumns, 1, null);
+ ArrayList<SizeF> sizes = AppWidgetResizeFrame
+ .getWidgetSizes(getContext(), idp.numColumns, 1);
+ Rect size = AppWidgetResizeFrame.getMinMaxSizes(sizes, null /* outRect */);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+ if (ATLEAST_S) {
+ opts.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes);
+ }
return opts;
}
diff --git a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
new file mode 100644
index 0000000..4653774
--- /dev/null
+++ b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
@@ -0,0 +1,44 @@
+/*
+ * 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.recyclerview;
+
+import android.view.ViewGroup;
+
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * Creates and populates views with data
+ *
+ * @param <T> A data model which is used to populate the {@link ViewHolder}.
+ * @param <V> A subclass of {@link ViewHolder} which holds references to views.
+ */
+public interface ViewHolderBinder<T, V extends ViewHolder> {
+ /**
+ * Creates a new view, and attach it to the parent {@link ViewGroup}. Then, populates UI
+ * references in a {@link ViewHolder}.
+ */
+ V newViewHolder(ViewGroup parent);
+
+ /** Populate UI references in {@link ViewHolder} with data. */
+ void bindViewHolder(V viewHolder, T data);
+
+ /**
+ * Called when the view is recycled. Views are recycled in batches once they are sufficiently
+ * far off screen that it is unlikely the user will scroll back to them soon. Optionally
+ * override this to free expensive resources.
+ */
+ default void unbindViewHolder(V viewHolder) {}
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchAlgorithm.java b/src/com/android/launcher3/search/SearchAlgorithm.java
similarity index 76%
rename from src/com/android/launcher3/allapps/search/SearchAlgorithm.java
rename to src/com/android/launcher3/search/SearchAlgorithm.java
index c409b1c..1665354 100644
--- a/src/com/android/launcher3/allapps/search/SearchAlgorithm.java
+++ b/src/com/android/launcher3/search/SearchAlgorithm.java
@@ -13,17 +13,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps.search;
+package com.android.launcher3.search;
/**
* An interface for handling search.
+ *
+ * @param <T> Search Result type
*/
-public interface SearchAlgorithm {
+public interface SearchAlgorithm<T> {
/**
- * Performs search and sends the result to the callback.
+ * Performs search and sends the result to {@link SearchCallback}.
*/
- void doSearch(String query, AllAppsSearchBarController.Callbacks callback);
+ void doSearch(String query, SearchCallback<T> callback);
/**
* Cancels any active request.
diff --git a/src/com/android/launcher3/search/SearchCallback.java b/src/com/android/launcher3/search/SearchCallback.java
new file mode 100644
index 0000000..5796116
--- /dev/null
+++ b/src/com/android/launcher3/search/SearchCallback.java
@@ -0,0 +1,46 @@
+/*
+ * 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.search;
+
+import java.util.ArrayList;
+
+/**
+ * An interface for receiving search results.
+ *
+ * @param <T> Search Result type
+ */
+public interface SearchCallback<T> {
+
+ /**
+ * Called when the search from primary source is complete.
+ *
+ * @param items list of search results
+ */
+ void onSearchResult(String query, ArrayList<T> items);
+
+ /**
+ * Called when the search from secondary source is complete.
+ *
+ * @param items list of search results
+ */
+ void onAppendSearchResult(String query, ArrayList<T> items);
+
+ /**
+ * Called when the search results should be cleared.
+ */
+ void clearSearchResult();
+}
+
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index 21ad275..0e8d4ae 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.secondarydisplay;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.ActivityOptions;
@@ -37,9 +35,10 @@
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.data.AppInfo;
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.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
@@ -47,7 +46,7 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.views.BaseDragLayer;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import java.util.ArrayList;
import java.util.HashMap;
@@ -196,9 +195,6 @@
public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
@Override
- public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
-
- @Override
public void bindScreens(IntArray orderedScreenIds) { }
@Override
@@ -219,12 +215,12 @@
ArrayList<ItemInfo> addAnimated) { }
@Override
- public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
- mAppsView.getAppsStore().updatePromiseAppProgress(app);
+ public void bindIncrementalDownloadProgressUpdated(AppInfo app) {
+ mAppsView.getAppsStore().updateProgressBar(app);
}
@Override
- public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) { }
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
@Override
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
@@ -236,7 +232,7 @@
public void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher) { }
@Override
- public void bindAllWidgets(ArrayList<WidgetListRowEntry> widgets) { }
+ public void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
@Override
public void onPageBoundSynchronously(int page) { }
@@ -298,6 +294,7 @@
@Override
public void bindAllApplications(AppInfo[] apps, int flags) {
mAppsView.getAppsStore().setApps(apps, flags);
+ PopupContainerWithArrow.dismissInvalidPopup(this);
}
public PopupDataProvider getPopupDataProvider() {
@@ -318,16 +315,18 @@
if (tag instanceof ItemInfo) {
ItemInfo item = (ItemInfo) tag;
Intent intent;
- if (item instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
- intent = promiseAppInfo.getMarketIntent(this);
+ if (item instanceof ItemInfoWithIcon
+ && (((ItemInfoWithIcon) item).runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+ intent = appInfo.getMarketIntent(this);
} else {
intent = item.getIntent();
}
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
- startActivitySafely(v, intent, item, CONTAINER_ALL_APPS);
+ startActivitySafely(v, intent, item);
}
}
}
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index b12d04f..8d676c9 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -15,7 +15,10 @@
*/
package com.android.launcher3.settings;
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
@@ -26,25 +29,29 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.EditText;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
@@ -99,6 +106,50 @@
maybeAddSandboxCategory();
}
+ private void filterPreferences(String query, PreferenceGroup pg) {
+ int count = pg.getPreferenceCount();
+ int hidden = 0;
+ for (int i = 0; i < count; i++) {
+ Preference preference = pg.getPreference(i);
+ if (preference instanceof PreferenceGroup) {
+ filterPreferences(query, (PreferenceGroup) preference);
+ } else {
+ String title = preference.getTitle().toString().toLowerCase().replace("_", " ");
+ if (query.isEmpty() || title.contains(query)) {
+ preference.setVisible(true);
+ } else {
+ preference.setVisible(false);
+ hidden++;
+ }
+ }
+ }
+ pg.setVisible(hidden != count);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ EditText filterBox = view.findViewById(R.id.filter_box);
+ filterBox.setVisibility(VISIBLE);
+ filterBox.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ String query = editable.toString().toLowerCase().replace("_", " ");
+ filterPreferences(query, mPreferenceScreen);
+ }
+ });
+ }
+
@Override
public void onDestroy() {
super.onDestroy();
@@ -161,7 +212,7 @@
Set<String> pluginActions = manager.getPluginActions();
- ArrayMap<Pair<String, String>, ArrayList<Pair<String, ServiceInfo>>> plugins =
+ ArrayMap<Pair<String, String>, ArrayList<Pair<String, ResolveInfo>>> plugins =
new ArrayMap<>();
Set<String> pluginPermissionApps = pm.getPackagesHoldingPermissions(
@@ -173,7 +224,7 @@
for (String action : pluginActions) {
String name = toName(action);
List<ResolveInfo> result = pm.queryIntentServices(
- new Intent(action), MATCH_DISABLED_COMPONENTS);
+ new Intent(action), MATCH_DISABLED_COMPONENTS | GET_RESOLVED_FILTER);
for (ResolveInfo info : result) {
String packageName = info.serviceInfo.packageName;
if (!pluginPermissionApps.contains(packageName)) {
@@ -184,7 +235,7 @@
if (!plugins.containsKey(key)) {
plugins.put(key, new ArrayList<>());
}
- plugins.get(key).add(Pair.create(name, info.serviceInfo));
+ plugins.get(key).add(Pair.create(name, info));
}
}
@@ -192,11 +243,11 @@
plugins.forEach((key, si) -> {
String packageName = key.first;
List<ComponentName> componentNames = si.stream()
- .map(p -> new ComponentName(packageName, p.second.name))
+ .map(p -> new ComponentName(packageName, p.second.serviceInfo.name))
.collect(Collectors.toList());
if (!componentNames.isEmpty()) {
SwitchPreference pref = new PluginPreference(
- prefContext, si.get(0).second.applicationInfo, enabler, componentNames);
+ prefContext, si.get(0).second, enabler, componentNames);
pref.setSummary("Plugins: "
+ si.stream().map(p -> p.first).collect(Collectors.joining(", ")));
mPluginsCategory.addPreference(pref);
@@ -224,7 +275,8 @@
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra(
- "tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
+ "tutorial_steps",
+ new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchBackTutorialPreference);
@@ -233,7 +285,9 @@
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"HOME_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchHomeTutorialPreference);
@@ -242,7 +296,9 @@
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"OVERVIEW_NAVIGATION"}));
return true;
});
sandboxCategory.addPreference(launchOverviewTutorialPreference);
@@ -251,10 +307,23 @@
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
- startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"ASSISTANT"}));
return true;
});
sandboxCategory.addPreference(launchAssistantTutorialPreference);
+ Preference launchSandboxModeTutorialPreference = new Preference(context);
+ launchSandboxModeTutorialPreference.setKey("launchSandboxMode");
+ launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
+ launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
+ launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
+ startActivity(launchSandboxIntent.putExtra(
+ "tutorial_steps",
+ new String[] {"SANDBOX_MODE"}));
+ return true;
+ });
+ sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
}
private String toName(String action) {
@@ -272,21 +341,33 @@
}
private static class PluginPreference extends SwitchPreference {
- private final boolean mHasSettings;
- private final PreferenceDataStore mPluginEnabler;
private final String mPackageName;
+ private final ResolveInfo mSettingsInfo;
+ private final PreferenceDataStore mPluginEnabler;
private final List<ComponentName> mComponentNames;
- PluginPreference(Context prefContext, ApplicationInfo info,
+ PluginPreference(Context prefContext, ResolveInfo pluginInfo,
PreferenceDataStore pluginEnabler, List<ComponentName> componentNames) {
super(prefContext);
PackageManager pm = prefContext.getPackageManager();
- mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
- .setPackage(info.packageName), 0) != null;
- mPackageName = info.packageName;
- mComponentNames = componentNames;
+ mPackageName = pluginInfo.serviceInfo.applicationInfo.packageName;
+ Intent settingsIntent = new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName);
+ // If any Settings activity in app has category filters, set plugin action as category.
+ List<ResolveInfo> settingsInfos =
+ pm.queryIntentActivities(settingsIntent, GET_RESOLVED_FILTER);
+ if (pluginInfo.filter != null) {
+ for (ResolveInfo settingsInfo : settingsInfos) {
+ if (settingsInfo.filter != null && settingsInfo.filter.countCategories() > 0) {
+ settingsIntent.addCategory(pluginInfo.filter.getAction(0));
+ break;
+ }
+ }
+ }
+
+ mSettingsInfo = pm.resolveActivity(settingsIntent, 0);
mPluginEnabler = pluginEnabler;
- setTitle(info.loadLabel(pm));
+ mComponentNames = componentNames;
+ setTitle(pluginInfo.loadLabel(pm));
setChecked(isPluginEnabled());
setWidgetLayoutResource(R.layout.switch_preference_with_settings);
}
@@ -327,17 +408,14 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
- : View.GONE);
- holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
- : View.GONE);
+ boolean hasSettings = mSettingsInfo != null;
+ holder.findViewById(R.id.settings).setVisibility(hasSettings ? VISIBLE : GONE);
+ holder.findViewById(R.id.divider).setVisibility(hasSettings ? VISIBLE : GONE);
holder.findViewById(R.id.settings).setOnClickListener(v -> {
- ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
- new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName), 0);
- if (result != null) {
+ if (hasSettings) {
v.getContext().startActivity(new Intent().setComponent(
- new ComponentName(result.activityInfo.packageName,
- result.activityInfo.name)));
+ new ComponentName(mSettingsInfo.activityInfo.packageName,
+ mSettingsInfo.activityInfo.name)));
}
});
holder.itemView.setOnLongClickListener(v -> {
diff --git a/src/com/android/launcher3/settings/NotificationDotsPreference.java b/src/com/android/launcher3/settings/NotificationDotsPreference.java
index a91303a..a354169 100644
--- a/src/com/android/launcher3/settings/NotificationDotsPreference.java
+++ b/src/com/android/launcher3/settings/NotificationDotsPreference.java
@@ -35,14 +35,14 @@
import com.android.launcher3.R;
import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.util.SecureSettingsObserver;
+import com.android.launcher3.util.SettingsCache;
/**
* A {@link Preference} for indicating notification dots status.
* Also has utility methods for updating UI based on dots status changes.
*/
public class NotificationDotsPreference extends Preference
- implements SecureSettingsObserver.OnChangeListener {
+ implements SettingsCache.OnChangeListener {
private boolean mWidgetFrameVisible = false;
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index d3213a1..f03065c 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -18,14 +18,13 @@
import static androidx.core.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
-import static com.android.launcher3.SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
+import static com.android.launcher3.util.SettingsCache.NOTIFICATION_ENABLED_LISTENERS;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
-import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -45,8 +44,9 @@
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.util.SettingsCache;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
-import com.android.launcher3.util.SecureSettingsObserver;
/**
* Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -59,8 +59,6 @@
private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
private static final String NOTIFICATION_DOTS_PREFERENCE_KEY = "pref_icon_badging";
- /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
- private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
public static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
public static final String EXTRA_SHOW_FRAGMENT_ARGS = ":settings:show_fragment_args";
@@ -126,10 +124,12 @@
*/
public static class LauncherSettingsFragment extends PreferenceFragmentCompat {
- private SecureSettingsObserver mNotificationDotsObserver;
+ private SettingsCache mSettingsCache;
private String mHighLightKey;
private boolean mPreferenceHighlighted = false;
+ private NotificationDotsPreference mNotificationSettingsChangedListener;
+ private Preference mDeveloperOptionPref;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -172,25 +172,23 @@
protected boolean initPreference(Preference preference) {
switch (preference.getKey()) {
case NOTIFICATION_DOTS_PREFERENCE_KEY:
- if (!Utilities.ATLEAST_OREO ||
- !getResources().getBoolean(R.bool.notification_dots_enabled)) {
+ if (WidgetsModel.GO_DISABLE_NOTIFICATION_DOTS) {
return false;
}
// Listen to system notification dot settings while this UI is active.
- mNotificationDotsObserver = newNotificationSettingsObserver(
- getActivity(), (NotificationDotsPreference) preference);
- mNotificationDotsObserver.register();
+ mSettingsCache = SettingsCache.INSTANCE.get(getActivity());
+ mNotificationSettingsChangedListener =
+ ((NotificationDotsPreference) preference);
+ mSettingsCache.register(NOTIFICATION_BADGING_URI,
+ (NotificationDotsPreference) mNotificationSettingsChangedListener);
// Also listen if notification permission changes
- mNotificationDotsObserver.getResolver().registerContentObserver(
- Settings.Secure.getUriFor(NOTIFICATION_ENABLED_LISTENERS), false,
- mNotificationDotsObserver);
- mNotificationDotsObserver.dispatchOnChange();
+ mSettingsCache.register(NOTIFICATION_ENABLED_LISTENERS,
+ mNotificationSettingsChangedListener);
+ mSettingsCache.dispatchOnChange(NOTIFICATION_BADGING_URI);
+ mSettingsCache.dispatchOnChange(NOTIFICATION_ENABLED_LISTENERS);
return true;
- case ADD_ICON_PREFERENCE_KEY:
- return Utilities.ATLEAST_OREO;
-
case ALLOW_ROTATION_PREFERENCE_KEY:
if (getResources().getBoolean(R.bool.allow_rotation)) {
// Launcher supports rotation by default. No need to show this setting.
@@ -205,18 +203,37 @@
return FeatureFlags.showFlagTogglerUi(getContext());
case DEVELOPER_OPTIONS_KEY:
- // Show if plugins are enabled or flag UI is enabled.
- return FeatureFlags.showFlagTogglerUi(getContext()) ||
- PluginManagerWrapper.hasPlugins(getContext());
+ mDeveloperOptionPref = preference;
+ return updateDeveloperOption();
}
return true;
}
+ /**
+ * Show if plugins are enabled or flag UI is enabled.
+ * @return True if we should show the preference option.
+ */
+ private boolean updateDeveloperOption() {
+ boolean showPreference = FeatureFlags.showFlagTogglerUi(getContext())
+ || PluginManagerWrapper.hasPlugins(getContext());
+ if (mDeveloperOptionPref != null) {
+ mDeveloperOptionPref.setEnabled(showPreference);
+ if (showPreference) {
+ getPreferenceScreen().addPreference(mDeveloperOptionPref);
+ } else {
+ getPreferenceScreen().removePreference(mDeveloperOptionPref);
+ }
+ }
+ return showPreference;
+ }
+
@Override
public void onResume() {
super.onResume();
+ updateDeveloperOption();
+
if (isAdded() && !mPreferenceHighlighted) {
PreferenceHighlighter highlighter = createHighlighter();
if (highlighter != null) {
@@ -255,9 +272,11 @@
@Override
public void onDestroy() {
- if (mNotificationDotsObserver != null) {
- mNotificationDotsObserver.unregister();
- mNotificationDotsObserver = null;
+ if (mSettingsCache != null) {
+ mSettingsCache.unregister(NOTIFICATION_BADGING_URI,
+ mNotificationSettingsChangedListener);
+ mSettingsCache.unregister(NOTIFICATION_ENABLED_LISTENERS,
+ mNotificationSettingsChangedListener);
}
super.onDestroy();
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index eb68592..ad3f8df 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -132,6 +132,12 @@
mLoadingStatePlaceholder.draw(canvas);
}
+ @Override
+ protected void drawDotIfNecessary(Canvas canvas) {
+ // This view (with the text label to the side of the icon) is not designed for a dot to be
+ // drawn on top of it, so never draw one even if a notification for this shortcut exists.
+ }
+
private void showLoadingState(boolean loading) {
if (loading == mShowLoadingState) {
return;
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index 3ca9490..0c6d675 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -30,6 +30,10 @@
return componentName.getClassName();
}
+ public String getPackageName() {
+ return componentName.getPackageName();
+ }
+
/**
* Creates a {@link ShortcutRequest} for this key
*/
diff --git a/src/com/android/launcher3/statemanager/StateManager.java b/src/com/android/launcher3/statemanager/StateManager.java
index 2792308..2b51e97 100644
--- a/src/com/android/launcher3/statemanager/StateManager.java
+++ b/src/com/android/launcher3/statemanager/StateManager.java
@@ -16,6 +16,8 @@
package com.android.launcher3.statemanager;
+import static android.animation.ValueAnimator.areAnimatorsEnabled;
+
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import android.animation.Animator;
@@ -24,15 +26,12 @@
import android.animation.AnimatorSet;
import android.os.Handler;
import android.os.Looper;
-import android.util.Log;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -89,7 +88,9 @@
public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
- mStateHandlers = mActivity.createStateHandlers();
+ ArrayList<StateHandler> handlers = new ArrayList<>();
+ mActivity.collectStateHandlers(handlers);
+ mStateHandlers = handlers.toArray(new StateHandler[handlers.size()]);
}
return mStateHandlers;
}
@@ -179,7 +180,7 @@
private void goToState(STATE_TYPE state, boolean animated, long delay,
final Runnable onCompleteRunnable) {
- animated &= Utilities.areAnimationsEnabled(mActivity);
+ animated &= areAnimatorsEnabled();
if (mActivity.isInState(state)) {
if (mConfig.currentAnimation == null) {
// Run any queued runnable
@@ -301,10 +302,6 @@
}
private PendingAnimation createAnimationToNewWorkspaceInternal(final STATE_TYPE state) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "createAnimationToNewWorkspaceInternal: "
- + state);
- }
PendingAnimation builder = new PendingAnimation(mConfig.duration);
if (mConfig.getAnimComponents() != 0) {
for (StateHandler handler : getStateHandlers()) {
@@ -327,9 +324,6 @@
@Override
public void onAnimationSuccess(Animator animator) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "onAnimationSuccess: " + state);
- }
onStateTransitionEnd(state);
}
};
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 601e117..8a35cb3 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -30,6 +30,8 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.views.BaseDragLayer;
+import java.util.List;
+
/**
* Abstract activity with state management
* @param <STATE_TYPE> Type of state object
@@ -46,7 +48,7 @@
/**
* Create handlers to control the property changes for this activity
*/
- protected abstract StateHandler<STATE_TYPE>[] createStateHandlers();
+ protected abstract void collectStateHandlers(List<StateHandler> out);
/**
* Returns true if the activity is in the provided state
@@ -150,8 +152,8 @@
private void handleDeferredResume() {
if (hasBeenResumed() && !getStateManager().getState().hasFlag(FLAG_NON_INTERACTIVE)) {
- onDeferredResumed();
addActivityFlags(ACTIVITY_STATE_DEFERRED_RESUMED);
+ onDeferredResumed();
mDeferredResumePending = false;
} else {
diff --git a/src/com/android/launcher3/states/HintState.java b/src/com/android/launcher3/states/HintState.java
index b8a184f..fd1d965 100644
--- a/src/com/android/launcher3/states/HintState.java
+++ b/src/com/android/launcher3/states/HintState.java
@@ -15,11 +15,12 @@
*/
package com.android.launcher3.states;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+
import android.content.Context;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* Scale down workspace/hotseat to hint at going to either overview (on pause) or first home screen.
@@ -30,7 +31,7 @@
| FLAG_HAS_SYS_UI_SCRIM;
public HintState(int id) {
- super(id, ContainerType.DEFAULT_CONTAINERTYPE, STATE_FLAGS);
+ super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
}
@Override
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 5a60f53..2da06e9 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -21,14 +21,9 @@
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import android.app.Activity;
-import android.content.ContentResolver;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
-import android.util.Log;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -43,16 +38,6 @@
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
- private final ContentResolver mContentResolver;
- private boolean mSystemAutoRotateEnabled;
-
- private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
- @Override
- public void onChange(boolean selfChange) {
- updateAutoRotateSetting();
- }
- };
-
public static boolean getAllowRotationDefaultValue() {
// 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.
@@ -66,7 +51,7 @@
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
- private final Activity mActivity;
+ private Activity mActivity;
private final SharedPreferences mSharedPrefs;
private boolean mIgnoreAutoRotateSettings;
@@ -91,7 +76,8 @@
private boolean mInitialized;
private boolean mDestroyed;
- private int mLastActivityFlags = -1;
+ // Initialize mLastActivityFlags to a value not used by SCREEN_ORIENTATION flags
+ private int mLastActivityFlags = -999;
public RotationHelper(Activity activity) {
mActivity = activity;
@@ -106,30 +92,16 @@
} else {
mSharedPrefs = null;
}
-
- mContentResolver = activity.getContentResolver();
- }
-
- private void updateAutoRotateSetting() {
- int autoRotateEnabled = 0;
- try {
- autoRotateEnabled = Settings.System.getInt(mContentResolver,
- Settings.System.ACCELEROMETER_ROTATION);
- } catch (Settings.SettingNotFoundException e) {
- Log.e(TAG, "autorotate setting not found", e);
- }
-
- mSystemAutoRotateEnabled = autoRotateEnabled == 1;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ if (mDestroyed) return;
boolean wasRotationEnabled = mHomeRotationEnabled;
mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
if (mHomeRotationEnabled != wasRotationEnabled) {
notifyChange();
- updateAutoRotateSetting();
}
}
@@ -165,21 +137,16 @@
if (!mInitialized) {
mInitialized = true;
notifyChange();
-
- mContentResolver.registerContentObserver(
- Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
- false, mSystemAutoRotateObserver);
- updateAutoRotateSetting();
}
}
public void destroy() {
if (!mDestroyed) {
mDestroyed = true;
+ mActivity = null;
if (mSharedPrefs != null) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
- mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
}
}
@@ -225,9 +192,8 @@
@Override
public String toString() {
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
- + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b,"
- + " mSystemAutoRotateEnabled=%b]",
+ + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]",
mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
- mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled);
+ mIgnoreAutoRotateSettings, mHomeRotationEnabled);
}
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 2a4f887..fce8fff 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.states;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+
import android.content.Context;
import android.graphics.Rect;
@@ -22,7 +24,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* Definition for spring loaded state used during drag and drop.
@@ -35,7 +36,7 @@
| FLAG_HIDE_BACK_BUTTON;
public SpringLoadedState(int id) {
- super(id, ContainerType.WORKSPACE, STATE_FLAGS);
+ super(id, LAUNCHER_STATE_HOME, STATE_FLAGS);
}
@Override
@@ -89,4 +90,9 @@
public float getWorkspaceScrimAlpha(Launcher launcher) {
return 0.3f;
}
+
+ @Override
+ public int getVisibleElements(Launcher launcher) {
+ return (super.getVisibleElements(launcher) | HOTSEAT_ICONS) & ~TASKBAR;
+ }
}
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 8b72177..e4c67ee 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -37,7 +37,8 @@
PLAY_ATOMIC_OVERVIEW_SCALE,
PLAY_ATOMIC_OVERVIEW_PEEK,
SKIP_OVERVIEW,
- SKIP_DEPTH_CONTROLLER
+ SKIP_DEPTH_CONTROLLER,
+ SKIP_TASKBAR,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimationFlags {}
@@ -46,6 +47,7 @@
public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
public static final int SKIP_OVERVIEW = 1 << 3;
public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
+ public static final int SKIP_TASKBAR = 1 << 5;
public long duration;
public boolean userControlled;
@@ -72,6 +74,8 @@
ANIM_OVERVIEW_MODAL,
ANIM_DEPTH,
ANIM_OVERVIEW_ACTIONS_FADE,
+ ANIM_TASKBAR_FADE,
+ ANIM_HOTSEAT_FADE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface AnimType {}
@@ -91,8 +95,10 @@
public static final int ANIM_OVERVIEW_MODAL = 13;
public static final int ANIM_DEPTH = 14;
public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
+ public static final int ANIM_TASKBAR_FADE = 16;
+ public static final int ANIM_HOTSEAT_FADE = 17; // if not set, falls back to ANIM_WORKSPACE_FADE
- private static final int ANIM_TYPES_COUNT = 16;
+ private static final int ANIM_TYPES_COUNT = 18;
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 38b3712..4261d08 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -33,6 +33,7 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.util.ResourceBasedOverride;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
@@ -92,6 +93,11 @@
l -> l.getAppsView().getActiveRecyclerView().getCurrentScrollY());
}
+ case TestProtocol.REQUEST_WIDGETS_SCROLL_Y: {
+ return getLauncherUIProperty(Bundle::putInt,
+ l -> WidgetsFullSheet.getWidgetsView(l).getCurrentScrollY());
+ }
+
case TestProtocol.REQUEST_WINDOW_INSETS: {
return getUIProperty(Bundle::putParcelable, a -> {
WindowInsets insets = a.getWindow()
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 58bff09..7cb6e34 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -27,12 +27,11 @@
public static final int NORMAL_STATE_ORDINAL = 0;
public static final int SPRING_LOADED_STATE_ORDINAL = 1;
public static final int OVERVIEW_STATE_ORDINAL = 2;
- public static final int OVERVIEW_PEEK_STATE_ORDINAL = 3;
- public static final int OVERVIEW_MODAL_TASK_STATE_ORDINAL = 4;
- public static final int QUICK_SWITCH_STATE_ORDINAL = 5;
- public static final int ALL_APPS_STATE_ORDINAL = 6;
- public static final int BACKGROUND_APP_STATE_ORDINAL = 7;
- public static final int HINT_STATE_ORDINAL = 8;
+ public static final int OVERVIEW_MODAL_TASK_STATE_ORDINAL = 3;
+ public static final int QUICK_SWITCH_STATE_ORDINAL = 4;
+ public static final int ALL_APPS_STATE_ORDINAL = 5;
+ public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
+ public static final int HINT_STATE_ORDINAL = 7;
public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static final String SEQUENCE_MAIN = "Main";
public static final String SEQUENCE_TIS = "TIS";
@@ -46,8 +45,6 @@
return "SpringLoaded";
case OVERVIEW_STATE_ORDINAL:
return "Overview";
- case OVERVIEW_PEEK_STATE_ORDINAL:
- return "OverviewPeek";
case OVERVIEW_MODAL_TASK_STATE_ORDINAL:
return "OverviewModal";
case QUICK_SWITCH_STATE_ORDINAL:
@@ -81,6 +78,7 @@
public static final String REQUEST_UNFREEZE_APP_LIST = "unfreeze-app-list";
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";
public static final String REQUEST_WINDOW_INSETS = "window-insets";
public static final String REQUEST_PID = "pid";
public static final String REQUEST_TOTAL_PSS_KB = "total_pss";
@@ -97,15 +95,16 @@
public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
- public static final String REQUEST_OVERVIEW_ACTIONS_ENABLED = "overview-actions-enabled";
public static final String REQUEST_OVERVIEW_SHARE_ENABLED = "overview-share-enabled";
+ public static final String REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED =
+ "overview-content-push-enabled";
public static boolean sDisableSensorRotation;
public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
public static final String PERMANENT_DIAG_TAG = "TaplTarget";
- public static final String PAUSE_NOT_DETECTED = "b/139891609";
- public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
public static final String NO_SWIPE_TO_HOME = "b/158017601";
public static final String WORK_PROFILE_REMOVED = "b/159671700";
+ public static final String TIS_NO_EVENTS = "b/180915942";
+ public static final String GET_RECENTS_FAILED = "b/177472267";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 3c78b08..516fc74 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -15,25 +15,29 @@
*/
package com.android.launcher3.touch;
-import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
+import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.config.FeatureFlags.UNSTABLE_SPRINGS;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEDOWN;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_UNKNOWN_SWIPEUP;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_ATOMIC_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.PLAY_NON_ATOMIC;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.os.SystemClock;
-import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
@@ -43,15 +47,10 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.FlingBlockCheck;
import com.android.launcher3.util.TouchController;
@@ -61,19 +60,20 @@
public abstract class AbstractStateChangeTouchController
implements TouchController, SingleAxisSwipeDetector.Listener {
- // Progress after which the transition is assumed to be a success in case user does not fling
- public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
/**
* Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this.
+ * TODO: Remove the atomic animation altogether and just go to OVERVIEW directly (b/175137718).
*/
- public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 0.5f;
+ public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 1f;
protected final long ATOMIC_DURATION = getAtomicDuration();
protected final Launcher mLauncher;
protected final SingleAxisSwipeDetector mDetector;
protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
+ protected final AnimatorListener mClearStateOnCancelListener =
+ newCancelListener(this::clearState);
+
private boolean mNoIntercept;
private boolean mIsLogContainerSet;
protected int mStartContainerType;
@@ -82,7 +82,7 @@
protected LauncherState mFromState;
protected LauncherState mToState;
protected AnimatorPlaybackController mCurrentAnimation;
- protected PendingAnimation mPendingAnimation;
+ protected boolean mGoingBetweenStates = true;
private float mStartProgress;
// Ratio of transition process [0, 1] to drag displacement (px)
@@ -120,7 +120,7 @@
protected abstract boolean canInterceptTouch(MotionEvent ev);
@Override
- public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = !canInterceptTouch(ev);
if (mNoIntercept) {
@@ -168,9 +168,6 @@
@Override
public final boolean onControllerTouchEvent(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "onControllerTouchEvent");
- }
return mDetector.onTouchEvent(ev);
}
@@ -187,31 +184,24 @@
protected abstract float initCurrentAnimation(@AnimationFlags int animComponents);
- /**
- * Returns the container that the touch started from when leaving NORMAL state.
- */
- protected abstract int getLogContainerTypeForNormalState(MotionEvent ev);
-
private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
: reachedToState ? mToState : mFromState;
LauncherState newToState = getTargetState(newFromState, isDragTowardPositive);
+ onReinitToState(newToState);
+
if (newFromState == mFromState && newToState == mToState || (newFromState == newToState)) {
return false;
}
mFromState = newFromState;
mToState = newToState;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.OVERIEW_NOT_ALLAPPS, "reinitCurrentAnimation: "
- + newToState.ordinal + " " + getClass().getSimpleName());
- }
mStartProgress = 0;
mPassedOverviewAtomicThreshold = false;
if (mCurrentAnimation != null) {
- mCurrentAnimation.setOnCancelRunnable(null);
+ mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
}
int animComponents = goingBetweenNormalAndOverview(mFromState, mToState)
? PLAY_NON_ATOMIC : ANIM_ALL_COMPONENTS;
@@ -235,11 +225,17 @@
return true;
}
+ protected void onReinitToState(LauncherState newToState) {
+ }
+
+ protected void onReachedFinalState(LauncherState newToState) {
+ }
+
protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
LauncherState toState) {
return (fromState == NORMAL || fromState == OVERVIEW)
&& (toState == NORMAL || toState == OVERVIEW)
- && mPendingAnimation == null;
+ && mGoingBetweenStates;
}
@Override
@@ -264,10 +260,6 @@
}
mCanBlockFling = mFromState == NORMAL;
mFlingBlockCheck.unblockFling();
- // Must be called after all the animation controllers have been paused
- if (mToState == ALL_APPS || mToState == NORMAL) {
- mLauncher.getAllAppsController().onDragStart(mToState == ALL_APPS);
- }
}
@Override
@@ -302,11 +294,11 @@
public boolean onDrag(float displacement, MotionEvent ev) {
if (!mIsLogContainerSet) {
if (mStartState == ALL_APPS) {
- mStartContainerType = ContainerType.ALLAPPS;
+ mStartContainerType = LAUNCHER_STATE_ALLAPPS;
} else if (mStartState == NORMAL) {
- mStartContainerType = getLogContainerTypeForNormalState(ev);
+ mStartContainerType = LAUNCHER_STATE_HOME;
} else if (mStartState == OVERVIEW) {
- mStartContainerType = ContainerType.TASKSWITCHER;
+ mStartContainerType = LAUNCHER_STATE_OVERVIEW;
}
mIsLogContainerSet = true;
}
@@ -335,9 +327,7 @@
if (!goingBetweenNormalAndOverview(fromState, toState)) {
return;
}
- float threshold = toState == OVERVIEW ? ATOMIC_OVERVIEW_ANIM_THRESHOLD
- : 1f - ATOMIC_OVERVIEW_ANIM_THRESHOLD;
- boolean passedThreshold = progress >= threshold;
+ boolean passedThreshold = progress >= ATOMIC_OVERVIEW_ANIM_THRESHOLD;
if (passedThreshold != mPassedOverviewAtomicThreshold) {
LauncherState atomicFromState = passedThreshold ? fromState: toState;
LauncherState atomicToState = passedThreshold ? toState : fromState;
@@ -395,8 +385,13 @@
@Override
public void onDragEnd(float velocity) {
+ if (mCurrentAnimation == null) {
+ // Unlikely, but we may have been canceled just before onDragEnd(). We assume whoever
+ // canceled us will handle a new state transition to clean up.
+ return;
+ }
+
boolean fling = mDetector.isFling(velocity);
- final int logAction = fling ? Touch.FLING : Touch.SWIPE;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
if (blockedFling) {
@@ -413,9 +408,8 @@
? mToState : mFromState;
// snap to top or bottom using the release velocity
} else {
- float successProgress = mToState == ALL_APPS
- ? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS;
- targetState = (interpolatedProgress > successProgress) ? mToState : mFromState;
+ targetState =
+ (interpolatedProgress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
}
final float endProgress;
@@ -439,7 +433,8 @@
} else {
// Let the state manager know that the animation didn't go to the target state,
// but don't cancel ourselves (we already clean up when the animation completes).
- mCurrentAnimation.dispatchOnCancelWithoutCancelRunnable();
+ mCurrentAnimation.getTarget().removeListener(mClearStateOnCancelListener);
+ mCurrentAnimation.dispatchOnCancel();
endProgress = 0;
if (progress <= 0) {
@@ -453,7 +448,7 @@
}
}
- mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+ mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState));
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
anim.setFloatValues(startProgress, endProgress);
maybeUpdateAtomicAnim(mFromState, targetState, targetState == mToState ? 1f : 0f);
@@ -508,15 +503,7 @@
if (mAtomicAnim == null) {
return 0;
}
- if (Utilities.ATLEAST_OREO) {
- return mAtomicAnim.getTotalDuration() - mAtomicAnim.getCurrentPlayTime();
- } else {
- long remainingDuration = 0;
- for (Animator anim : mAtomicAnim.getChildAnimations()) {
- remainingDuration = Math.max(remainingDuration, anim.getDuration());
- }
- return remainingDuration;
- }
+ return mAtomicAnim.getTotalDuration() - mAtomicAnim.getCurrentPlayTime();
}
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
@@ -525,58 +512,44 @@
.setInterpolator(scrollInterpolatorForVelocity(velocity));
}
- protected int getDirectionForLog() {
- return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN;
- }
-
- protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+ protected void onSwipeInteractionCompleted(LauncherState targetState) {
if (mAtomicComponentsController != null) {
mAtomicComponentsController.getAnimationPlayer().end();
mAtomicComponentsController = null;
}
+ onReachedFinalState(mToState);
clearState();
- boolean shouldGoToTargetState = true;
- if (mPendingAnimation != null) {
- boolean reachedTarget = mToState == targetState;
- mPendingAnimation.finish(reachedTarget, logAction);
- mPendingAnimation = null;
- shouldGoToTargetState = !reachedTarget;
- }
+ boolean shouldGoToTargetState = mGoingBetweenStates || (mToState != targetState);
if (shouldGoToTargetState) {
- goToTargetState(targetState, logAction);
+ goToTargetState(targetState);
}
}
- protected void goToTargetState(LauncherState targetState, int logAction) {
+ protected void goToTargetState(LauncherState targetState) {
if (targetState != mStartState) {
- logReachedState(logAction, targetState);
+ logReachedState(targetState);
}
if (!mLauncher.isInState(targetState)) {
// If we're already in the target state, don't jump to it at the end of the animation in
// case the user started interacting with it before the animation finished.
mLauncher.getStateManager().goToState(targetState, false /* animated */);
}
- mLauncher.getDragLayer().getScrim().createSysuiMultiplierAnim(1f).setDuration(0).start();
+ mLauncher.getDragLayer().getSysUiScrim().createSysuiMultiplierAnim(
+ 1f).setDuration(0).start();
}
- private void logReachedState(int logAction, LauncherState targetState) {
+ private void logReachedState(LauncherState targetState) {
// Transition complete. log the action
- mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
- getDirectionForLog(), mDetector.getDownX(), mDetector.getDownY(),
- mStartContainerType /* e.g., hotseat */,
- mStartState.containerType /* e.g., workspace */,
- targetState.containerType,
- mLauncher.getWorkspace().getCurrentPage());
mLauncher.getStatsLogManager().logger()
- .withSrcState(StatsLogManager.containerTypeToAtomState(mStartState.containerType))
- .withDstState(StatsLogManager.containerTypeToAtomState(targetState.containerType))
+ .withSrcState(mStartState.statsLogOrdinal)
+ .withDstState(targetState.statsLogOrdinal)
.withContainerInfo(LauncherAtom.ContainerInfo.newBuilder()
.setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder()
.setPageIndex(mLauncher.getWorkspace().getCurrentPage()))
.build())
- .log(StatsLogManager.getLauncherAtomEvent(mStartState.containerType,
- targetState.containerType, mToState.ordinal > mFromState.ordinal
+ .log(StatsLogManager.getLauncherAtomEvent(mStartState.statsLogOrdinal,
+ targetState.statsLogOrdinal, mToState.ordinal > mFromState.ordinal
? LAUNCHER_UNKNOWN_SWIPEUP
: LAUNCHER_UNKNOWN_SWIPEDOWN));
}
@@ -587,6 +560,7 @@
mAtomicAnim.cancel();
mAtomicAnim = null;
}
+ mGoingBetweenStates = true;
mScheduleResumeAtomicComponent = false;
mDetector.finishedScrolling();
mDetector.setDetectableScrollConditions(0, false);
diff --git a/src/com/android/launcher3/touch/AllAppsSwipeController.java b/src/com/android/launcher3/touch/AllAppsSwipeController.java
index 4a202b6..f9dcf2d 100644
--- a/src/com/android/launcher3/touch/AllAppsSwipeController.java
+++ b/src/com/android/launcher3/touch/AllAppsSwipeController.java
@@ -24,7 +24,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* TouchController to switch between NORMAL and ALL_APPS state.
@@ -70,12 +69,6 @@
}
@Override
- protected int getLogContainerTypeForNormalState(MotionEvent ev) {
- return mLauncher.getDragLayer().isEventOverView(mLauncher.getHotseat(), mTouchDownEvent)
- ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
- }
-
- @Override
protected float initCurrentAnimation(@AnimationFlags int animComponents) {
float range = getShiftRange();
long maxAccuracy = (long) (2 * range);
diff --git a/src/com/android/launcher3/touch/BaseSwipeDetector.java b/src/com/android/launcher3/touch/BaseSwipeDetector.java
index 01b33d8..1276ece 100644
--- a/src/com/android/launcher3/touch/BaseSwipeDetector.java
+++ b/src/com/android/launcher3/touch/BaseSwipeDetector.java
@@ -26,8 +26,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import com.android.launcher3.testing.TestProtocol;
-
import java.util.LinkedList;
import java.util.Queue;
@@ -175,9 +173,6 @@
if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
setState(ScrollState.DRAGGING);
}
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "before report dragging");
- }
if (mState == ScrollState.DRAGGING) {
reportDragging(ev);
}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index f50b2b3..61bd30a 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,9 +17,8 @@
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_APP_LAUNCH_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
-import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_HOTSEAT;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
@@ -27,7 +26,9 @@
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import android.app.AlertDialog;
+import android.app.PendingIntent;
import android.content.Intent;
+import android.content.IntentSender;
import android.content.pm.LauncherApps;
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.Process;
@@ -38,12 +39,8 @@
import android.view.View.OnClickListener;
import android.widget.Toast;
-import androidx.annotation.Nullable;
-
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.folder.Folder;
@@ -52,14 +49,16 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.PromiseAppInfo;
+import com.android.launcher3.model.data.SearchActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.views.FloatingIconView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetManagerHelper;
@@ -74,13 +73,9 @@
/**
* Instance used for click handling on items
*/
- public static final OnClickListener INSTANCE = getInstance(null);
+ public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
- public static final OnClickListener getInstance(String sourceContainer) {
- return v -> onClick(v, sourceContainer);
- }
-
- private static void onClick(View v, String sourceContainer) {
+ private static void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) return;
@@ -90,18 +85,20 @@
Object tag = v.getTag();
if (tag instanceof WorkspaceItemInfo) {
- onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher, sourceContainer);
+ onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
} else if (tag instanceof AppInfo) {
- startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
- sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
+ startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher
+ );
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
}
+ } else if (tag instanceof SearchActionItemInfo) {
+ onClickSearchAction(launcher, (SearchActionItemInfo) tag);
}
}
@@ -193,7 +190,7 @@
// Fallback to using custom market intent.
Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
- launcher.startActivitySafely(v, intent, item, null);
+ launcher.startActivitySafely(v, intent, item);
}
/**
@@ -201,8 +198,7 @@
*
* @param v The view that was clicked. Must be a tagged with a {@link WorkspaceItemInfo}.
*/
- public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher,
- @Nullable String sourceContainer) {
+ public static void onClickAppShortcut(View v, WorkspaceItemInfo shortcut, Launcher launcher) {
if (shortcut.isDisabled()) {
final int disabledFlags = shortcut.runtimeStatusFlags
& WorkspaceItemInfo.FLAG_DISABLED_MASK;
@@ -236,30 +232,60 @@
? shortcut.getIntent().getComponent().getPackageName()
: shortcut.getIntent().getPackage();
if (!TextUtils.isEmpty(packageName)) {
- onClickPendingAppItem(v, launcher, packageName,
- shortcut.hasStatusFlag(WorkspaceItemInfo.FLAG_INSTALL_SESSION_ACTIVE));
+ onClickPendingAppItem(
+ v,
+ launcher,
+ packageName,
+ (shortcut.runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0);
return;
}
}
- if (sourceContainer == null && (
- shortcut.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
- || shortcut.container
- == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION)) {
- sourceContainer = CONTAINER_HOTSEAT;
- }
// Start activities
- startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer);
+ startAppShortcutOrInfoActivity(v, shortcut, launcher);
}
- private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
- @Nullable String sourceContainer) {
+ /**
+ * 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();
+ }
+ }
+ 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");
Intent intent;
- if (item instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
- intent = promiseAppInfo.getMarketIntent(launcher);
+ if (item instanceof ItemInfoWithIcon
+ && (((ItemInfoWithIcon) item).runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ ItemInfoWithIcon appInfo = (ItemInfoWithIcon) item;
+ intent = new PackageManagerHelper(launcher)
+ .getMarketIntent(appInfo.getTargetComponent().getPackageName());
} else {
intent = item.getIntent();
}
@@ -278,10 +304,10 @@
intent.setPackage(null);
}
}
- if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) {
+ if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation(v)) {
// Preload the icon to reduce latency b/w swapping the floating view with the original.
FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
}
- launcher.startActivitySafely(v, intent, item, sourceContainer);
+ launcher.startActivitySafely(v, intent, item);
}
}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 7baeab8..f876dd9 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -21,6 +21,7 @@
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.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED;
import android.view.View;
import android.view.View.OnLongClickListener;
@@ -32,6 +33,7 @@
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
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;
@@ -55,7 +57,7 @@
if (!(v.getTag() instanceof ItemInfo)) return false;
launcher.setWaitingForResult(null);
- beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions());
+ beginDrag(v, launcher, (ItemInfo) v.getTag(), launcher.getDefaultWorkspaceDragOptions());
return true;
}
@@ -86,6 +88,12 @@
if (!launcher.isInState(ALL_APPS) && !launcher.isInState(OVERVIEW)) return false;
if (launcher.getWorkspace().isSwitchingState()) return false;
+ StatsLogger logger = launcher.getStatsLogManager().logger();
+ if (v.getTag() instanceof ItemInfo) {
+ logger.withItemInfo((ItemInfo) v.getTag());
+ }
+ logger.log(LAUNCHER_ALLAPPS_ITEM_LONG_PRESSED);
+
// Start the drag
final DragController dragController = launcher.getDragController();
dragController.addDragListener(new DragController.DragListener() {
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index bfa24eb..8a64f3d 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -75,11 +75,6 @@
}
@Override
- public boolean isGoingUp(float displacement, boolean isRtl) {
- return isRtl ? displacement < 0 : displacement > 0;
- }
-
- @Override
public boolean isLayoutNaturalToLauncher() {
return false;
}
@@ -152,12 +147,6 @@
}
@Override
- public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
- view.setTranslationX(0);
- view.setTranslationY(translation);
- }
-
- @Override
public int getPrimaryScroll(View view) {
return view.getScrollY();
}
@@ -214,11 +203,6 @@
}
@Override
- public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() {
- return HORIZONTAL;
- }
-
- @Override
public int getPrimaryTranslationDirectionFactor() {
return -1;
}
@@ -228,11 +212,6 @@
}
@Override
- public int getTaskDragDisplacementFactor(boolean isRtl) {
- return isRtl ? 1 : -1;
- }
-
- @Override
public float getTaskMenuX(float x, View thumbnailView) {
return thumbnailView.getMeasuredWidth() + x;
}
@@ -260,6 +239,31 @@
lp.weight = 1;
}
+ /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+ @Override
+ public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
+ return HORIZONTAL;
+ }
+
+ @Override
+ public int getUpDirection(boolean isRtl) {
+ return isRtl ? SingleAxisSwipeDetector.DIRECTION_NEGATIVE
+ : SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+ }
+
+ @Override
+ public boolean isGoingUp(float displacement, boolean isRtl) {
+ return isRtl ? displacement < 0 : displacement > 0;
+ }
+
+ @Override
+ public int getTaskDragDisplacementFactor(boolean isRtl) {
+ return isRtl ? 1 : -1;
+ }
+
+ /* -------------------- */
+
@Override
public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
boolean layoutChild) {
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index e7d2ad5..e1cec87 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -66,7 +66,6 @@
int getSecondaryDimension(View view);
FloatProperty<View> getPrimaryViewTranslate();
FloatProperty<View> getSecondaryViewTranslate();
- void setPrimaryAndResetSecondaryTranslate(View view, float translation);
int getPrimaryScroll(View view);
float getPrimaryScale(View view);
int getChildStart(View view);
@@ -74,10 +73,8 @@
int getCenterForPage(View view, Rect insets);
int getScrollOffsetStart(View view, Rect insets);
int getScrollOffsetEnd(View view, Rect insets);
- SingleAxisSwipeDetector.Direction getOppositeSwipeDirection();
int getPrimaryTranslationDirectionFactor();
int getSecondaryTranslationDirectionFactor();
- int getTaskDragDisplacementFactor(boolean isRtl);
ChildBounds getChildBounds(View child, int childStart, int pageCenter, boolean layoutChild);
void setMaxScroll(AccessibilityEvent event, int maxScroll);
boolean getRecentsRtlSetting(Resources resources);
@@ -91,7 +88,6 @@
void delegateScrollBy(PagedView pagedView, int unboundedScroll, int x, int y);
void scrollerStartScroll(OverScroller scroller, int newPosition);
void getCurveProperties(PagedView view, Rect insets, CurveProperties out);
- boolean isGoingUp(float displacement, boolean isRtl);
boolean isLayoutNaturalToLauncher();
float getTaskMenuX(float x, View thumbnailView);
float getTaskMenuY(float y, View thumbnailView);
@@ -100,6 +96,16 @@
void setLayoutParamsForTaskMenuOptionItem(LinearLayout.LayoutParams lp);
int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect);
+ // The following are only used by TaskViewTouchHandler.
+ /** @return Either VERTICAL or HORIZONTAL. */
+ SingleAxisSwipeDetector.Direction getUpDownSwipeDirection();
+ /** @return Given {@link #getUpDownSwipeDirection()}, whether POSITIVE or NEGATIVE is up. */
+ int getUpDirection(boolean isRtl);
+ /** @return Whether the displacement is going towards the top of the screen. */
+ boolean isGoingUp(float displacement, boolean isRtl);
+ /** @return Either 1 or -1, a factor to multiply by so the animation goes the correct way. */
+ int getTaskDragDisplacementFactor(boolean isRtl);
+
/**
* Maps the velocity from the coordinate plane of the foreground app to that
* of Launcher's (which now will always be portrait)
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index e5a8967..bcaf5f4 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -73,12 +73,6 @@
}
@Override
- public boolean isGoingUp(float displacement, boolean isRtl) {
- // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
- return displacement < 0;
- }
-
- @Override
public boolean isLayoutNaturalToLauncher() {
return true;
}
@@ -149,12 +143,6 @@
}
@Override
- public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
- view.setTranslationX(translation);
- view.setTranslationY(0);
- }
-
- @Override
public int getPrimaryScroll(View view) {
return view.getScrollX();
}
@@ -211,11 +199,6 @@
}
@Override
- public SingleAxisSwipeDetector.Direction getOppositeSwipeDirection() {
- return VERTICAL;
- }
-
- @Override
public int getPrimaryTranslationDirectionFactor() {
return 1;
}
@@ -225,12 +208,6 @@
}
@Override
- public int getTaskDragDisplacementFactor(boolean isRtl) {
- // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
- return 1;
- }
-
- @Override
public float getTaskMenuX(float x, View thumbnailView) {
return x;
}
@@ -256,6 +233,33 @@
// no-op, defaults are fine
}
+ /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+ @Override
+ public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
+ return VERTICAL;
+ }
+
+ @Override
+ public int getUpDirection(boolean isRtl) {
+ // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+ return SingleAxisSwipeDetector.DIRECTION_POSITIVE;
+ }
+
+ @Override
+ public boolean isGoingUp(float displacement, boolean isRtl) {
+ // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+ return displacement < 0;
+ }
+
+ @Override
+ public int getTaskDragDisplacementFactor(boolean isRtl) {
+ // Ignore rtl since it only affects X value displacement, Y displacement doesn't change
+ return 1;
+ }
+
+ /* -------------------- */
+
@Override
public ChildBounds getChildBounds(View child, int childStart, int pageCenter,
boolean layoutChild) {
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 7153452..54af029 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -16,6 +16,8 @@
package com.android.launcher3.touch;
+import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
+
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -33,11 +35,6 @@
}
@Override
- public int getTaskDragDisplacementFactor(boolean isRtl) {
- return isRtl ? -1 : 1;
- }
-
- @Override
public boolean getRecentsRtlSetting(Resources resources) {
return Utilities.isRtl(resources);
}
@@ -53,11 +50,6 @@
}
@Override
- public boolean isGoingUp(float displacement, boolean isRtl) {
- return isRtl ? displacement > 0 : displacement < 0;
- }
-
- @Override
public void adjustFloatingIconStartVelocity(PointF velocity) {
float oldX = velocity.x;
float oldY = velocity.y;
@@ -75,13 +67,32 @@
}
@Override
- public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
- view.setTranslationX(0);
- view.setTranslationY(translation);
- }
-
- @Override
public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
return dp.widthPx - rect.right;
}
+
+ /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
+
+ @Override
+ public SingleAxisSwipeDetector.Direction getUpDownSwipeDirection() {
+ return HORIZONTAL;
+ }
+
+ @Override
+ public int getUpDirection(boolean isRtl) {
+ return isRtl ? SingleAxisSwipeDetector.DIRECTION_POSITIVE
+ : SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
+ }
+
+ @Override
+ public boolean isGoingUp(float displacement, boolean isRtl) {
+ return isRtl ? displacement > 0 : displacement < 0;
+ }
+
+ @Override
+ public int getTaskDragDisplacementFactor(boolean isRtl) {
+ return isRtl ? -1 : 1;
+ }
+
+ /* -------------------- */
}
diff --git a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
index 875eefb..8c3c115 100644
--- a/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
+++ b/src/com/android/launcher3/touch/SingleAxisSwipeDetector.java
@@ -17,7 +17,6 @@
import android.content.Context;
import android.graphics.PointF;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -25,7 +24,6 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
/**
* One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
@@ -60,6 +58,11 @@
return direction.x;
}
+ @NonNull
+ @Override
+ public String toString() {
+ return "VERTICAL";
+ }
};
public static final Direction HORIZONTAL = new Direction() {
@@ -86,6 +89,11 @@
return direction.y;
}
+ @NonNull
+ @Override
+ public String toString() {
+ return "HORIZONTAL";
+ }
};
private final Direction mDir;
@@ -105,11 +113,6 @@
super(config, isRtl);
mListener = l;
mDir = dir;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector.ctor "
- + l.getClass().getSimpleName()
- + " @ " + android.util.Log.getStackTraceString(new Throwable()));
- }
}
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
@@ -117,10 +120,6 @@
mIgnoreSlopWhenSettling = ignoreSlop;
}
- public int getScrollDirections() {
- return mScrollDirections;
- }
-
/**
* Returns if the start drag was towards the positive direction or negative.
*
@@ -161,10 +160,6 @@
@Override
protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "SingleAxisSwipeDetector "
- + mListener.getClass().getSimpleName());
- }
mListener.onDrag(mDir.extractDirection(displacement),
mDir.extractOrthogonalDirection(displacement), event);
}
diff --git a/src/com/android/launcher3/util/BgObjectWithLooper.java b/src/com/android/launcher3/util/BgObjectWithLooper.java
new file mode 100644
index 0000000..1483c43
--- /dev/null
+++ b/src/com/android/launcher3/util/BgObjectWithLooper.java
@@ -0,0 +1,46 @@
+/*
+ * 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.launcher3.util;
+
+import android.os.Looper;
+
+import androidx.annotation.WorkerThread;
+
+/**
+ * Utility class to define an object which does most of it's processing on a
+ * dedicated background thread.
+ */
+public abstract class BgObjectWithLooper {
+
+ /**
+ * Start initialization of the object
+ */
+ public final void initializeInBackground(String threadName) {
+ new Thread(this::runOnThread, threadName).start();
+ }
+
+ private void runOnThread() {
+ Looper.prepare();
+ onInitialized(Looper.myLooper());
+ Looper.loop();
+ }
+
+ /**
+ * Called on the background thread to handle initialization
+ */
+ @WorkerThread
+ protected abstract void onInitialized(Looper looper);
+}
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 0f81520..b3b69f6 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -26,14 +26,16 @@
import android.graphics.Point;
import android.util.Log;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
+
import java.util.function.Consumer;
/**
* {@link BroadcastReceiver} which watches configuration changes and
* notifies the callback in case changes which affect the device profile occur.
*/
-public class ConfigMonitor extends BroadcastReceiver implements
- DefaultDisplay.DisplayInfoChangeListener {
+public class ConfigMonitor extends BroadcastReceiver implements DisplayInfoChangeListener {
private static final String TAG = "ConfigMonitor";
@@ -57,9 +59,9 @@
mFontScale = config.fontScale;
mDensity = config.densityDpi;
- DefaultDisplay display = DefaultDisplay.INSTANCE.get(context);
+ DisplayController.DisplayHolder display = DisplayController.getDefaultDisplay(context);
display.addChangeListener(this);
- DefaultDisplay.Info displayInfo = display.getInfo();
+ Info displayInfo = display.getInfo();
mDisplayId = displayInfo.id;
mRealSize = new Point(displayInfo.realSize);
@@ -82,7 +84,7 @@
}
@Override
- public void onDisplayInfoChanged(DefaultDisplay.Info info, int flags) {
+ public void onDisplayInfoChanged(Info info, int flags) {
if (info.id != mDisplayId) {
return;
}
@@ -113,8 +115,7 @@
public void unregister() {
try {
mContext.unregisterReceiver(this);
- DefaultDisplay display = DefaultDisplay.INSTANCE.get(mContext);
- display.removeChangeListener(this);
+ DisplayController.getDefaultDisplay(mContext).removeChangeListener(this);
} catch (Exception e) {
Log.e(TAG, "Failed to unregister config monitor", e);
}
diff --git a/src/com/android/launcher3/util/ContentWriter.java b/src/com/android/launcher3/util/ContentWriter.java
index 30c9ff9..ee64e98 100644
--- a/src/com/android/launcher3/util/ContentWriter.java
+++ b/src/com/android/launcher3/util/ContentWriter.java
@@ -113,14 +113,26 @@
public static final class CommitParams {
- final Uri mUri = LauncherSettings.Favorites.CONTENT_URI;
- String mWhere;
- String[] mSelectionArgs;
+ final Uri mUri;
+ final String mWhere;
+ final String[] mSelectionArgs;
public CommitParams(String where, String[] selectionArgs) {
+ this(LauncherSettings.Favorites.CONTENT_URI, where, selectionArgs);
+ }
+
+ private CommitParams(Uri uri, String where, String[] selectionArgs) {
+ mUri = uri;
mWhere = where;
mSelectionArgs = selectionArgs;
}
+ /**
+ * Creates commit params for backup table.
+ */
+ public static CommitParams backupCommitParams(String where, String[] selectionArgs) {
+ return new CommitParams(
+ LauncherSettings.Favorites.BACKUP_CONTENT_URI, where, selectionArgs);
+ }
}
}
diff --git a/src/com/android/launcher3/util/DefaultDisplay.java b/src/com/android/launcher3/util/DefaultDisplay.java
deleted file mode 100644
index 35788a5..0000000
--- a/src/com/android/launcher3/util/DefaultDisplay.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.Handler;
-import android.os.Message;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Display;
-
-import androidx.annotation.VisibleForTesting;
-
-import java.util.ArrayList;
-
-/**
- * Utility class to cache properties of default display to avoid a system RPC on every call.
- */
-public class DefaultDisplay implements DisplayListener {
-
- public static final MainThreadInitializedObject<DefaultDisplay> INSTANCE =
- new MainThreadInitializedObject<>(DefaultDisplay::new);
-
- private static final String TAG = "DefaultDisplay";
-
- public static final int CHANGE_SIZE = 1 << 0;
- public static final int CHANGE_ROTATION = 1 << 1;
- public static final int CHANGE_FRAME_DELAY = 1 << 2;
-
- public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION | CHANGE_FRAME_DELAY;
-
- private final Context mDisplayContext;
- private final int mId;
- private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
- private final Handler mChangeHandler;
- private Info mInfo;
-
- private DefaultDisplay(Context context) {
- DisplayManager dm = context.getSystemService(DisplayManager.class);
- // Use application context to create display context so that it can have its own Resources.
- mDisplayContext = context.getApplicationContext().createDisplayContext(
- dm.getDisplay(DEFAULT_DISPLAY));
- // Note that the Display object must be obtained from DisplayManager which is associated to
- // the display context, so the Display is isolated from Activity and Application to provide
- // the actual state of device that excludes the additional adjustment and override.
- mInfo = new Info(mDisplayContext);
- mId = mInfo.id;
- mChangeHandler = new Handler(this::onChange);
-
- dm.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
- }
-
- @Override
- public final void onDisplayAdded(int displayId) { }
-
- @Override
- public final void onDisplayRemoved(int displayId) { }
-
- @Override
- public final void onDisplayChanged(int displayId) {
- if (displayId != mId) {
- return;
- }
-
- Info oldInfo = mInfo;
- Info info = new Info(mDisplayContext);
-
- int change = 0;
- if (info.hasDifferentSize(oldInfo)) {
- change |= CHANGE_SIZE;
- }
- if (oldInfo.rotation != info.rotation) {
- change |= CHANGE_ROTATION;
- }
- if (info.singleFrameMs != oldInfo.singleFrameMs) {
- change |= CHANGE_FRAME_DELAY;
- }
-
- if (change != 0) {
- mInfo = info;
- mChangeHandler.sendEmptyMessage(change);
- }
- }
-
- public static int getSingleFrameMs(Context context) {
- return INSTANCE.get(context).getInfo().singleFrameMs;
- }
-
- public Info getInfo() {
- return mInfo;
- }
-
- public void addChangeListener(DisplayInfoChangeListener listener) {
- mListeners.add(listener);
- }
-
- public void removeChangeListener(DisplayInfoChangeListener listener) {
- mListeners.remove(listener);
- }
-
- private boolean onChange(Message msg) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onDisplayInfoChanged(mInfo, msg.what);
- }
- return true;
- }
-
- public static class Info {
-
- public final int id;
- public final int rotation;
- public final int singleFrameMs;
-
- public final Point realSize;
- public final Point smallestSize;
- public final Point largestSize;
-
- public final DisplayMetrics metrics;
-
- @VisibleForTesting
- public Info(int id, int rotation, int singleFrameMs, Point realSize, Point smallestSize,
- Point largestSize, DisplayMetrics metrics) {
- this.id = id;
- this.rotation = rotation;
- this.singleFrameMs = singleFrameMs;
- this.realSize = realSize;
- this.smallestSize = smallestSize;
- this.largestSize = largestSize;
- this.metrics = metrics;
- }
-
- private Info(Context context) {
- this(context, context.getSystemService(DisplayManager.class)
- .getDisplay(DEFAULT_DISPLAY));
- }
-
- public Info(Context context, Display display) {
- id = display.getDisplayId();
- rotation = display.getRotation();
-
- float refreshRate = display.getRefreshRate();
- singleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
-
- realSize = new Point();
- smallestSize = new Point();
- largestSize = new Point();
- display.getRealSize(realSize);
- display.getCurrentSizeRange(smallestSize, largestSize);
-
- metrics = context.getResources().getDisplayMetrics();
- }
-
- private boolean hasDifferentSize(Info info) {
- if (!realSize.equals(info.realSize)
- && !realSize.equals(info.realSize.y, info.realSize.x)) {
- Log.d(TAG, String.format("Display size changed from %s to %s",
- info.realSize, realSize));
- return true;
- }
-
- if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
- Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
- smallestSize, largestSize, info.smallestSize, info.largestSize));
- return true;
- }
-
- return false;
- }
- }
-
- /**
- * Interface for listening for display changes
- */
- public interface DisplayInfoChangeListener {
-
- void onDisplayInfoChanged(Info info, int flags);
- }
-}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
new file mode 100644
index 0000000..d0e8bb1
--- /dev/null
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.launcher3.Utilities;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to cache properties of default display to avoid a system RPC on every call.
+ */
+public class DisplayController implements DisplayListener {
+
+ private static final String TAG = "DisplayController";
+
+ public static final MainThreadInitializedObject<DisplayController> INSTANCE =
+ new MainThreadInitializedObject<>(DisplayController::new);
+
+ private final SparseArray<DisplayHolder> mOtherDisplays = new SparseArray<>(0);
+ // We store the default display separately, to avoid null checks for primary use case.
+ private final DisplayHolder mDefaultDisplay;
+
+ private final ArrayList<DisplayListChangeListener> mListListeners = new ArrayList<>();
+
+ private DisplayController(Context context) {
+ mDefaultDisplay = DisplayHolder.create(context, DEFAULT_DISPLAY);
+
+ DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
+ }
+
+ @Override
+ public final void onDisplayAdded(int displayId) {
+ DisplayHolder holder = DisplayHolder.create(mDefaultDisplay.mDisplayContext, displayId);
+ if (holder == null) {
+ // Display is already removed by the time we dot this.
+ return;
+ }
+ synchronized (mOtherDisplays) {
+ mOtherDisplays.put(displayId, holder);
+ }
+ MAIN_EXECUTOR.execute(() -> mListListeners.forEach(l-> l.onDisplayAdded(holder)));
+ }
+
+ @Override
+ public final void onDisplayRemoved(int displayId) {
+ synchronized (mOtherDisplays) {
+ mOtherDisplays.remove(displayId);
+ }
+ MAIN_EXECUTOR.execute(() -> mListListeners.forEach(l-> l.onDisplayRemoved(displayId)));
+ }
+
+ /**
+ * Returns the holder corresponding to the given display
+ */
+ public DisplayHolder getHolder(int displayId) {
+ if (displayId == mDefaultDisplay.mId) {
+ return mDefaultDisplay;
+ } else {
+ synchronized (mOtherDisplays) {
+ return mOtherDisplays.get(displayId);
+ }
+ }
+ }
+
+ /**
+ * Adds a listener for display list changes
+ */
+ public void addListChangeListener(DisplayListChangeListener listener) {
+ mListListeners.add(listener);
+ }
+
+ /**
+ * Removes a previously added display list change listener
+ */
+ public void removeListChangeListener(DisplayListChangeListener listener) {
+ mListListeners.remove(listener);
+ }
+
+ @Override
+ public final void onDisplayChanged(int displayId) {
+ DisplayHolder holder = getHolder(displayId);
+ if (holder != null) {
+ holder.handleOnChange();
+ }
+ }
+
+ public static int getSingleFrameMs(Context context) {
+ return getDefaultDisplay(context).getInfo().singleFrameMs;
+ }
+
+ public static DisplayHolder getDefaultDisplay(Context context) {
+ return INSTANCE.get(context).mDefaultDisplay;
+ }
+
+ /**
+ * A listener to receiving addition or removal of new displays
+ */
+ public interface DisplayListChangeListener {
+
+ /**
+ * Called when a new display is added
+ */
+ void onDisplayAdded(DisplayHolder holder);
+
+ /**
+ * Called when a previously added display is removed
+ */
+ void onDisplayRemoved(int displayId);
+ }
+
+ /**
+ * Interface for listening for display changes
+ */
+ public interface DisplayInfoChangeListener {
+
+ void onDisplayInfoChanged(Info info, int flags);
+ }
+
+ public static class DisplayHolder {
+
+ public static final int CHANGE_SIZE = 1 << 0;
+ public static final int CHANGE_ROTATION = 1 << 1;
+ public static final int CHANGE_FRAME_DELAY = 1 << 2;
+
+ public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION | CHANGE_FRAME_DELAY;
+
+ final Context mDisplayContext;
+ final int mId;
+ private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
+ private DisplayController.Info mInfo;
+
+ private DisplayHolder(Context displayContext) {
+ mDisplayContext = displayContext;
+ // Note that the Display object must be obtained from DisplayManager which is
+ // associated to the display context, so the Display is isolated from Activity and
+ // Application to provide the actual state of device that excludes the additional
+ // adjustment and override.
+ mInfo = new DisplayController.Info(mDisplayContext);
+ mId = mInfo.id;
+ }
+
+ public void addChangeListener(DisplayInfoChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeChangeListener(DisplayInfoChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ public DisplayController.Info getInfo() {
+ return mInfo;
+ }
+
+ /** Creates and up-to-date DisplayController.Info for the given context. */
+ @Nullable
+ public Info createInfoForContext(Context context) {
+ Display display = Utilities.ATLEAST_R ? context.getDisplay() : null;
+ if (display == null) {
+ display = context.getSystemService(DisplayManager.class).getDisplay(mId);
+ }
+ if (display == null) {
+ return null;
+ }
+ // Refresh the Context the prevent stale DisplayMetrics.
+ Context displayContext = context.getApplicationContext().createDisplayContext(display);
+ return new Info(displayContext, display);
+ }
+
+ public Context getDisplayContext() {
+ return mDisplayContext;
+ }
+
+ protected void handleOnChange() {
+ Info oldInfo = mInfo;
+ Info newInfo = createInfoForContext(mDisplayContext);
+ if (newInfo == null) {
+ return;
+ }
+
+ int change = 0;
+ if (newInfo.hasDifferentSize(oldInfo)) {
+ change |= CHANGE_SIZE;
+ }
+ if (newInfo.rotation != oldInfo.rotation) {
+ change |= CHANGE_ROTATION;
+ }
+ if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
+ change |= CHANGE_FRAME_DELAY;
+ }
+
+ if (change != 0) {
+ mInfo = newInfo;
+ final int flags = change;
+ MAIN_EXECUTOR.execute(() -> notifyChange(flags));
+ }
+ }
+
+ private void notifyChange(int flags) {
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onDisplayInfoChanged(mInfo, flags);
+ }
+ }
+
+ private static DisplayHolder create(Context context, int id) {
+ DisplayManager dm = context.getSystemService(DisplayManager.class);
+ Display display = dm.getDisplay(id);
+ if (display == null) {
+ return null;
+ }
+ // Use application context to create display context so that it can have its own
+ // Resources.
+ Context displayContext = context.getApplicationContext().createDisplayContext(display);
+ return new DisplayHolder(displayContext);
+ }
+ }
+
+ public static class Info {
+
+ public final int id;
+ public final int rotation;
+ public final int singleFrameMs;
+
+ public final Point realSize;
+ public final Point smallestSize;
+ public final Point largestSize;
+
+ public final DisplayMetrics metrics;
+
+ @VisibleForTesting
+ public Info(int id, int rotation, int singleFrameMs, Point realSize, Point smallestSize,
+ Point largestSize, DisplayMetrics metrics) {
+ this.id = id;
+ this.rotation = rotation;
+ this.singleFrameMs = singleFrameMs;
+ this.realSize = realSize;
+ this.smallestSize = smallestSize;
+ this.largestSize = largestSize;
+ this.metrics = metrics;
+ }
+
+ private Info(Context context) {
+ this(context, context.getSystemService(DisplayManager.class)
+ .getDisplay(DEFAULT_DISPLAY));
+ }
+
+ public Info(Context context, Display display) {
+ id = display.getDisplayId();
+ rotation = display.getRotation();
+
+ float refreshRate = display.getRefreshRate();
+ singleFrameMs = refreshRate > 0 ? (int) (1000 / refreshRate) : 16;
+
+ realSize = new Point();
+ smallestSize = new Point();
+ largestSize = new Point();
+ display.getRealSize(realSize);
+ display.getCurrentSizeRange(smallestSize, largestSize);
+
+ metrics = context.getResources().getDisplayMetrics();
+ }
+
+ private boolean hasDifferentSize(Info info) {
+ if (!realSize.equals(info.realSize)
+ && !realSize.equals(info.realSize.y, info.realSize.x)) {
+ Log.d(TAG, String.format("Display size changed from %s to %s",
+ info.realSize, realSize));
+ return true;
+ }
+
+ if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
+ Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
+ smallestSize, largestSize, info.smallestSize, info.largestSize));
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/Executors.java b/src/com/android/launcher3/util/Executors.java
index 0a32734..a85ae45 100644
--- a/src/com/android/launcher3/util/Executors.java
+++ b/src/com/android/launcher3/util/Executors.java
@@ -20,8 +20,10 @@
import android.os.Process;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Various different executors used in Launcher
@@ -83,4 +85,29 @@
*/
public static final LooperExecutor MODEL_EXECUTOR =
new LooperExecutor(createAndStartNewLooper("launcher-loader"));
+
+ /**
+ * A simple ThreadFactory to set the thread name and priority when used with executors.
+ */
+ public static class SimpleThreadFactory implements ThreadFactory {
+
+ private final int mPriority;
+ private final String mNamePrefix;
+
+ private final AtomicInteger mCount = new AtomicInteger(0);
+
+ public SimpleThreadFactory(String namePrefix, int priority) {
+ mNamePrefix = namePrefix;
+ mPriority = priority;
+ }
+
+ @Override
+ public Thread newThread(Runnable runnable) {
+ Thread t = new Thread(() -> {
+ Process.setThreadPriority(mPriority);
+ runnable.run();
+ }, mNamePrefix + mCount.incrementAndGet());
+ return t;
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
deleted file mode 100644
index 4f4cccd..0000000
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ /dev/null
@@ -1,553 +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.util;
-
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.Arrays;
-
-/**
- * Calculates the next item that a {@link KeyEvent} should change the focus to.
- *<p>
- * Note, this utility class calculates everything regards to icon index and its (x,y) coordinates.
- * Currently supports:
- * <ul>
- * <li> full matrix of cells that are 1x1
- * <li> sparse matrix of cells that are 1x1
- * [ 1][ ][ 2][ ]
- * [ ][ ][ 3][ ]
- * [ ][ 4][ ][ ]
- * [ ][ 5][ 6][ 7]
- * </ul>
- * *<p>
- * For testing, one can use a BT keyboard, or use following adb command.
- * ex. $ adb shell input keyevent 20 // KEYCODE_DPAD_LEFT
- */
-public class FocusLogic {
-
- private static final String TAG = "FocusLogic";
- private static final boolean DEBUG = false;
-
- /** Item and page index related constant used by {@link #handleKeyEvent}. */
- public static final int NOOP = -1;
-
- public static final int PREVIOUS_PAGE_RIGHT_COLUMN = -2;
- public static final int PREVIOUS_PAGE_FIRST_ITEM = -3;
- public static final int PREVIOUS_PAGE_LAST_ITEM = -4;
- public static final int PREVIOUS_PAGE_LEFT_COLUMN = -5;
-
- public static final int CURRENT_PAGE_FIRST_ITEM = -6;
- public static final int CURRENT_PAGE_LAST_ITEM = -7;
-
- public static final int NEXT_PAGE_FIRST_ITEM = -8;
- public static final int NEXT_PAGE_LEFT_COLUMN = -9;
- public static final int NEXT_PAGE_RIGHT_COLUMN = -10;
-
- public static final int ALL_APPS_COLUMN = -11;
-
- // Matrix related constant.
- public static final int EMPTY = -1;
- public static final int PIVOT = 100;
-
- /**
- * Returns true only if this utility class handles the key code.
- */
- public static boolean shouldConsume(int keyCode) {
- return (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ||
- keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN ||
- keyCode == KeyEvent.KEYCODE_MOVE_HOME || keyCode == KeyEvent.KEYCODE_MOVE_END ||
- keyCode == KeyEvent.KEYCODE_PAGE_UP || keyCode == KeyEvent.KEYCODE_PAGE_DOWN);
- }
-
- public static int handleKeyEvent(int keyCode, int [][] map, int iconIdx, int pageIndex,
- int pageCount, boolean isRtl) {
-
- int cntX = map == null ? -1 : map.length;
- int cntY = map == null ? -1 : map[0].length;
-
- if (DEBUG) {
- Log.v(TAG, String.format(
- "handleKeyEvent START: cntX=%d, cntY=%d, iconIdx=%d, pageIdx=%d, pageCnt=%d",
- cntX, cntY, iconIdx, pageIndex, pageCount));
- }
-
- int newIndex = NOOP;
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_LEFT:
- newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/, isRtl);
- if (!isRtl && newIndex == NOOP && pageIndex > 0) {
- newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
- } else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
- newIndex = NEXT_PAGE_RIGHT_COLUMN;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/, isRtl);
- if (!isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
- newIndex = NEXT_PAGE_LEFT_COLUMN;
- } else if (isRtl && newIndex == NOOP && pageIndex > 0) {
- newIndex = PREVIOUS_PAGE_LEFT_COLUMN;
- }
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, 1 /*increment*/);
- break;
- case KeyEvent.KEYCODE_DPAD_UP:
- newIndex = handleDpadVertical(iconIdx, cntX, cntY, map, -1 /*increment*/);
- break;
- case KeyEvent.KEYCODE_MOVE_HOME:
- newIndex = handleMoveHome();
- break;
- case KeyEvent.KEYCODE_MOVE_END:
- newIndex = handleMoveEnd();
- break;
- case KeyEvent.KEYCODE_PAGE_DOWN:
- newIndex = handlePageDown(pageIndex, pageCount);
- break;
- case KeyEvent.KEYCODE_PAGE_UP:
- newIndex = handlePageUp(pageIndex);
- break;
- default:
- break;
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format("handleKeyEvent FINISH: index [%d -> %s]",
- iconIdx, getStringIndex(newIndex)));
- }
- return newIndex;
- }
-
- /**
- * Returns a matrix of size (m x n) that has been initialized with {@link #EMPTY}.
- *
- * @param m number of columns in the matrix
- * @param n number of rows in the matrix
- */
- // TODO: get rid of dynamic matrix creation.
- private static int[][] createFullMatrix(int m, int n) {
- int[][] matrix = new int [m][n];
-
- for (int i=0; i < m;i++) {
- Arrays.fill(matrix[i], EMPTY);
- }
- return matrix;
- }
-
- /**
- * Returns a matrix of size same as the {@link CellLayout} dimension that is initialized with the
- * index of the child view.
- */
- // TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrix(CellLayout layout) {
- ShortcutAndWidgetContainer parent = layout.getShortcutsAndWidgets();
- final int m = layout.getCountX();
- final int n = layout.getCountY();
- final boolean invert = parent.invertLayoutHorizontally();
-
- int[][] matrix = createFullMatrix(m, n);
-
- // Iterate thru the children.
- for (int i = 0; i < parent.getChildCount(); i++ ) {
- View cell = parent.getChildAt(i);
- if (!cell.isFocusable()) {
- continue;
- }
- int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
- int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
- int x = invert ? (m - cx - 1) : cx;
- if (x < m && cy < n) { // check if view fits into matrix, else skip
- matrix[x][cy] = i;
- }
- }
- if (DEBUG) {
- printMatrix(matrix);
- }
- return matrix;
- }
-
- /**
- * Creates a sparse matrix that merges the icon and hotseat view group using the cell layout.
- * The size of the returning matrix is [icon column count x (icon + hotseat row count)]
- * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
- */
- // TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrixWithHotseat(
- CellLayout iconLayout, CellLayout hotseatLayout, DeviceProfile dp) {
-
- ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
- ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
-
- boolean isHotseatHorizontal = !dp.isVerticalBarLayout();
-
- int m, n;
- if (isHotseatHorizontal) {
- m = hotseatLayout.getCountX();
- n = iconLayout.getCountY() + hotseatLayout.getCountY();
- } else {
- m = iconLayout.getCountX() + hotseatLayout.getCountX();
- n = hotseatLayout.getCountY();
- }
- int[][] matrix = createFullMatrix(m, n);
- // Iterate through the children of the workspace.
- for (int i = 0; i < iconParent.getChildCount(); i++) {
- View cell = iconParent.getChildAt(i);
- if (!cell.isFocusable()) {
- continue;
- }
- int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
- int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
- matrix[cx][cy] = i;
- }
-
- // Iterate thru the children of the hotseat.
- for (int i = hotseatParent.getChildCount() - 1; i >= 0; i--) {
- if (isHotseatHorizontal) {
- int cx = ((CellLayout.LayoutParams)
- hotseatParent.getChildAt(i).getLayoutParams()).cellX;
- matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i;
- } else {
- int cy = ((CellLayout.LayoutParams)
- hotseatParent.getChildAt(i).getLayoutParams()).cellY;
- matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i;
- }
- }
- if (DEBUG) {
- printMatrix(matrix);
- }
- return matrix;
- }
-
- /**
- * Creates a sparse matrix that merges the icon of previous/next page and last column of
- * current page. When left key is triggered on the leftmost column, sparse matrix is created
- * that combines previous page matrix and an extra column on the right. Likewise, when right
- * key is triggered on the rightmost column, sparse matrix is created that combines this column
- * on the 0th column and the next page matrix.
- *
- * @param pivotX x coordinate of the focused item in the current page
- * @param pivotY y coordinate of the focused item in the current page
- */
- // TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrixWithPivotColumn(CellLayout iconLayout,
- int pivotX, int pivotY) {
-
- ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
-
- int[][] matrix = createFullMatrix(iconLayout.getCountX() + 1, iconLayout.getCountY());
-
- // Iterate thru the children of the top parent.
- for (int i = 0; i < iconParent.getChildCount(); i++) {
- View cell = iconParent.getChildAt(i);
- if (!cell.isFocusable()) {
- continue;
- }
- int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
- int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
- if (pivotX < 0) {
- matrix[cx - pivotX][cy] = i;
- } else {
- matrix[cx][cy] = i;
- }
- }
-
- if (pivotX < 0) {
- matrix[0][pivotY] = PIVOT;
- } else {
- matrix[pivotX][pivotY] = PIVOT;
- }
- if (DEBUG) {
- printMatrix(matrix);
- }
- return matrix;
- }
-
- //
- // key event handling methods.
- //
-
- /**
- * Calculates icon that has is closest to the horizontal axis in reference to the cur icon.
- *
- * Example of the check order for KEYCODE_DPAD_RIGHT:
- * [ ][ ][13][14][15]
- * [ ][ 6][ 8][10][12]
- * [ X][ 1][ 2][ 3][ 4]
- * [ ][ 5][ 7][ 9][11]
- */
- // TODO: add unit tests to verify all permutation.
- private static int handleDpadHorizontal(int iconIdx, int cntX, int cntY,
- int[][] matrix, int increment, boolean isRtl) {
- if(matrix == null) {
- throw new IllegalStateException("Dpad navigation requires a matrix.");
- }
- int newIconIndex = NOOP;
-
- int xPos = -1;
- int yPos = -1;
- // Figure out the location of the icon.
- for (int i = 0; i < cntX; i++) {
- for (int j = 0; j < cntY; j++) {
- if (matrix[i][j] == iconIdx) {
- xPos = i;
- yPos = j;
- }
- }
- }
- if (DEBUG) {
- Log.v(TAG, String.format("\thandleDpadHorizontal: \t[x, y]=[%d, %d] iconIndex=%d",
- xPos, yPos, iconIdx));
- }
-
- // Rule1: check first in the horizontal direction
- for (int x = xPos + increment; 0 <= x && x < cntX; x += increment) {
- if ((newIconIndex = inspectMatrix(x, yPos, cntX, cntY, matrix)) != NOOP
- && newIconIndex != ALL_APPS_COLUMN) {
- return newIconIndex;
- }
- }
-
- // Rule2: check (x1-n, yPos + increment), (x1-n, yPos - increment)
- // (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment)
- int nextYPos1;
- int nextYPos2;
- boolean haveCrossedAllAppsColumn1 = false;
- boolean haveCrossedAllAppsColumn2 = false;
- int x = -1;
- for (int coeff = 1; coeff < cntY; coeff++) {
- nextYPos1 = yPos + coeff * increment;
- nextYPos2 = yPos - coeff * increment;
- x = xPos + increment * coeff;
- if (inspectMatrix(x, nextYPos1, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
- haveCrossedAllAppsColumn1 = true;
- }
- if (inspectMatrix(x, nextYPos2, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
- haveCrossedAllAppsColumn2 = true;
- }
- for (; 0 <= x && x < cntX; x += increment) {
- int offset1 = haveCrossedAllAppsColumn1 && x < cntX - 1 ? increment : 0;
- newIconIndex = inspectMatrix(x, nextYPos1 + offset1, cntX, cntY, matrix);
- if (newIconIndex != NOOP) {
- return newIconIndex;
- }
- int offset2 = haveCrossedAllAppsColumn2 && x < cntX - 1 ? -increment : 0;
- newIconIndex = inspectMatrix(x, nextYPos2 + offset2, cntX, cntY, matrix);
- if (newIconIndex != NOOP) {
- return newIconIndex;
- }
- }
- }
-
- // Rule3: if switching between pages, do a brute-force search to find an item that was
- // missed by rules 1 and 2 (such as when going from a bottom right icon to top left)
- if (iconIdx == PIVOT) {
- if (isRtl) {
- return increment < 0 ? NEXT_PAGE_FIRST_ITEM : PREVIOUS_PAGE_LAST_ITEM;
- }
- return increment < 0 ? PREVIOUS_PAGE_LAST_ITEM : NEXT_PAGE_FIRST_ITEM;
- }
- return newIconIndex;
- }
-
- /**
- * Calculates icon that is closest to the vertical axis in reference to the current icon.
- *
- * Example of the check order for KEYCODE_DPAD_DOWN:
- * [ ][ ][ ][ X][ ][ ][ ]
- * [ ][ ][ 5][ 1][ 4][ ][ ]
- * [ ][10][ 7][ 2][ 6][ 9][ ]
- * [14][12][ 9][ 3][ 8][11][13]
- */
- // TODO: add unit tests to verify all permutation.
- private static int handleDpadVertical(int iconIndex, int cntX, int cntY,
- int [][] matrix, int increment) {
- int newIconIndex = NOOP;
- if(matrix == null) {
- throw new IllegalStateException("Dpad navigation requires a matrix.");
- }
-
- int xPos = -1;
- int yPos = -1;
- // Figure out the location of the icon.
- for (int i = 0; i< cntX; i++) {
- for (int j = 0; j < cntY; j++) {
- if (matrix[i][j] == iconIndex) {
- xPos = i;
- yPos = j;
- }
- }
- }
-
- if (DEBUG) {
- Log.v(TAG, String.format("\thandleDpadVertical: \t[x, y]=[%d, %d] iconIndex=%d",
- xPos, yPos, iconIndex));
- }
-
- // Rule1: check first in the dpad direction
- for (int y = yPos + increment; 0 <= y && y <cntY && 0 <= y; y += increment) {
- if ((newIconIndex = inspectMatrix(xPos, y, cntX, cntY, matrix)) != NOOP
- && newIconIndex != ALL_APPS_COLUMN) {
- return newIconIndex;
- }
- }
-
- // Rule2: check (xPos + increment, y_(1-n)), (xPos - increment, y_(1-n))
- // (xPos + 2*increment, y_(2-n))), (xPos - 2*increment, y_(2-n))
- int nextXPos1;
- int nextXPos2;
- boolean haveCrossedAllAppsColumn1 = false;
- boolean haveCrossedAllAppsColumn2 = false;
- int y = -1;
- for (int coeff = 1; coeff < cntX; coeff++) {
- nextXPos1 = xPos + coeff * increment;
- nextXPos2 = xPos - coeff * increment;
- y = yPos + increment * coeff;
- if (inspectMatrix(nextXPos1, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
- haveCrossedAllAppsColumn1 = true;
- }
- if (inspectMatrix(nextXPos2, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
- haveCrossedAllAppsColumn2 = true;
- }
- for (; 0 <= y && y < cntY; y = y + increment) {
- int offset1 = haveCrossedAllAppsColumn1 && y < cntY - 1 ? increment : 0;
- newIconIndex = inspectMatrix(nextXPos1 + offset1, y, cntX, cntY, matrix);
- if (newIconIndex != NOOP) {
- return newIconIndex;
- }
- int offset2 = haveCrossedAllAppsColumn2 && y < cntY - 1 ? -increment : 0;
- newIconIndex = inspectMatrix(nextXPos2 + offset2, y, cntX, cntY, matrix);
- if (newIconIndex != NOOP) {
- return newIconIndex;
- }
- }
- }
- return newIconIndex;
- }
-
- private static int handleMoveHome() {
- return CURRENT_PAGE_FIRST_ITEM;
- }
-
- private static int handleMoveEnd() {
- return CURRENT_PAGE_LAST_ITEM;
- }
-
- private static int handlePageDown(int pageIndex, int pageCount) {
- if (pageIndex < pageCount -1) {
- return NEXT_PAGE_FIRST_ITEM;
- }
- return CURRENT_PAGE_LAST_ITEM;
- }
-
- private static int handlePageUp(int pageIndex) {
- if (pageIndex > 0) {
- return PREVIOUS_PAGE_FIRST_ITEM;
- } else {
- return CURRENT_PAGE_FIRST_ITEM;
- }
- }
-
- //
- // Helper methods.
- //
-
- private static boolean isValid(int xPos, int yPos, int countX, int countY) {
- return (0 <= xPos && xPos < countX && 0 <= yPos && yPos < countY);
- }
-
- private static int inspectMatrix(int x, int y, int cntX, int cntY, int[][] matrix) {
- int newIconIndex = NOOP;
- if (isValid(x, y, cntX, cntY)) {
- if (matrix[x][y] != -1) {
- newIconIndex = matrix[x][y];
- if (DEBUG) {
- Log.v(TAG, String.format("\t\tinspect: \t[x, y]=[%d, %d] %d",
- x, y, matrix[x][y]));
- }
- return newIconIndex;
- }
- }
- return newIconIndex;
- }
-
- /**
- * Only used for debugging.
- */
- private static String getStringIndex(int index) {
- switch(index) {
- case NOOP: return "NOOP";
- case PREVIOUS_PAGE_FIRST_ITEM: return "PREVIOUS_PAGE_FIRST";
- case PREVIOUS_PAGE_LAST_ITEM: return "PREVIOUS_PAGE_LAST";
- case PREVIOUS_PAGE_RIGHT_COLUMN:return "PREVIOUS_PAGE_RIGHT_COLUMN";
- case CURRENT_PAGE_FIRST_ITEM: return "CURRENT_PAGE_FIRST";
- case CURRENT_PAGE_LAST_ITEM: return "CURRENT_PAGE_LAST";
- case NEXT_PAGE_FIRST_ITEM: return "NEXT_PAGE_FIRST";
- case NEXT_PAGE_LEFT_COLUMN: return "NEXT_PAGE_LEFT_COLUMN";
- case ALL_APPS_COLUMN: return "ALL_APPS_COLUMN";
- default:
- return Integer.toString(index);
- }
- }
-
- /**
- * Only used for debugging.
- */
- private static void printMatrix(int[][] matrix) {
- Log.v(TAG, "\tprintMap:");
- int m = matrix.length;
- int n = matrix[0].length;
-
- for (int j=0; j < n; j++) {
- String colY = "\t\t";
- for (int i=0; i < m; i++) {
- colY += String.format("%3d",matrix[i][j]);
- }
- Log.v(TAG, colY);
- }
- }
-
- /**
- * @param edgeColumn the column of the new icon. either {@link #NEXT_PAGE_LEFT_COLUMN} or
- * {@link #NEXT_PAGE_RIGHT_COLUMN}
- * @return the view adjacent to {@param oldView} in the {@param nextPage} of the folder.
- */
- public static View getAdjacentChildInNextFolderPage(
- ShortcutAndWidgetContainer nextPage, View oldView, int edgeColumn) {
- final int newRow = ((CellLayout.LayoutParams) oldView.getLayoutParams()).cellY;
-
- int column = (edgeColumn == NEXT_PAGE_LEFT_COLUMN) ^ nextPage.invertLayoutHorizontally()
- ? 0 : (((CellLayout) nextPage.getParent()).getCountX() - 1);
-
- for (; column >= 0; column--) {
- for (int row = newRow; row >= 0; row--) {
- View newView = nextPage.getChildAt(column, row);
- if (newView != null) {
- return newView;
- }
- }
- }
- return null;
- }
-}
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index fcb96d7..1cec0ec 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -16,20 +16,19 @@
package com.android.launcher3.util;
-import android.content.Context;
+import android.os.FileUtils;
import android.util.Log;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.UUID;
/**
* Supports various IO utility functions
@@ -52,6 +51,9 @@
}
public static long copy(InputStream from, OutputStream to) throws IOException {
+ if (Utilities.ATLEAST_Q) {
+ return FileUtils.copy(from, to);
+ }
byte[] buf = new byte[BUF_SIZE];
long total = 0;
int r;
@@ -62,25 +64,6 @@
return total;
}
- /**
- * Utility method to debug binary data
- */
- public static String createTempFile(Context context, byte[] data) {
- if (!FeatureFlags.IS_STUDIO_BUILD) {
- throw new IllegalStateException("Method only allowed in development mode");
- }
-
- String name = UUID.randomUUID().toString();
- File file = new File(context.getCacheDir(), name);
- try (FileOutputStream fo = new FileOutputStream(file)) {
- fo.write(data);
- fo.flush();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return file.getAbsolutePath();
- }
-
public static void closeSilently(Closeable c) {
if (c != null) {
try {
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
index 4d5405d..354609d 100644
--- a/src/com/android/launcher3/util/ItemInfoMatcher.java
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -20,13 +20,11 @@
import android.os.UserHandle;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import java.util.HashSet;
+import java.util.Set;
/**
* A utility class to check for {@link ItemInfo}
@@ -36,34 +34,15 @@
boolean matches(ItemInfo info, ComponentName cn);
/**
- * Filters {@param infos} to those satisfying the {@link #matches(ItemInfo, ComponentName)}.
+ * Returns true if the itemInfo matches this check
*/
- default HashSet<ItemInfo> filterItemInfos(Iterable<ItemInfo> infos) {
- HashSet<ItemInfo> filtered = new HashSet<>();
- for (ItemInfo i : infos) {
- if (i instanceof WorkspaceItemInfo) {
- WorkspaceItemInfo info = (WorkspaceItemInfo) i;
- ComponentName cn = info.getTargetComponent();
- if (cn != null && matches(info, cn)) {
- filtered.add(info);
- }
- } else if (i instanceof FolderInfo) {
- FolderInfo info = (FolderInfo) i;
- for (WorkspaceItemInfo s : info.contents) {
- ComponentName cn = s.getTargetComponent();
- if (cn != null && matches(s, cn)) {
- filtered.add(s);
- }
- }
- } else if (i instanceof LauncherAppWidgetInfo) {
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) i;
- ComponentName cn = info.providerName;
- if (cn != null && matches(info, cn)) {
- filtered.add(info);
- }
- }
+ default boolean matchesInfo(ItemInfo info) {
+ if (info != null) {
+ ComponentName cn = info.getTargetComponent();
+ return cn != null && matches(info, cn);
+ } else {
+ return false;
}
- return filtered;
}
/**
@@ -81,11 +60,10 @@
}
/**
- * Returns a new matcher which returns the opposite boolean value of the provided
- * {@param matcher}.
+ * Returns a new matcher with returns the opposite value of this.
*/
- static ItemInfoMatcher not(ItemInfoMatcher matcher) {
- return (info, cn) -> !matcher.matches(info, cn);
+ default ItemInfoMatcher negate() {
+ return (info, cn) -> !matches(info, cn);
}
static ItemInfoMatcher ofUser(UserHandle user) {
@@ -96,16 +74,19 @@
return (info, cn) -> components.contains(cn) && info.user.equals(user);
}
- static ItemInfoMatcher ofPackages(HashSet<String> packageNames, UserHandle user) {
+ static ItemInfoMatcher ofPackages(Set<String> packageNames, UserHandle user) {
return (info, cn) -> packageNames.contains(cn.getPackageName()) && info.user.equals(user);
}
- static ItemInfoMatcher ofShortcutKeys(HashSet<ShortcutKey> keys) {
+ static ItemInfoMatcher ofShortcutKeys(Set<ShortcutKey> keys) {
return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
keys.contains(ShortcutKey.fromItemInfo(info));
}
- static ItemInfoMatcher ofItemIds(IntSparseArrayMap<Boolean> ids, Boolean matchDefault) {
- return (info, cn) -> ids.get(info.id, matchDefault);
+ /**
+ * Returns a matcher for items with provided ids
+ */
+ static ItemInfoMatcher ofItemIds(IntSet ids) {
+ return (info, cn) -> ids.contains(info.id);
}
}
diff --git a/src/com/android/launcher3/util/MultiHashMap.java b/src/com/android/launcher3/util/MultiHashMap.java
deleted file mode 100644
index b7275c1..0000000
--- a/src/com/android/launcher3/util/MultiHashMap.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-
-/**
- * A utility map from keys to an ArrayList of values.
- */
-public class MultiHashMap<K, V> extends HashMap<K, ArrayList<V>> {
-
- public MultiHashMap() { }
-
- public MultiHashMap(int size) {
- super(size);
- }
-
- public void addToList(K key, V value) {
- ArrayList<V> list = get(key);
- if (list == null) {
- list = new ArrayList<>();
- list.add(value);
- put(key, list);
- } else {
- list.add(value);
- }
- }
-
- @Override
- public MultiHashMap<K, V> clone() {
- MultiHashMap<K, V> map = new MultiHashMap<>(size());
- for (Entry<K, ArrayList<V>> entry : entrySet()) {
- map.put(entry.getKey(), new ArrayList<V>(entry.getValue()));
- }
- return map;
- }
-}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index d4e074c..2e279fa 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -33,19 +33,18 @@
public class OnboardingPrefs<T extends Launcher> {
public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
- public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
- public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
- public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";
+ public static final String SEARCH_EDU_SEEN = "launcher.search_edu";
/**
* Events that either have happened or have not (booleans).
*/
@StringDef(value = {
HOME_BOUNCE_SEEN,
- SHELF_BOUNCE_SEEN
+ HOTSEAT_LONGPRESS_TIP_SEEN,
+ SEARCH_EDU_SEEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {}
@@ -55,8 +54,6 @@
*/
@StringDef(value = {
HOME_BOUNCE_COUNT,
- SHELF_BOUNCE_COUNT,
- ALL_APPS_COUNT,
HOTSEAT_DISCOVERY_TIP_COUNT
})
@Retention(RetentionPolicy.SOURCE)
@@ -64,10 +61,8 @@
private static final Map<String, Integer> MAX_COUNTS;
static {
- Map<String, Integer> maxCounts = new ArrayMap<>(3);
+ Map<String, Integer> maxCounts = new ArrayMap<>(4);
maxCounts.put(HOME_BOUNCE_COUNT, 3);
- maxCounts.put(SHELF_BOUNCE_COUNT, 3);
- maxCounts.put(ALL_APPS_COUNT, 5);
maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 86f3431..08ec591 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -37,7 +37,6 @@
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
-import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
@@ -49,8 +48,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.AppInfo;
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.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import java.net.URISyntaxException;
@@ -93,44 +92,28 @@
}
/**
+ * Returns whether the target app is installed for a given user
+ */
+ public boolean isAppInstalled(String packageName, UserHandle user) {
+ 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) {
- if (Utilities.ATLEAST_OREO) {
- try {
- ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
- return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
- ? null : info;
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- } else {
- final boolean isPrimaryUser = Process.myUserHandle().equals(user);
- if (!isPrimaryUser && (flags == 0)) {
- // We are looking for an installed app on a secondary profile. Prior to O, the only
- // entry point for work profiles is through the LauncherActivity.
- List<LauncherActivityInfo> activityList =
- mLauncherApps.getActivityList(packageName, user);
- return activityList.size() > 0 ? activityList.get(0).getApplicationInfo() : null;
- }
- try {
- ApplicationInfo info = mPm.getApplicationInfo(packageName, flags);
- // There is no way to check if the app is installed for managed profile. But for
- // primary profile, we can still have this check.
- if (isPrimaryUser && ((info.flags & ApplicationInfo.FLAG_INSTALLED) == 0)
- || !info.enabled) {
- return null;
- }
- return info;
- } catch (PackageManager.NameNotFoundException e) {
- // Package not found
- return null;
- }
+ try {
+ ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
+ return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
+ ? null : info;
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
}
}
public boolean isSafeMode() {
- return mContext.getPackageManager().isSafeMode();
+ return mPm.isSafeMode();
}
public Intent getAppLaunchIntent(String pkg, UserHandle user) {
@@ -226,9 +209,12 @@
* Starts the details activity for {@code info}
*/
public void startDetailsActivityForInfo(ItemInfo info, Rect sourceBounds, Bundle opts) {
- if (info instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
- mContext.startActivity(promiseAppInfo.getMarketIntent(mContext));
+ if (info instanceof ItemInfoWithIcon
+ && (((ItemInfoWithIcon) info).runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+ ItemInfoWithIcon appInfo = (ItemInfoWithIcon) info;
+ mContext.startActivity(new PackageManagerHelper(mContext)
+ .getMarketIntent(appInfo.getTargetComponent().getPackageName()));
return;
}
ComponentName componentName = null;
@@ -345,4 +331,12 @@
}
return false;
}
+
+ /** Returns the incremental download progress for the given shortcut's app. */
+ public static int getLoadingProgress(LauncherActivityInfo info) {
+ if (Utilities.ATLEAST_S) {
+ return (int) (100 * info.getLoadingProgress());
+ }
+ return 100;
+ }
}
diff --git a/src/com/android/launcher3/util/PersistedItemArray.java b/src/com/android/launcher3/util/PersistedItemArray.java
new file mode 100644
index 0000000..7ff2abb
--- /dev/null
+++ b/src/com/android/launcher3/util/PersistedItemArray.java
@@ -0,0 +1,184 @@
+/*
+ * 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.launcher3.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Xml;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.WorkerThread;
+
+import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.pm.UserCache;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.LongFunction;
+
+/**
+ * Utility class to read/write a list of {@link com.android.launcher3.model.data.ItemInfo} on disk.
+ * This class is not thread safe, the caller should ensure proper threading
+ */
+public class PersistedItemArray<T extends ItemInfo> {
+
+ private static final String TAG = "PersistedItemArray";
+
+ private static final String TAG_ROOT = "items";
+ private static final String TAG_ENTRY = "entry";
+
+ private final String mFileName;
+
+ public PersistedItemArray(String fileName) {
+ mFileName = fileName + ".xml";
+ }
+
+ /**
+ * Writes the provided list of items on the disk
+ */
+ @WorkerThread
+ public void write(Context context, List<T> items) {
+ AtomicFile file = getFile(context);
+ FileOutputStream fos;
+ try {
+ fos = file.startWrite();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to persist items in " + mFileName, e);
+ return;
+ }
+
+ UserCache userCache = UserCache.INSTANCE.get(context);
+
+ try {
+ XmlSerializer out = Xml.newSerializer();
+ out.setOutput(fos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+ out.startTag(null, TAG_ROOT);
+ for (T item : items) {
+ Intent intent = item.getIntent();
+ if (intent == null) {
+ continue;
+ }
+
+ out.startTag(null, TAG_ENTRY);
+ out.attribute(null, Favorites.ITEM_TYPE, Integer.toString(item.itemType));
+ out.attribute(null, Favorites.PROFILE_ID,
+ Long.toString(userCache.getSerialNumberForUser(item.user)));
+ out.attribute(null, Favorites.INTENT, intent.toUri(0));
+ out.endTag(null, TAG_ENTRY);
+ }
+ out.endTag(null, TAG_ROOT);
+ out.endDocument();
+ } catch (IOException e) {
+ file.failWrite(fos);
+ Log.e(TAG, "Unable to persist items in " + mFileName, e);
+ return;
+ }
+
+ file.finishWrite(fos);
+ }
+
+ /**
+ * Reads the items from the disk
+ */
+ @WorkerThread
+ public List<T> read(Context context, ItemFactory<T> factory) {
+ return read(context, factory, UserCache.INSTANCE.get(context)::getUserForSerialNumber);
+ }
+
+ /**
+ * Reads the items from the disk
+ * @param userFn method to provide user handle for a given user serial
+ */
+ @WorkerThread
+ public List<T> read(Context context, ItemFactory<T> factory, LongFunction<UserHandle> userFn) {
+ List<T> result = new ArrayList<>();
+ try (FileInputStream fis = getFile(context).openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new InputStreamReader(fis, StandardCharsets.UTF_8));
+
+ AutoInstallsLayout.beginDocument(parser, TAG_ROOT);
+ final int depth = parser.getDepth();
+
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG
+ || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG || !TAG_ENTRY.equals(parser.getName())) {
+ continue;
+ }
+ try {
+ int itemType = Integer.parseInt(
+ parser.getAttributeValue(null, Favorites.ITEM_TYPE));
+ UserHandle user = userFn.apply(Long.parseLong(
+ parser.getAttributeValue(null, Favorites.PROFILE_ID)));
+ Intent intent = Intent.parseUri(
+ parser.getAttributeValue(null, Favorites.INTENT), 0);
+
+ if (user != null && intent != null) {
+ T item = factory.createInfo(itemType, user, intent);
+ if (item != null) {
+ result.add(item);
+ }
+ }
+ } catch (Exception e) {
+ // Ignore this entry
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // Ignore
+ } catch (IOException | XmlPullParserException e) {
+ Log.e(TAG, "Unable to read items in " + mFileName, e);
+ return Collections.emptyList();
+ }
+ return result;
+ }
+
+ /**
+ * Returns the underlying file used for persisting data
+ */
+ public AtomicFile getFile(Context context) {
+ return new AtomicFile(context.getFileStreamPath(mFileName));
+ }
+
+ /**
+ * Interface to create an ItemInfo during parsing
+ */
+ public interface ItemFactory<T extends ItemInfo> {
+
+ /**
+ * Returns an item info or null in which case the entry is ignored
+ */
+ @Nullable
+ T createInfo(int itemType, UserHandle user, Intent intent);
+ }
+}
diff --git a/src/com/android/launcher3/util/SecureSettingsObserver.java b/src/com/android/launcher3/util/SecureSettingsObserver.java
deleted file mode 100644
index 48aa02b..0000000
--- a/src/com/android/launcher3/util/SecureSettingsObserver.java
+++ /dev/null
@@ -1,82 +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 android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.provider.Settings;
-
-/**
- * Utility class to listen for secure settings changes
- */
-public class SecureSettingsObserver extends ContentObserver {
-
- /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
- public static final String NOTIFICATION_BADGING = "notification_badging";
-
- private final ContentResolver mResolver;
- private final String mKeySetting;
- private final int mDefaultValue;
- private final OnChangeListener mOnChangeListener;
-
- public SecureSettingsObserver(ContentResolver resolver, OnChangeListener listener,
- String keySetting, int defaultValue) {
- super(new Handler());
-
- mResolver = resolver;
- mOnChangeListener = listener;
- mKeySetting = keySetting;
- mDefaultValue = defaultValue;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- mOnChangeListener.onSettingsChanged(getValue());
- }
-
- public boolean getValue() {
- return Settings.Secure.getInt(mResolver, mKeySetting, mDefaultValue) == 1;
- }
-
- public void register() {
- mResolver.registerContentObserver(Settings.Secure.getUriFor(mKeySetting), false, this);
- }
-
- public ContentResolver getResolver() {
- return mResolver;
- }
-
- public void dispatchOnChange() {
- onChange(true);
- }
-
- public void unregister() {
- mResolver.unregisterContentObserver(this);
- }
-
- public interface OnChangeListener {
- void onSettingsChanged(boolean isEnabled);
- }
-
- public static SecureSettingsObserver newNotificationSettingsObserver(Context context,
- OnChangeListener listener) {
- return new SecureSettingsObserver(
- context.getContentResolver(), listener, NOTIFICATION_BADGING, 1);
- }
-}
diff --git a/src/com/android/launcher3/util/SettingsCache.java b/src/com/android/launcher3/util/SettingsCache.java
new file mode 100644
index 0000000..22b4d38
--- /dev/null
+++ b/src/com/android/launcher3/util/SettingsCache.java
@@ -0,0 +1,179 @@
+/*
+ * 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.util;
+
+import static android.provider.Settings.System.ACCELEROMETER_ROTATION;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * ContentObserver over Settings keys that also has a caching layer.
+ * Consumers can register for callbacks via {@link #register(Uri, OnChangeListener)} and
+ * {@link #unregister(Uri, OnChangeListener)} methods.
+ *
+ * This can be used as a normal cache without any listeners as well via the
+ * {@link #getValue(Uri, int)} and {@link #dispatchOnChange(Uri)} to update (and subsequently call
+ * get)
+ *
+ * The cache will be invalidated/updated through the normal
+ * {@link ContentObserver#onChange(boolean)} calls
+ * or can be force updated by calling {@link #dispatchOnChange(Uri)}.
+ *
+ * Cache will also be updated if a key queried is missing (even if it has no listeners registered).
+ */
+public class SettingsCache extends ContentObserver {
+
+ /** Hidden field Settings.Secure.NOTIFICATION_BADGING */
+ public static final Uri NOTIFICATION_BADGING_URI =
+ Settings.Secure.getUriFor("notification_badging");
+ /** Hidden field Settings.Secure.ONE_HANDED_MODE_ENABLED */
+ public static final String ONE_HANDED_ENABLED = "one_handed_mode_enabled";
+ /** Hidden field Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED */
+ public static final String ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED =
+ "swipe_bottom_to_notification_enabled";
+ /** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
+ public static final Uri NOTIFICATION_ENABLED_LISTENERS =
+ Settings.Secure.getUriFor("enabled_notification_listeners");
+ public static final Uri ROTATION_SETTING_URI =
+ Settings.System.getUriFor(ACCELEROMETER_ROTATION);
+
+ private static final String SYSTEM_URI_PREFIX = Settings.System.CONTENT_URI.toString();
+
+ /**
+ * Caches the last seen value for registered keys.
+ */
+ private Map<Uri, Boolean> mKeyCache = new ConcurrentHashMap<>();
+ private final Map<Uri, CopyOnWriteArrayList<OnChangeListener>> mListenerMap = new HashMap<>();
+ protected final ContentResolver mResolver;
+
+
+ /**
+ * Singleton instance
+ */
+ public static MainThreadInitializedObject<SettingsCache> INSTANCE =
+ new MainThreadInitializedObject<>(SettingsCache::new);
+
+ private SettingsCache(Context context) {
+ super(new Handler());
+ mResolver = context.getContentResolver();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ // We use default of 1, but if we're getting an onChange call, can assume a non-default
+ // value will exist
+ boolean newVal = updateValue(uri, 1 /* Effectively Unused */);
+ if (!mListenerMap.containsKey(uri)) {
+ return;
+ }
+
+ for (OnChangeListener listener : mListenerMap.get(uri)) {
+ listener.onSettingsChanged(newVal);
+ }
+ }
+
+ /**
+ * Returns the value for this classes key from the cache. If not in cache, will call
+ * {@link #updateValue(Uri, int)} to fetch.
+ */
+ public boolean getValue(Uri keySetting, int defaultValue) {
+ if (mKeyCache.containsKey(keySetting)) {
+ return mKeyCache.get(keySetting);
+ } else {
+ return updateValue(keySetting, defaultValue);
+ }
+ }
+
+ /**
+ * Does not de-dupe if you add same listeners for the same key multiple times.
+ * Unregister once complete using {@link #unregister(Uri, OnChangeListener)}
+ */
+ public void register(Uri uri, OnChangeListener changeListener) {
+ if (mListenerMap.containsKey(uri)) {
+ mListenerMap.get(uri).add(changeListener);
+ } else {
+ CopyOnWriteArrayList<OnChangeListener> l = new CopyOnWriteArrayList<>();
+ l.add(changeListener);
+ mListenerMap.put(uri, l);
+ mResolver.registerContentObserver(uri, false, this);
+ }
+ }
+
+ private boolean updateValue(Uri keyUri, int defaultValue) {
+ String key = keyUri.getLastPathSegment();
+ boolean newVal;
+ if (keyUri.toString().startsWith(SYSTEM_URI_PREFIX)) {
+ newVal = Settings.System.getInt(mResolver, key, defaultValue) == 1;
+ } else { // SETTING_SECURE
+ newVal = Settings.Secure.getInt(mResolver, key, defaultValue) == 1;
+ }
+
+ mKeyCache.put(keyUri, newVal);
+ return newVal;
+ }
+
+ /**
+ * Force update a change for a given URI and have all listeners for that URI receive callbacks
+ * even if the value is unchanged.
+ */
+ public void dispatchOnChange(Uri uri) {
+ onChange(true, uri);
+ }
+
+ /**
+ * Call to stop receiving updates on the given {@param listener}.
+ * This Uri/Listener pair must correspond to the same pair called with for
+ * {@link #register(Uri, OnChangeListener)}
+ */
+ public void unregister(Uri uri, OnChangeListener listener) {
+ List<OnChangeListener> listenersToRemoveFrom = mListenerMap.get(uri);
+ if (!listenersToRemoveFrom.contains(listener)) {
+ return;
+ }
+
+ listenersToRemoveFrom.remove(listener);
+ if (listenersToRemoveFrom.isEmpty()) {
+ mListenerMap.remove(uri);
+ }
+ }
+
+ /**
+ * Don't use this. Ever.
+ * @param keyCache Cache to replace {@link #mKeyCache}
+ */
+ @VisibleForTesting
+ void setKeyCache(Map<Uri, Boolean> keyCache) {
+ mKeyCache = keyCache;
+ }
+
+ public interface OnChangeListener {
+ void onSettingsChanged(boolean isEnabled);
+ }
+}
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 275c024..50166c3 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -19,8 +19,6 @@
import android.view.View;
import android.view.Window;
-import com.android.launcher3.Utilities;
-
import java.util.Arrays;
/**
@@ -33,6 +31,7 @@
public static final int UI_STATE_SCRIM_VIEW = 1;
public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
public static final int UI_STATE_OVERVIEW = 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;
@@ -40,7 +39,7 @@
public static final int FLAG_DARK_STATUS = 1 << 3;
private final Window mWindow;
- private final int[] mStates = new int[4];
+ private final int[] mStates = new int[5];
public SystemUiController(Window window) {
mWindow = window;
@@ -77,12 +76,10 @@
}
private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
- if (Utilities.ATLEAST_OREO) {
- if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
- currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
- } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
- currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
- }
+ if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
+ currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
+ currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index b74686f..512a286 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -81,6 +81,16 @@
return getAttrColor(context, android.R.attr.colorAccent);
}
+ /** Returns the floating background color attribute. */
+ public static int getColorBackground(Context context) {
+ return getAttrColor(context, android.R.attr.colorBackground);
+ }
+
+ /** Returns the floating background color attribute. */
+ public static int getColorBackgroundFloating(Context context) {
+ return getAttrColor(context, android.R.attr.colorBackgroundFloating);
+ }
+
public static int getAttrColor(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int colorAccent = ta.getColor(0, 0);
diff --git a/proto_overrides/launcher_log_extension.proto b/src/com/android/launcher3/util/VelocityUtils.java
similarity index 61%
copy from proto_overrides/launcher_log_extension.proto
copy to src/com/android/launcher3/util/VelocityUtils.java
index 2995aa2..d5962ed 100644
--- a/proto_overrides/launcher_log_extension.proto
+++ b/src/com/android/launcher3/util/VelocityUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
+package com.android.launcher3.util;
-option java_package = "com.android.launcher3.userevent";
-option java_outer_classname = "LauncherLogExtensions";
+/**
+ * Contains some constants and functions to standardize velocity usage.
+ */
+public class VelocityUtils {
-package userevent;
+ /**
+ * Unit to pass to {@link android.view.VelocityTracker#computeCurrentVelocity(int)}.
+ */
+ public static final int PX_PER_MS = 1;
-//
-// Use this to add any app specific extensions to the proto.
-//
-message LauncherEventExtension {
-}
-
-message TargetExtension {
}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
index 2ad80cf..b8554e4 100644
--- a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -145,14 +145,17 @@
msg.sendToTarget();
}
- private void updateOffset() {
- int numPagesForWallpaperParallax;
+ /** Returns the number of pages used for the wallpaper parallax. */
+ public int getNumPagesForWallpaperParallax() {
if (mWallpaperIsLiveWallpaper) {
- numPagesForWallpaperParallax = mNumScreens;
+ return mNumScreens;
} else {
- numPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
+ return Math.max(MIN_PARALLAX_PAGE_SPAN, mNumScreens);
}
- Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, numPagesForWallpaperParallax, 0,
+ }
+
+ private void updateOffset() {
+ Message.obtain(mHandler, MSG_SET_NUM_PARALLAX, getNumPagesForWallpaperParallax(), 0,
mWindowToken).sendToTarget();
}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 11c1029..e08f881 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -64,7 +64,7 @@
protected final ObjectAnimator mOpenCloseAnimator;
protected View mContent;
- private final View mColorScrim;
+ protected final View mColorScrim;
protected Interpolator mScrollInterpolator;
// range [0, 1], 0=> completely open, 1=> completely closed
@@ -216,7 +216,6 @@
return mLauncher.getDragLayer();
}
-
protected static View createColorScrim(Context context, int bgColor) {
View view = new View(context);
view.forceHasOverlappingRendering(false);
diff --git a/src/com/android/launcher3/views/AccessibilityActionsView.java b/src/com/android/launcher3/views/AccessibilityActionsView.java
new file mode 100644
index 0000000..0eacaa3
--- /dev/null
+++ b/src/com/android/launcher3/views/AccessibilityActionsView.java
@@ -0,0 +1,97 @@
+/*
+ * 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.launcher3.views;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.views.OptionsPopupView.OptionItem;
+
+/**
+ * Placeholder view to expose additional Launcher actions via accessibility actions
+ */
+public class AccessibilityActionsView extends View implements StateListener<LauncherState> {
+
+ public AccessibilityActionsView(Context context) {
+ this(context, null);
+ }
+
+ public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AccessibilityActionsView(Context context, @Nullable AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ Launcher.getLauncher(context).getStateManager().addStateListener(this);
+ setWillNotDraw(true);
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ setImportantForAccessibility(finalState == NORMAL
+ ? IMPORTANT_FOR_ACCESSIBILITY_YES : IMPORTANT_FOR_ACCESSIBILITY_NO);
+ }
+
+ @Override
+ public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+ AccessibilityNodeInfo info = super.createAccessibilityNodeInfo();
+ Launcher l = Launcher.getLauncher(getContext());
+ info.addAction(new AccessibilityAction(
+ R.string.all_apps_button_label, l.getText(R.string.all_apps_button_label)));
+ for (OptionItem item : OptionsPopupView.getOptions(l)) {
+ info.addAction(new AccessibilityAction(item.labelRes, l.getText(item.labelRes)));
+ }
+ return info;
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (super.performAccessibilityAction(action, arguments)) {
+ return true;
+ }
+ Launcher l = Launcher.getLauncher(getContext());
+ if (action == R.string.all_apps_button_label) {
+ l.getStateManager().goToState(ALL_APPS);
+ return true;
+ }
+ for (OptionItem item : OptionsPopupView.getOptions(l)) {
+ if (item.labelRes == action) {
+ if (item.eventId.getId() > 0) {
+ l.getStatsLogManager().logger().log(item.eventId);
+ }
+ if (item.clickListener.onLongClick(this)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index ae459e1..505c6ce 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -17,7 +17,7 @@
import android.content.Context;
import android.content.ContextWrapper;
-import android.view.ContextThemeWrapper;
+import android.graphics.Rect;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile;
@@ -49,6 +49,23 @@
return null;
}
+ default Rect getFolderBoundingBox() {
+ return getDeviceProfile().getAbsoluteOpenFolderBounds();
+ }
+
+ /**
+ * After calling {@link #getFolderBoundingBox()}, we calculate a (left, top) position for a
+ * Folder of size width x height to be within those bounds. However, the chosen position may
+ * not be visually ideal (e.g. uncanny valley of centeredness), so here's a chance to update it.
+ * @param inOutPosition A 2-size array where the first element is the left position of the open
+ * folder and the second element is the top position. Should be updated in place if desired.
+ * @param bounds The bounds that the open folder should fit inside.
+ * @param width The width of the open folder.
+ * @param height The height of the open folder.
+ */
+ default void updateOpenFolderPosition(int[] inOutPosition, Rect bounds, int width, int height) {
+ }
+
/**
* The root view to support drag-and-drop and popup support.
*/
@@ -56,10 +73,10 @@
DeviceProfile getDeviceProfile();
- static ActivityContext lookupContext(Context context) {
+ static <T extends ActivityContext> T lookupContext(Context context) {
if (context instanceof ActivityContext) {
- return (ActivityContext) context;
- } else if (context instanceof ContextThemeWrapper) {
+ return (T) context;
+ } else if (context instanceof ContextWrapper) {
return lookupContext(((ContextWrapper) context).getBaseContext());
} else {
throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
diff --git a/src/com/android/launcher3/views/ArrowTipView.java b/src/com/android/launcher3/views/ArrowTipView.java
index b4a6b14..1f12a2f 100644
--- a/src/com/android/launcher3/views/ArrowTipView.java
+++ b/src/com/android/launcher3/views/ArrowTipView.java
@@ -87,10 +87,6 @@
}
@Override
- public void logActionCommand(int command) {
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_ON_BOARD_POPUP) != 0;
}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 357eeb8..1939d15 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -20,7 +20,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_UP;
-import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
import android.annotation.TargetApi;
import android.app.WallpaperInfo;
@@ -33,7 +33,6 @@
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Property;
import android.view.MotionEvent;
import android.view.View;
@@ -49,7 +48,6 @@
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -191,12 +189,6 @@
}
private TouchController findControllerToHandleTouch(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "findControllerToHandleTouch ev=" + ev
- + ", isEventInLauncher=" + isEventInLauncher(ev)
- + ", topOpenView=" + AbstractFloatingView.getTopOpenView(mActivity));
- }
-
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null
&& (isEventInLauncher(ev) || topView.canInterceptEventsInSystemGestureRegion())
@@ -214,15 +206,19 @@
protected boolean findActiveController(MotionEvent ev) {
mActiveController = null;
- if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
- | TOUCH_DISPATCHING_FROM_PROXY)) == 0) {
- // Only look for controllers if we are not dispatching from gesture area and proxy is
- // not active
+ if (canFindActiveController()) {
mActiveController = findControllerToHandleTouch(ev);
}
return mActiveController != null;
}
+ protected boolean canFindActiveController() {
+ // Only look for controllers if we are not dispatching from gesture area and proxy is
+ // not active
+ return (mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
+ | TOUCH_DISPATCHING_FROM_PROXY)) == 0;
+ }
+
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder
@@ -463,12 +459,6 @@
}
@Override
- public boolean dispatchUnhandledMove(View focused, int direction) {
- // Consume the unhandled move if a container is open, to avoid switching pages underneath.
- return AbstractFloatingView.getTopOpenView(mActivity) != null;
- }
-
- @Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) {
diff --git a/src/com/android/launcher3/views/ClipIconView.java b/src/com/android/launcher3/views/ClipIconView.java
index fab0bd4..4e82336 100644
--- a/src/com/android/launcher3/views/ClipIconView.java
+++ b/src/com/android/launcher3/views/ClipIconView.java
@@ -161,6 +161,11 @@
float scaleY = rect.height() / minSize;
float scale = Math.max(1f, Math.min(scaleX, scaleY));
+ if (Float.isNaN(scale)) {
+ // Views are no longer laid out, do not update.
+ return;
+ }
+
update(rect, progress, shapeProgressStart, cornerRadius, isOpening, scale,
minSize, lp, isVerticalBarLayout, dp);
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 8186dfa..23c3722 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.views;
-import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM;
@@ -23,9 +22,6 @@
import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
@@ -55,11 +51,12 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.DeepShortcutView;
-import com.android.launcher3.testing.TestProtocol;
/**
* A view that is created to look like another view with the purpose of creating fluid animations.
@@ -74,7 +71,6 @@
private static @Nullable IconLoadResult sIconLoadResult;
public static final float SHAPE_PROGRESS_DURATION = 0.10f;
- private static final int FADE_DURATION_MS = 200;
private static final RectF sTmpRectF = new RectF();
private static final Object[] sTmpObjArray = new Object[1];
@@ -89,6 +85,9 @@
private IconLoadResult mIconLoadResult;
+ // Draw the drawable of the BubbleTextView behind ClipIconView to reveal the built in shadow.
+ private View mBtvDrawable;
+
private ClipIconView mClipIconView;
private @Nullable Drawable mBadge;
@@ -98,7 +97,6 @@
private final Rect mFinalDrawableBounds = new Rect();
- private AnimatorSet mFadeAnimatorSet;
private ListenerView mListenerView;
private Runnable mFastFinishRunnable;
@@ -116,6 +114,8 @@
mIsRtl = Utilities.isRtl(getResources());
mListenerView = new ListenerView(context, attrs);
mClipIconView = new ClipIconView(context, attrs);
+ mBtvDrawable = new ImageView(context, attrs);
+ addView(mBtvDrawable);
addView(mClipIconView);
setWillNotDraw(false);
}
@@ -176,6 +176,7 @@
setLayoutParams(lp);
mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+ mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
}
private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
@@ -253,13 +254,26 @@
@SuppressWarnings("WrongThread")
private static void getIconResult(Launcher l, View originalView, ItemInfo info, RectF pos,
IconLoadResult iconLoadResult) {
- Drawable drawable = null;
+ Drawable drawable;
+ Drawable btvIcon;
Drawable badge = null;
boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
- && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& !info.isDisabled(); // Use original icon for disabled icons.
- Drawable btvIcon = originalView instanceof BubbleTextView
- ? ((BubbleTextView) originalView).getIcon() : null;
+
+ if (originalView instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) originalView;
+
+ if (info instanceof ItemInfoWithIcon
+ && (((ItemInfoWithIcon) info).runtimeStatusFlags
+ & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK) != 0) {
+ btvIcon = btv.makePreloadIcon();
+ } else {
+ btvIcon = btv.getIcon();
+ }
+ } else {
+ btvIcon = null;
+ }
+
if (info instanceof SystemShortcut) {
if (originalView instanceof ImageView) {
drawable = ((ImageView) originalView).getDrawable();
@@ -268,6 +282,9 @@
} else {
drawable = originalView.getBackground();
}
+ } else if (btvIcon instanceof PreloadIconDrawable) {
+ // Force the progress bar to display.
+ drawable = btvIcon;
} else {
int width = (int) pos.width();
int height = (int) pos.height();
@@ -293,6 +310,8 @@
drawable = drawable == null ? null : drawable.getConstantState().newDrawable();
int iconOffset = getOffsetForIconBounds(l, drawable, pos);
synchronized (iconLoadResult) {
+ iconLoadResult.btvDrawable = btvIcon == null || drawable == btvIcon
+ ? null : btvIcon.getConstantState().newDrawable();
iconLoadResult.drawable = drawable;
iconLoadResult.badge = badge;
iconLoadResult.iconOffset = iconOffset;
@@ -312,7 +331,8 @@
* @param iconOffset The amount of offset needed to match this view with the original view.
*/
@UiThread
- private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge, int iconOffset) {
+ private void setIcon(@Nullable Drawable drawable, @Nullable Drawable badge,
+ @Nullable Drawable btvIcon, int iconOffset) {
final InsettableFrameLayout.LayoutParams lp =
(InsettableFrameLayout.LayoutParams) getLayoutParams();
mBadge = badge;
@@ -343,6 +363,10 @@
mBadge.setBounds(0, 0, clipViewOgWidth, clipViewOgHeight);
}
}
+
+ if (!mIsOpening && btvIcon != null) {
+ mBtvDrawable.setBackground(btvIcon);
+ }
invalidate();
}
@@ -361,7 +385,7 @@
synchronized (mIconLoadResult) {
if (mIconLoadResult.isIconLoaded) {
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
- mIconLoadResult.iconOffset);
+ mIconLoadResult.btvDrawable, mIconLoadResult.iconOffset);
setIconAndDotVisible(originalView, false);
} else {
mIconLoadResult.onIconLoaded = () -> {
@@ -370,7 +394,7 @@
}
setIcon(mIconLoadResult.drawable, mIconLoadResult.badge,
- mIconLoadResult.iconOffset);
+ mIconLoadResult.btvDrawable, mIconLoadResult.iconOffset);
setVisibility(VISIBLE);
setIconAndDotVisible(originalView, false);
@@ -383,8 +407,7 @@
@WorkerThread
@SuppressWarnings("WrongThread")
private static int getOffsetForIconBounds(Launcher l, Drawable drawable, RectF position) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O
- || !(drawable instanceof AdaptiveIconDrawable)
+ if (!(drawable instanceof AdaptiveIconDrawable)
|| (drawable instanceof FolderAdaptiveIcon)) {
return 0;
}
@@ -436,10 +459,6 @@
mEndRunnable.run();
mEndRunnable = null;
}
- if (mFadeAnimatorSet != null) {
- mFadeAnimatorSet.end();
- mFadeAnimatorSet = null;
- }
}
@Override
@@ -533,11 +552,6 @@
view.setVisibility(INVISIBLE);
parent.addView(view);
dragLayer.addView(view.mListenerView);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "getFloatingIconView. listenerView "
- + "added to dragLayer. listenerView=" + view.mListenerView + ", fiv=" + view,
- new Exception());
- }
view.mListenerView.setListener(view::fastFinish);
view.mEndRunnable = () -> {
@@ -548,8 +562,16 @@
setIconAndDotVisible(originalView, true);
view.finish(dragLayer);
} else {
- view.mFadeAnimatorSet = view.createFadeAnimation(originalView, dragLayer);
- view.mFadeAnimatorSet.start();
+ originalView.setVisibility(VISIBLE);
+ if (originalView instanceof IconLabelDotView) {
+ setIconAndDotVisible(originalView, true);
+ }
+ if (originalView instanceof BubbleTextView) {
+ BubbleTextView btv = (BubbleTextView) originalView;
+ btv.setIconVisible(true);
+ btv.setForceHideDot(true);
+ }
+ view.finish(dragLayer);
}
} else {
view.finish(dragLayer);
@@ -566,54 +588,9 @@
return view;
}
- private AnimatorSet createFadeAnimation(View originalView, DragLayer dragLayer) {
- AnimatorSet fade = new AnimatorSet();
- fade.setDuration(FADE_DURATION_MS);
- fade.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- originalView.setVisibility(VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- finish(dragLayer);
- }
- });
-
- if (originalView instanceof IconLabelDotView) {
- fade.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- setIconAndDotVisible(originalView, true);
- }
- });
- }
-
- if (originalView instanceof BubbleTextView) {
- BubbleTextView btv = (BubbleTextView) originalView;
- fade.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- btv.setIconVisible(true);
- btv.setForceHideDot(true);
- }
- });
- fade.play(ObjectAnimator.ofInt(btv.getIcon(), DRAWABLE_ALPHA, 0, 255));
- } else if (!(originalView instanceof FolderIcon)) {
- fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
- }
-
- return fade;
- }
-
private void finish(DragLayer dragLayer) {
((ViewGroup) dragLayer.getParent()).removeView(this);
dragLayer.removeView(mListenerView);
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "listenerView removed from dragLayer. "
- + "listenerView=" + mListenerView + ", fiv=" + this, new Exception());
- }
recycle();
mLauncher.getViewCache().recycleView(R.layout.floating_icon_view, this);
}
@@ -630,11 +607,7 @@
mLoadIconSignal = null;
mEndRunnable = null;
mFinalDrawableBounds.setEmpty();
- if (mFadeAnimatorSet != null) {
- mFadeAnimatorSet.cancel();
- }
mPositionOut = null;
- mFadeAnimatorSet = null;
mListenerView.setListener(null);
mOriginalIcon = null;
mOnTargetChangeRunnable = null;
@@ -642,11 +615,13 @@
sTmpObjArray[0] = null;
mIconLoadResult = null;
mClipIconView.recycle();
+ mBtvDrawable.setBackground(null);
mFastFinishRunnable = null;
}
private static class IconLoadResult {
final ItemInfo itemInfo;
+ Drawable btvDrawable;
Drawable drawable;
Drawable badge;
int iconOffset;
diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java
index 040619e..011f6de 100644
--- a/src/com/android/launcher3/views/FloatingSurfaceView.java
+++ b/src/com/android/launcher3/views/FloatingSurfaceView.java
@@ -40,7 +40,7 @@
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.util.DefaultDisplay;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
/**
@@ -97,7 +97,7 @@
// Remove after some time, to avoid flickering
Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
- DefaultDisplay.INSTANCE.get(mLauncher).getInfo().singleFrameMs);
+ DisplayController.getDefaultDisplay(mLauncher).getInfo().singleFrameMs);
}
private void removeViewFromParent() {
@@ -122,9 +122,6 @@
}
@Override
- public void logActionCommand(int command) { }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_ICON_SURFACE) != 0;
}
diff --git a/src/com/android/launcher3/views/ListenerView.java b/src/com/android/launcher3/views/ListenerView.java
index 3ef778b..b2df0ee 100644
--- a/src/com/android/launcher3/views/ListenerView.java
+++ b/src/com/android/launcher3/views/ListenerView.java
@@ -17,13 +17,11 @@
import android.content.Context;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.testing.TestProtocol;
/**
* An invisible AbstractFloatingView that can run a callback when it is being closed.
@@ -38,20 +36,12 @@
}
public void setListener(Runnable listener) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView setListener lv=" + this
- + ", listener=" + listener, new Exception());
- }
mCloseListener = listener;
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onAttachedToWindow lv=" + this,
- new Exception());
- }
mIsOpen = true;
}
@@ -59,19 +49,10 @@
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIsOpen = false;
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView onDetachedFromView lv=" + this,
- new Exception());
- }
}
@Override
protected void handleClose(boolean animate) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView handeClose lv=" + this
- + ", mIsOpen=" + mIsOpen + ", mCloseListener=" + mCloseListener
- + ", getParent()=" + getParent(), new Exception());
- }
if (mIsOpen) {
if (mCloseListener != null) {
mCloseListener.run();
@@ -85,21 +66,12 @@
}
@Override
- public void logActionCommand(int command) {
- // Users do not interact with FloatingIconView, so there is nothing to log here.
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_LISTENER) != 0;
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "ListenerView touchEvent lv=" + this
- + ", ev=" + ev, new Exception());
- }
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
handleClose(false);
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index d558781..899dcf7 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -16,6 +16,7 @@
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;
@@ -48,7 +49,7 @@
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
import java.util.ArrayList;
import java.util.List;
@@ -85,10 +86,10 @@
if (item == null) {
return false;
}
- if (item.mEventId.getId() > 0) {
- mLauncher.getStatsLogManager().logger().log(item.mEventId);
+ if (item.eventId.getId() > 0) {
+ mLauncher.getStatsLogManager().logger().log(item.eventId);
}
- if (item.mClickListener.onLongClick(view)) {
+ if (item.clickListener.onLongClick(view)) {
close(true);
return true;
}
@@ -108,11 +109,6 @@
}
@Override
- public void logActionCommand(int command) {
- // TODO:
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_OPTIONS_POPUP) != 0;
}
@@ -122,7 +118,8 @@
mTargetRect.roundOut(outPos);
}
- public static void show(Launcher launcher, RectF targetRect, List<OptionItem> items) {
+ public static OptionsPopupView show(
+ Launcher launcher, RectF targetRect, List<OptionItem> items) {
OptionsPopupView popup = (OptionsPopupView) launcher.getLayoutInflater()
.inflate(R.layout.longpress_options_menu, launcher.getDragLayer(), false);
popup.mTargetRect = targetRect;
@@ -130,14 +127,15 @@
for (OptionItem item : items) {
DeepShortcutView view =
(DeepShortcutView) popup.inflateAndAdd(R.layout.system_shortcut, popup);
- view.getIconView().setBackgroundResource(item.mIconRes);
- view.getBubbleText().setText(item.mLabelRes);
+ view.getIconView().setBackgroundResource(item.iconRes);
+ view.getBubbleText().setText(item.labelRes);
view.setDividerVisibility(View.INVISIBLE);
view.setOnClickListener(popup);
view.setOnLongClickListener(popup);
popup.mItemMap.put(view, item);
}
- popup.reorderAndShow(popup.getChildCount());
+ popup.show();
+ return popup;
}
@VisibleForTesting
@@ -152,8 +150,22 @@
y = launcher.getDragLayer().getHeight() / 2;
}
RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
+ show(launcher, target, getOptions(launcher));
+ }
+ /**
+ * Returns the list of supported actions
+ */
+ public static ArrayList<OptionItem> getOptions(Launcher launcher) {
ArrayList<OptionItem> options = new ArrayList<>();
+ options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
+ LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
+ OptionsPopupView::startSettings));
+ if (!WidgetsModel.GO_DISABLE_WIDGETS) {
+ options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
+ LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,
+ OptionsPopupView::onWidgetsClicked));
+ }
int resString = Utilities.existsStyleWallpapers(launcher) ?
R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
int resDrawable = Utilities.existsStyleWallpapers(launcher) ?
@@ -161,19 +173,10 @@
options.add(new OptionItem(resString, resDrawable,
IGNORE,
OptionsPopupView::startWallpaperPicker));
- if (!WidgetsModel.GO_DISABLE_WIDGETS) {
- options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
- LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS,
- OptionsPopupView::onWidgetsClicked));
- }
- options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
- LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
- OptionsPopupView::startSettings));
-
- show(launcher, target, options);
+ return options;
}
- public static boolean onWidgetsClicked(View view) {
+ private static boolean onWidgetsClicked(View view) {
return openWidgets(Launcher.getLauncher(view.getContext())) != null;
}
@@ -188,7 +191,7 @@
}
}
- public static boolean startSettings(View view) {
+ private static boolean startSettings(View view) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "start: startSettings");
Launcher launcher = Launcher.getLauncher(view.getContext());
launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
@@ -201,7 +204,7 @@
* Event handler for the wallpaper picker button that appears after a long press
* on the home screen.
*/
- public static boolean startWallpaperPicker(View v) {
+ private static boolean startWallpaperPicker(View v) {
Launcher launcher = Launcher.getLauncher(v.getContext());
if (!Utilities.isWallpaperAllowed(launcher)) {
Toast.makeText(launcher, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
@@ -210,7 +213,8 @@
Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(EXTRA_WALLPAPER_OFFSET,
- launcher.getWorkspace().getWallpaperOffsetForCenterPage());
+ launcher.getWorkspace().getWallpaperOffsetForCenterPage())
+ .putExtra(EXTRA_WALLPAPER_LAUNCH_SOURCE, "app_launched_launcher");
if (!Utilities.existsStyleWallpapers(launcher)) {
intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
} else {
@@ -220,30 +224,30 @@
if (!TextUtils.isEmpty(pickerPackage)) {
intent.setPackage(pickerPackage);
}
- return launcher.startActivitySafely(v, intent, dummyInfo(intent), null);
+ return launcher.startActivitySafely(v, intent, placeholderInfo(intent));
}
- static WorkspaceItemInfo dummyInfo(Intent intent) {
- WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
- dummyInfo.intent = intent;
- dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
- dummyInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
- return dummyInfo;
+ static WorkspaceItemInfo placeholderInfo(Intent intent) {
+ WorkspaceItemInfo placeholderInfo = new WorkspaceItemInfo();
+ placeholderInfo.intent = intent;
+ placeholderInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+ placeholderInfo.container = LauncherSettings.Favorites.CONTAINER_SETTINGS;
+ return placeholderInfo;
}
public static class OptionItem {
- private final int mLabelRes;
- private final int mIconRes;
- private final EventEnum mEventId;
- private final OnLongClickListener mClickListener;
+ public final int labelRes;
+ public final int iconRes;
+ public final EventEnum eventId;
+ public final OnLongClickListener clickListener;
public OptionItem(int labelRes, int iconRes, EventEnum eventId,
OnLongClickListener clickListener) {
- mLabelRes = labelRes;
- mIconRes = iconRes;
- mEventId = eventId;
- mClickListener = clickListener;
+ this.labelRes = labelRes;
+ this.iconRes = iconRes;
+ this.eventId = eventId;
+ this.clickListener = clickListener;
}
}
}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 6a83332..ae34257 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -16,6 +16,8 @@
package com.android.launcher3.views;
+import static android.view.HapticFeedbackConstants.CLOCK_TICK;
+
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -32,6 +34,7 @@
import android.view.ViewConfiguration;
import android.widget.TextView;
+import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BaseRecyclerView;
@@ -97,6 +100,7 @@
private boolean mIsThumbDetached;
private final boolean mCanThumbDetach;
private boolean mIgnoreDragGesture;
+ private boolean mIsRecyclerViewFirstChildInParent = true;
// This is the offset from the top of the scrollbar when the user first starts touching. To
// prevent jumping, this offset is applied as the user scrolls.
@@ -110,6 +114,7 @@
protected BaseRecyclerView mRv;
private RecyclerView.OnScrollListener mOnScrollListener;
+ @Nullable private OnFastScrollChangeListener mOnFastScrollChangeListener;
private int mDownX;
private int mDownY;
@@ -186,6 +191,9 @@
updatePopupY(y);
mThumbOffsetY = y;
invalidate();
+ if (mOnFastScrollChangeListener != null) {
+ mOnFastScrollChangeListener.onThumbOffsetYChanged(mThumbOffsetY);
+ }
}
public int getThumbOffsetY() {
@@ -288,6 +296,7 @@
if (!sectionName.equals(mPopupSectionName)) {
mPopupSectionName = sectionName;
mPopupView.setText(sectionName);
+ performHapticFeedback(CLOCK_TICK);
}
animatePopupVisibility(!sectionName.isEmpty());
mLastTouchY = boundedY;
@@ -388,7 +397,9 @@
return false;
}
getHitRect(sTempRect);
- sTempRect.top += mRv.getScrollBarTop();
+ if (mIsRecyclerViewFirstChildInParent) {
+ sTempRect.top += mRv.getScrollBarTop();
+ }
if (outOffset != null) {
outOffset.set(sTempRect.left, sTempRect.top);
}
@@ -401,4 +412,23 @@
// alpha is so low, it does not matter.
return false;
}
+
+ public void setIsRecyclerViewFirstChildInParent(boolean isRecyclerViewFirstChildInParent) {
+ mIsRecyclerViewFirstChildInParent = isRecyclerViewFirstChildInParent;
+ }
+
+ public void setOnFastScrollChangeListener(
+ @Nullable OnFastScrollChangeListener onFastScrollChangeListener) {
+ mOnFastScrollChangeListener = onFastScrollChangeListener;
+ }
+
+ /**
+ * A callback that is invoked when there is a scroll change in {@link RecyclerViewFastScroller}.
+ */
+ public interface OnFastScrollChangeListener {
+ /**
+ * Called when the thumb offset vertical position, in pixels, has changed to {@code y}.
+ */
+ void onThumbOffsetYChanged(int y);
+ }
}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 22faf97..c9bd284 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -15,119 +15,39 @@
*/
package com.android.launcher3.views;
-import static android.content.Context.ACCESSIBILITY_SERVICE;
-import static android.view.MotionEvent.ACTION_DOWN;
-
import static androidx.core.graphics.ColorUtils.compositeColors;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.Keyframe;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.animation.RectEvaluator;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.util.AttributeSet;
-import android.util.IntProperty;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
-import androidx.core.view.ViewCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
-import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.statemanager.StateManager;
-import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
import com.android.launcher3.uioverrides.WallpaperColorInfo.OnChangeListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.util.MultiValueAlpha;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.widget.WidgetsFullSheet;
-
-import java.util.List;
/**
* Simple scrim which draws a flat color
*/
-public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener,
- AccessibilityStateChangeListener {
+public class ScrimView<T extends Launcher> extends View implements Insettable, OnChangeListener {
- public static final IntProperty<ScrimView> DRAG_HANDLE_ALPHA =
- new IntProperty<ScrimView>("dragHandleAlpha") {
-
- @Override
- public Integer get(ScrimView scrimView) {
- return scrimView.mDragHandleAlpha;
- }
-
- @Override
- public void setValue(ScrimView scrimView, int value) {
- scrimView.setDragHandleAlpha(value);
- }
- };
- private static final int WALLPAPERS = R.string.wallpaper_button_text;
- private static final int WIDGETS = R.string.widget_button_text;
- private static final int SETTINGS = R.string.settings_button_text;
- private static final int ALPHA_CHANNEL_COUNT = 1;
-
- private static final long DRAG_HANDLE_BOUNCE_DURATION_MS = 300;
- // How much to delay before repeating the bounce.
- private static final long DRAG_HANDLE_BOUNCE_DELAY_MS = 200;
- // Repeat this many times (i.e. total number of bounces is 1 + this).
- private static final int DRAG_HANDLE_BOUNCE_REPEAT_COUNT = 2;
-
- private final Rect mTempRect = new Rect();
- private final int[] mTempPos = new int[2];
-
+ private static final float SCRIM_ALPHA = .95f;
protected final T mLauncher;
private final WallpaperColorInfo mWallpaperColorInfo;
- private final AccessibilityManager mAM;
protected final int mEndScrim;
protected final boolean mIsScrimDark;
- private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
- new StateListener<LauncherState>() {
- @Override
- public void onStateTransitionComplete(LauncherState finalState) {
- setImportantForAccessibility(finalState == ALL_APPS
- ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
- : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- }
- };
-
protected float mMaxScrimAlpha;
protected float mProgress = 1;
@@ -137,83 +57,37 @@
protected int mEndFlatColor;
protected int mEndFlatColorAlpha;
- protected final Point mDragHandleSize;
- private final int mDragHandleTouchSize;
- private final int mDragHandlePaddingInVerticalBarLayout;
- protected float mDragHandleOffset;
- private final Rect mDragHandleBounds;
- private final RectF mHitRect = new RectF();
- private ObjectAnimator mDragHandleAnim;
-
- private final MultiValueAlpha mMultiValueAlpha;
-
- private final AccessibilityHelper mAccessibilityHelper;
- @Nullable
- protected Drawable mDragHandle;
-
- private int mDragHandleAlpha = 255;
-
public ScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context);
- mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ int endScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ endScrim = Themes.getColorBackgroundFloating(context);
+ endScrim = ColorUtils.setAlphaComponent(endScrim, (int) (255 * SCRIM_ALPHA));
+ }
+ mEndScrim = endScrim;
mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
mMaxScrimAlpha = 0.7f;
-
- Resources res = context.getResources();
- mDragHandleSize = new Point(res.getDimensionPixelSize(R.dimen.vertical_drag_handle_width),
- res.getDimensionPixelSize(R.dimen.vertical_drag_handle_height));
- mDragHandleBounds = new Rect(0, 0, mDragHandleSize.x, mDragHandleSize.y);
- mDragHandleTouchSize = res.getDimensionPixelSize(R.dimen.vertical_drag_handle_touch_size);
- mDragHandlePaddingInVerticalBarLayout = context.getResources()
- .getDimensionPixelSize(R.dimen.vertical_drag_handle_padding_in_vertical_bar_layout);
-
- mAccessibilityHelper = createAccessibilityHelper();
- ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
-
- mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
setFocusable(false);
- mMultiValueAlpha = new MultiValueAlpha(this, ALPHA_CHANNEL_COUNT);
- }
-
- public AlphaProperty getAlphaProperty(int index) {
- return mMultiValueAlpha.getProperty(index);
- }
-
- @NonNull
- protected AccessibilityHelper createAccessibilityHelper() {
- return new AccessibilityHelper();
}
@Override
- public void setInsets(Rect insets) {
- updateDragHandleBounds();
- updateDragHandleVisibility();
- }
+ public void setInsets(Rect insets) { }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- updateDragHandleBounds();
- }
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mWallpaperColorInfo.addOnChangeListener(this);
onExtractedColorsChanged(mWallpaperColorInfo);
-
- mAM.addAccessibilityStateChangeListener(this);
- onAccessibilityStateChanged(mAM.isEnabled());
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mWallpaperColorInfo.removeOnChangeListener(this);
- mAM.removeAccessibilityStateChangeListener(this);
}
@Override
@@ -234,10 +108,8 @@
public void setProgress(float progress) {
if (mProgress != progress) {
mProgress = progress;
- stopDragHandleEducationAnim();
updateColors();
updateSysUiColors();
- updateDragHandleAlpha();
invalidate();
}
}
@@ -260,286 +132,10 @@
}
}
- protected void updateDragHandleAlpha() {
- if (mDragHandle != null) {
- mDragHandle.setAlpha(mDragHandleAlpha);
- }
- }
-
- private void setDragHandleAlpha(int alpha) {
- if (alpha != mDragHandleAlpha) {
- mDragHandleAlpha = alpha;
- if (mDragHandle != null) {
- mDragHandle.setAlpha(mDragHandleAlpha);
- invalidate();
- }
- }
- }
-
@Override
protected void onDraw(Canvas canvas) {
if (mCurrentFlatColor != 0) {
canvas.drawColor(mCurrentFlatColor);
}
- drawDragHandle(canvas);
- }
-
- protected void drawDragHandle(Canvas canvas) {
- if (mDragHandle != null) {
- canvas.translate(0, -mDragHandleOffset);
- mDragHandle.draw(canvas);
- canvas.translate(0, mDragHandleOffset);
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- boolean superHandledTouch = super.onTouchEvent(event);
- if (event.getAction() == ACTION_DOWN) {
- if (!superHandledTouch && mHitRect.contains(event.getX(), event.getY())) {
- if (startDragHandleEducationAnim()) {
- return true;
- }
- }
- stopDragHandleEducationAnim();
- }
- return superHandledTouch;
- }
-
- /**
- * Animates the drag handle to demonstrate how to get to all apps.
- * @return Whether the animation was started (false if drag handle is invisible).
- */
- public boolean startDragHandleEducationAnim() {
- stopDragHandleEducationAnim();
-
- if (mDragHandle == null || mDragHandle.getAlpha() != 255) {
- return false;
- }
-
- final Drawable drawable = mDragHandle;
- mDragHandle = null;
-
- Rect bounds = new Rect(mDragHandleBounds);
- bounds.offset(0, -(int) mDragHandleOffset);
- drawable.setBounds(bounds);
-
- Rect topBounds = new Rect(bounds);
- topBounds.offset(0, -bounds.height());
-
- Rect invalidateRegion = new Rect(bounds);
- invalidateRegion.top = topBounds.top;
-
- final float progressToReachTop = 0.6f;
- Keyframe frameTop = Keyframe.ofObject(progressToReachTop, topBounds);
- frameTop.setInterpolator(DEACCEL);
- Keyframe frameBot = Keyframe.ofObject(1, bounds);
- frameBot.setInterpolator(ACCEL_DEACCEL);
- PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("bounds",
- Keyframe.ofObject(0, bounds), frameTop, frameBot);
- holder.setEvaluator(new RectEvaluator());
-
- mDragHandleAnim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
- long totalBounceDuration = DRAG_HANDLE_BOUNCE_DURATION_MS + DRAG_HANDLE_BOUNCE_DELAY_MS;
- // The bounce finishes by this progress, the rest of the duration just delays next bounce.
- float delayStartProgress = 1f - (float) DRAG_HANDLE_BOUNCE_DELAY_MS / totalBounceDuration;
- mDragHandleAnim.addUpdateListener((v) -> invalidate(invalidateRegion));
- mDragHandleAnim.setDuration(totalBounceDuration);
- mDragHandleAnim.setInterpolator(clampToProgress(LINEAR, 0, delayStartProgress));
- mDragHandleAnim.setRepeatCount(DRAG_HANDLE_BOUNCE_REPEAT_COUNT);
- getOverlay().add(drawable);
-
- mDragHandleAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mDragHandleAnim = null;
- getOverlay().remove(drawable);
- updateDragHandleVisibility(drawable);
- }
- });
- mDragHandleAnim.start();
- return true;
- }
-
- private void stopDragHandleEducationAnim() {
- if (mDragHandleAnim != null) {
- mDragHandleAnim.end();
- }
- }
-
- protected void updateDragHandleBounds() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- final int left;
- final int width = getMeasuredWidth();
- final int top = getMeasuredHeight() - mDragHandleSize.y - grid.getInsets().bottom;
- final int topMargin;
-
- if (grid.isVerticalBarLayout()) {
- topMargin = grid.workspacePadding.bottom + mDragHandlePaddingInVerticalBarLayout;
- if (grid.isSeascape()) {
- left = width - grid.getInsets().right - mDragHandleSize.x
- - mDragHandlePaddingInVerticalBarLayout;
- } else {
- left = grid.getInsets().left + mDragHandlePaddingInVerticalBarLayout;
- }
- } else {
- left = Math.round((width - mDragHandleSize.x) / 2f);
- topMargin = grid.hotseatBarSizePx;
- }
- mDragHandleBounds.offsetTo(left, top - topMargin);
- mHitRect.set(mDragHandleBounds);
- // Inset outwards to increase touch size.
- mHitRect.inset((mDragHandleSize.x - mDragHandleTouchSize) / 2f,
- (mDragHandleSize.y - mDragHandleTouchSize) / 2f);
-
- if (mDragHandle != null) {
- mDragHandle.setBounds(mDragHandleBounds);
- }
- }
-
- @Override
- public void onAccessibilityStateChanged(boolean enabled) {
- StateManager<LauncherState> stateManager = mLauncher.getStateManager();
- stateManager.removeStateListener(mAccessibilityLauncherStateListener);
-
- if (enabled) {
- stateManager.addStateListener(mAccessibilityLauncherStateListener);
- mAccessibilityLauncherStateListener.onStateTransitionComplete(stateManager.getState());
- } else {
- setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- }
- updateDragHandleVisibility();
- }
-
- public void updateDragHandleVisibility() {
- updateDragHandleVisibility(null);
- }
-
- private void updateDragHandleVisibility(@Nullable Drawable recycle) {
- boolean visible = shouldDragHandleBeVisible();
- boolean wasVisible = mDragHandle != null;
- if (visible != wasVisible) {
- if (visible) {
- mDragHandle = recycle != null ? recycle :
- mLauncher.getDrawable(R.drawable.drag_handle_indicator_shadow);
- mDragHandle.setBounds(mDragHandleBounds);
-
- updateDragHandleAlpha();
- } else {
- mDragHandle = null;
- }
- invalidate();
- }
- }
-
- protected boolean shouldDragHandleBeVisible() {
- return mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
- }
-
- @Override
- public boolean dispatchHoverEvent(MotionEvent event) {
- return mAccessibilityHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- return mAccessibilityHelper.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
- }
-
- @Override
- public void onFocusChanged(boolean gainFocus, int direction,
- Rect previouslyFocusedRect) {
- super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- mAccessibilityHelper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
- }
-
- protected class AccessibilityHelper extends ExploreByTouchHelper {
-
- private static final int DRAG_HANDLE_ID = 1;
-
- public AccessibilityHelper() {
- super(ScrimView.this);
- }
-
- @Override
- protected int getVirtualViewAt(float x, float y) {
- return mHitRect.contains((int) x, (int) y)
- ? DRAG_HANDLE_ID : INVALID_ID;
- }
-
- @Override
- protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
- virtualViewIds.add(DRAG_HANDLE_ID);
- }
-
- @Override
- protected void onPopulateNodeForVirtualView(int virtualViewId,
- AccessibilityNodeInfoCompat node) {
- node.setContentDescription(getContext().getString(R.string.all_apps_button_label));
- node.setBoundsInParent(mDragHandleBounds);
-
- getLocationOnScreen(mTempPos);
- mTempRect.set(mDragHandleBounds);
- mTempRect.offset(mTempPos[0], mTempPos[1]);
- node.setBoundsInScreen(mTempRect);
-
- node.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
- node.setClickable(true);
- node.setFocusable(true);
-
- if (mLauncher.isInState(NORMAL)) {
- Context context = getContext();
- if (Utilities.isWallpaperAllowed(context)) {
- node.addAction(
- new AccessibilityActionCompat(WALLPAPERS, context.getText(WALLPAPERS)));
- }
- node.addAction(new AccessibilityActionCompat(WIDGETS, context.getText(WIDGETS)));
- node.addAction(new AccessibilityActionCompat(SETTINGS, context.getText(SETTINGS)));
- }
- }
-
- @Override
- protected boolean onPerformActionForVirtualView(
- int virtualViewId, int action, Bundle arguments) {
- if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
- mLauncher.getUserEventDispatcher().logActionOnControl(
- Action.Touch.TAP, ControlType.ALL_APPS_BUTTON,
- mLauncher.getStateManager().getState().containerType);
- mLauncher.getStateManager().goToState(ALL_APPS);
- return true;
- } else if (action == WALLPAPERS) {
- return OptionsPopupView.startWallpaperPicker(ScrimView.this);
- } else if (action == WIDGETS) {
- int originalImportanceForAccessibility = getImportantForAccessibility();
- setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- WidgetsFullSheet widgetsFullSheet = OptionsPopupView.openWidgets(mLauncher);
- if (widgetsFullSheet == null) {
- setImportantForAccessibility(originalImportanceForAccessibility);
- return false;
- }
- widgetsFullSheet.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {}
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- setImportantForAccessibility(originalImportanceForAccessibility);
- widgetsFullSheet.removeOnAttachStateChangeListener(this);
- }
- });
- return true;
- } else if (action == SETTINGS) {
- return OptionsPopupView.startSettings(ScrimView.this);
- }
-
- return false;
- }
- }
-
- /**
- * @return The top of this scrim view, or {@link Float#MAX_VALUE} if there's no distinct top.
- */
- public float getVisualTop() {
- return Float.MAX_VALUE;
}
}
diff --git a/src/com/android/launcher3/views/Snackbar.java b/src/com/android/launcher3/views/Snackbar.java
index 513ce59..49fcd2e 100644
--- a/src/com/android/launcher3/views/Snackbar.java
+++ b/src/com/android/launcher3/views/Snackbar.java
@@ -167,11 +167,6 @@
}
@Override
- public void logActionCommand(int command) {
- // TODO
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_SNACKBAR) != 0;
}
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index d35a38f..c8cf627 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -38,7 +38,6 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.statemanager.StateManager.StateListener;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
/**
* On boarding flow for users right after setting up work profile
@@ -61,7 +60,6 @@
private View mViewWrapper;
private Button mProceedButton;
private TextView mContentText;
- private AllAppsPagedView mAllAppsPagedView;
private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
@@ -89,16 +87,6 @@
}
@Override
- public void logActionCommand(int command) {
- // Since this is on-boarding popup, it is not a user controlled action.
- }
-
- @Override
- public int getLogContainerType() {
- return LauncherLogProto.ContainerType.TIP;
- }
-
- @Override
protected boolean isOfType(int type) {
return (type & TYPE_ON_BOARD_POPUP) != 0;
}
@@ -112,13 +100,10 @@
// make sure layout does not shrink when we change the text
mContentText.post(() -> mContentText.setMinLines(mContentText.getLineCount()));
- if (mLauncher.getAppsView().getContentView() instanceof AllAppsPagedView) {
- mAllAppsPagedView = (AllAppsPagedView) mLauncher.getAppsView().getContentView();
- }
mProceedButton.setOnClickListener(view -> {
- if (mAllAppsPagedView != null) {
- mAllAppsPagedView.snapToPage(AllAppsContainerView.AdapterHolder.WORK);
+ if (getAllAppsPagedView() != null) {
+ getAllAppsPagedView().snapToPage(AllAppsContainerView.AdapterHolder.WORK);
}
goToWorkTab(true);
});
@@ -166,8 +151,8 @@
}
private void goToFirstPage() {
- if (mAllAppsPagedView != null) {
- mAllAppsPagedView.snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
+ if (getAllAppsPagedView() != null) {
+ getAllAppsPagedView().snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
}
}
@@ -182,6 +167,11 @@
mOpenCloseAnimator.start();
}
+ private AllAppsPagedView getAllAppsPagedView() {
+ View v = mLauncher.getAppsView().getContentView();
+ return (v instanceof AllAppsPagedView) ? (AllAppsPagedView) v : null;
+ }
+
/**
* Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
*/
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 3e5113a..03c58bb 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -16,7 +16,6 @@
package com.android.launcher3.widget;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import android.content.Context;
import android.graphics.Point;
@@ -31,24 +30,19 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.uioverrides.WallpaperColorInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.AbstractSlideInView;
-import java.util.ArrayList;
-
/**
* Base class for various widgets popup
*/
-abstract class BaseWidgetSheet extends AbstractSlideInView
+public abstract class BaseWidgetSheet extends AbstractSlideInView
implements OnClickListener, OnLongClickListener, DragSource,
PopupDataProvider.PopupDataChangeListener {
@@ -80,16 +74,7 @@
@Override
public final void onClick(View v) {
- // Let the user know that they have to long press to add a widget
- if (mWidgetInstructionToast != null) {
- mWidgetInstructionToast.cancel();
- }
-
- CharSequence msg = Utilities.wrapForTts(
- getContext().getText(R.string.long_press_widget_to_add),
- getContext().getString(R.string.long_accessible_way_to_add));
- mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
- mWidgetInstructionToast.show();
+ mWidgetInstructionToast = showWidgetToast(getContext(), mWidgetInstructionToast);
}
@Override
@@ -100,6 +85,8 @@
if (v instanceof WidgetCell) {
return beginDraggingWidget((WidgetCell) v);
+ } else if (v.getParent() instanceof WidgetCell) {
+ return beginDraggingWidget((WidgetCell) v.getParent());
}
return true;
}
@@ -114,12 +101,14 @@
return false;
}
+ PendingItemDragHelper dragHelper = new PendingItemDragHelper(v);
+ dragHelper.setPreview(v.getPreview());
+
int[] loc = new int[2];
getPopupContainer().getLocationInDragLayer(image, loc);
- new PendingItemDragHelper(v).startDrag(
- image.getBitmapBounds(), image.getBitmap().getWidth(), image.getWidth(),
- new Point(loc[0], loc[1]), this, new DragOptions());
+ dragHelper.startDrag(image.getBitmapBounds(), image.getBitmap().getWidth(),
+ image.getWidth(), new Point(loc[0], loc[1]), this, new DragOptions());
close(true);
return true;
}
@@ -149,29 +138,24 @@
isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
}
- @Override
- public void fillInLogContainerData(ItemInfo childInfo, Target child,
- ArrayList<Target> parents) {
- Target target = newContainerTarget(ContainerType.WIDGETS);
- target.cardinality = getElementsRowCount();
- parents.add(target);
- }
-
- @Override
- public final void logActionCommand(int command) {
- Target target = newContainerTarget(getLogContainerType());
- target.cardinality = getElementsRowCount();
- mLauncher.getUserEventDispatcher().logActionCommand(command, target);
- }
-
- @Override
- public int getLogContainerType() {
- return ContainerType.WIDGETS;
- }
-
- protected abstract int getElementsRowCount();
-
protected SystemUiController getSystemUiController() {
return mLauncher.getSystemUiController();
}
+
+ /**
+ * Show Widget tap toast prompting user to drag instead
+ */
+ public static Toast showWidgetToast(Context context, Toast toast) {
+ // Let the user know that they have to long press to add a widget
+ if (toast != null) {
+ toast.cancel();
+ }
+
+ CharSequence msg = Utilities.wrapForTts(
+ context.getText(R.string.long_press_widget_to_add),
+ context.getString(R.string.long_accessible_way_to_add));
+ toast = Toast.makeText(context, msg, Toast.LENGTH_SHORT);
+ toast.show();
+ return toast;
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
similarity index 82%
rename from src/com/android/launcher3/LauncherAppWidgetHost.java
rename to src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index 7ea6851..d745754 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.widget;
import static android.app.Activity.RESULT_CANCELED;
@@ -29,12 +29,13 @@
import android.util.SparseArray;
import android.widget.Toast;
+import com.android.launcher3.BaseActivity;
+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.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.widget.DeferredAppWidgetHostView;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
-import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.ArrayList;
@@ -49,8 +50,11 @@
public class LauncherAppWidgetHost extends AppWidgetHost {
private static final int FLAG_LISTENING = 1;
- private static final int FLAG_RESUMED = 1 << 1;
- private static final int FLAG_LISTEN_IF_RESUMED = 1 << 2;
+ 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;
public static final int APPWIDGET_HOST_ID = 1024;
@@ -59,7 +63,7 @@
private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
private final Context mContext;
- private int mFlags = FLAG_RESUMED;
+ private int mFlags = FLAG_STATE_IS_NORMAL;
private IntConsumer mAppWidgetRemovedCallback = null;
@@ -130,49 +134,45 @@
}
/**
- * Updates the resumed state of the host.
- * When a host is not resumed, it defers calls to startListening until host is resumed again.
- * But if the host was already listening, it will not call stopListening.
- *
- * @see #setListenIfResumed(boolean)
+ * Sets or unsets a flag the can change whether the widget host should be in the listening
+ * state.
*/
- public void setResumed(boolean isResumed) {
- if (isResumed == ((mFlags & FLAG_RESUMED) != 0)) {
- return;
- }
- if (isResumed) {
- mFlags |= FLAG_RESUMED;
- // Start listening if we were supposed to start listening on resume
- if ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0 && (mFlags & FLAG_LISTENING) == 0) {
- startListening();
- }
+ private void setShouldListenFlag(int flag, boolean on) {
+ if (on) {
+ mFlags |= flag;
} else {
- mFlags &= ~FLAG_RESUMED;
+ 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();
}
}
/**
- * Updates the listening state of the host. If the host is not resumed, startListening is
- * deferred until next resume.
- *
- * @see #setResumed(boolean)
+ * Registers an "entering/leaving Normal state" event.
*/
- public void setListenIfResumed(boolean listenIfResumed) {
- if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
- return;
- }
- if (listenIfResumed) {
- mFlags |= FLAG_LISTEN_IF_RESUMED;
- if ((mFlags & FLAG_RESUMED) != 0) {
- // If we are resumed, start listening immediately. Note we do not check for
- // duplicate calls before calling startListening as startListening is safe to call
- // multiple times.
- startListening();
- }
- } else {
- mFlags &= ~FLAG_LISTEN_IF_RESUMED;
- stopListening();
- }
+ 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);
}
@Override
@@ -200,7 +200,7 @@
}
}
- void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
+ public void addPendingView(int appWidgetId, PendingAppWidgetHostView view) {
mPendingViews.put(appWidgetId, view);
}
@@ -248,7 +248,7 @@
super.onProviderChanged(appWidgetId, info);
// The super method updates the dimensions of the providerInfo. Update the
// launcher spans accordingly.
- info.initSpans(mContext);
+ info.initSpans(mContext, LauncherAppState.getIDP(mContext));
}
/**
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 6f2e179..df01295 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,12 +16,17 @@
package com.android.launcher3.widget;
+import android.app.WallpaperManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Handler;
import android.os.SystemClock;
+import android.util.Log;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -32,11 +37,13 @@
import android.widget.Advanceable;
import android.widget.RemoteViews;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
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.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -44,11 +51,16 @@
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
+import java.util.List;
+
/**
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends NavigableAppWidgetHostView
- implements TouchCompleteListener, View.OnLongClickListener {
+ implements TouchCompleteListener, View.OnLongClickListener,
+ LocalColorExtractor.Listener {
+
+ private static final String LOG_TAG = "LauncherAppWidgetHostView";
// Related to the auto-advancing of widgets
private static final long ADVANCE_INTERVAL = 20000;
@@ -61,31 +73,51 @@
private final CheckLongPressHelper mLongPressHelper;
protected final Launcher mLauncher;
+ private final Workspace mWorkspace;
+ private final WallpaperManager mWallpaperManager;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mReinflateOnConfigChange;
+ // Maintain the color manager.
+ private final LocalColorExtractor mColorExtractor;
+
private boolean mIsScrollable;
private boolean mIsAttachedToWindow;
private boolean mIsAutoAdvanceRegistered;
private Runnable mAutoAdvanceRunnable;
-
-
+ private RectF mLastLocationRegistered = null;
+ // Used to store the widget size during onLayout.
+ private final Rect mCurrentWidgetSize = new Rect();
+ private final RectF mTempRectF = new RectF();
+ private final boolean mIsRtl;
public LauncherAppWidgetHostView(Context context) {
super(context);
mLauncher = Launcher.getLauncher(context);
+ mWorkspace = mLauncher.getWorkspace();
mLongPressHelper = new CheckLongPressHelper(this, this);
mInflater = LayoutInflater.from(context);
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
setBackgroundResource(R.drawable.widget_internal_focus_bg);
- if (Utilities.ATLEAST_OREO) {
- setExecutor(Executors.THREAD_POOL_EXECUTOR);
- }
+ setExecutor(Executors.THREAD_POOL_EXECUTOR);
if (Utilities.ATLEAST_Q && Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
setOnLightBackground(true);
}
+ mIsRtl = Utilities.isRtl(context.getResources());
+ mWallpaperManager = WallpaperManager.getInstance(getContext());
+ mColorExtractor = LocalColorExtractor.newInstance(getContext());
+ mColorExtractor.setListener(this);
+ }
+
+ @Override
+ public void setColorResources(@Nullable SparseIntArray colors) {
+ if (colors == null) {
+ resetColorResources();
+ } else {
+ super.setColorResources(colors);
+ }
}
@Override
@@ -172,6 +204,7 @@
// state is updated. So isAttachedToWindow() will return true until next frame.
mIsAttachedToWindow = false;
checkIfAutoAdvance();
+ mColorExtractor.removeLocations();
}
@Override
@@ -218,6 +251,78 @@
}
mIsScrollable = checkScrollableRecursively(this);
+
+ mCurrentWidgetSize.left = left;
+ mCurrentWidgetSize.top = top;
+ mCurrentWidgetSize.right = right;
+ mCurrentWidgetSize.bottom = bottom;
+ updateColorExtraction(mCurrentWidgetSize);
+ }
+
+ private void updateColorExtraction(Rect widgetLocation) {
+ // If the widget hasn't been measured and laid out, we cannot do this.
+ if (widgetLocation.isEmpty()) {
+ return;
+ }
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+ if (info != null) {
+ int screenWidth = mLauncher.getDeviceProfile().widthPx;
+ int screenHeight = mLauncher.getDeviceProfile().heightPx;
+ int numScreens = mWorkspace.getNumPagesForWallpaperParallax();
+ int screenId = mIsRtl ? numScreens - info.screenId : info.screenId;
+ float relativeScreenWidth = 1f / numScreens;
+ float absoluteTop = widgetLocation.top;
+ float absoluteBottom = widgetLocation.bottom;
+ for (View v = (View) getParent();
+ v != null && v.getId() != R.id.launcher;
+ v = (View) v.getParent()) {
+ absoluteBottom += v.getTop();
+ absoluteTop += v.getTop();
+ }
+ float xOffset = 0;
+ View parentView = (View) getParent();
+ // The layout depends on the orientation.
+ if (getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE) {
+ xOffset = screenHeight - mWorkspace.getPaddingRight()
+ - parentView.getWidth();
+ } else {
+ xOffset = mWorkspace.getPaddingLeft() + parentView.getPaddingLeft();
+ }
+ // This is the position of the widget relative to the wallpaper, as expected by the
+ // local color extraction of the WallpaperManager.
+ // The coordinate system is such that, on the horizontal axis, each screen has a
+ // distinct range on the [0,1] segment. So if there are 3 screens, they will have the
+ // ranges [0, 1/3], [1/3, 2/3] and [2/3, 1]. The position on the subrange should be
+ // the position of the widget relative to the screen. For the vertical axis, this is
+ // simply the location of the widget relative to the screen.
+ mTempRectF.left = ((widgetLocation.left + xOffset) / screenWidth + screenId)
+ * relativeScreenWidth;
+ mTempRectF.right = ((widgetLocation.right + xOffset) / screenWidth + screenId)
+ * relativeScreenWidth;
+ mTempRectF.top = absoluteTop / screenHeight;
+ mTempRectF.bottom = absoluteBottom / screenHeight;
+ if (mTempRectF.left < 0 || mTempRectF.right > 1 || mTempRectF.top < 0
+ || mTempRectF.bottom > 1) {
+ Log.e(LOG_TAG, " Error, invalid relative position");
+ return;
+ }
+ if (!mTempRectF.equals(mLastLocationRegistered)) {
+ if (mLastLocationRegistered != null) {
+ mColorExtractor.removeLocations();
+ }
+ mLastLocationRegistered = new RectF(mTempRectF);
+ mColorExtractor.addLocation(List.of(mLastLocationRegistered));
+ }
+ } else {
+ mColorExtractor.removeLocations();
+ }
+ }
+
+ @Override
+ public void onColorsChanged(RectF rectF, SparseIntArray colors) {
+ // setColorResources will reapply the view, which must happen in the UI thread.
+ post(() -> setColorResources(colors));
}
@Override
@@ -230,6 +335,14 @@
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
maybeRegisterAutoAdvance();
+
+ if (visibility == View.VISIBLE) {
+ if (mLastLocationRegistered != null) {
+ mColorExtractor.addLocation(List.of(mLastLocationRegistered));
+ }
+ } else {
+ mColorExtractor.removeLocations();
+ }
}
private void checkIfAutoAdvance() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
similarity index 86%
rename from src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
rename to src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index 618b5de..ce97d2e 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -1,4 +1,4 @@
-package com.android.launcher3;
+package com.android.launcher3.widget;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
@@ -11,6 +11,9 @@
import android.os.Parcel;
import android.os.UserHandle;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -48,7 +51,7 @@
launcherInfo = new LauncherAppWidgetProviderInfo(p);
p.recycle();
}
- launcherInfo.initSpans(context);
+ launcherInfo.initSpans(context, LauncherAppState.getIDP(context));
return launcherInfo;
}
@@ -58,9 +61,7 @@
super(in);
}
- public void initSpans(Context context) {
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-
+ public void initSpans(Context context, InvariantDeviceProfile idp) {
Point landCellSize = idp.landscapeProfile.getCellSize();
Point portCellSize = idp.portraitProfile.getCellSize();
@@ -71,8 +72,12 @@
// We want to account for the extra amount of padding that we are adding to the widget
// to ensure that it gets the full amount of space that it has requested.
- Rect widgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(
- context, provider, null);
+ // If grids supports insetting widgets, we do not account for widget padding.
+ Rect widgetPadding = new Rect();
+ if (!idp.landscapeProfile.shouldInsetWidgets()
+ || !idp.portraitProfile.shouldInsetWidgets()) {
+ AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
+ }
spanX = Math.max(1, (int) Math.ceil(
(minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
spanY = Math.max(1, (int) Math.ceil(
diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java
new file mode 100644
index 0000000..097158b
--- /dev/null
+++ b/src/com/android/launcher3/widget/LocalColorExtractor.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.appwidget.AppWidgetHostView;
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.SparseIntArray;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import java.util.List;
+
+/** Extracts the colors we need from the wallpaper at given locations. */
+public class LocalColorExtractor implements ResourceBasedOverride {
+
+ /** Listener for color changes on a screen location. */
+ public interface Listener {
+ /**
+ * Method called when the colors on a registered location has changed.
+ *
+ * {@code extractedColors} maps the color resources {@code android.R.colors.system_*} to
+ * their value, in a format that can be passed directly to
+ * {@link AppWidgetHostView#setColorResources(SparseIntArray)}.
+ */
+ void onColorsChanged(RectF rect, SparseIntArray extractedColors);
+ }
+
+ static LocalColorExtractor newInstance(Context context) {
+ return Overrides.getObject(LocalColorExtractor.class, context.getApplicationContext(),
+ R.string.local_colors_extraction_class);
+ }
+
+ /** Sets the object that will receive the color changes. */
+ public void setListener(@Nullable Listener listener) {
+ // no-op
+ }
+
+ /** Adds a list of locations to track with this listener. */
+ public void addLocation(List<RectF> locations) {
+ // no-op
+ }
+
+ /** Stops tracking any locations. */
+ public void removeLocations() {
+ // no-op
+ }
+}
diff --git a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
index ed42bc4..6163b51 100644
--- a/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/NavigableAppWidgetHostView.java
@@ -17,6 +17,7 @@
package com.android.launcher3.widget;
import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -25,8 +26,11 @@
import android.view.ViewDebug;
import android.view.ViewGroup;
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Reorderable;
import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.views.ActivityContext;
import java.util.ArrayList;
@@ -50,11 +54,16 @@
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mScaleForReorderBounce = 1f;
+ private final Rect mTempRect = new Rect();
+
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mChildrenFocused;
+ protected final BaseActivity mActivity;
+
public NavigableAppWidgetHostView(Context context) {
super(context);
+ mActivity = ActivityContext.lookupContext(context);
}
@Override
@@ -222,6 +231,25 @@
int width = (int) (getMeasuredWidth() * mScaleToFit);
int height = (int) (getMeasuredHeight() * mScaleToFit);
- bounds.set(0, 0 , width, height);
+ getWidgetInset(mActivity.getDeviceProfile(), mTempRect);
+ bounds.set(mTempRect.left, mTempRect.top, width - mTempRect.right,
+ height - mTempRect.bottom);
+ }
+
+ /**
+ * Widgets have padding added by the system. We may choose to inset this padding if the grid
+ * supports it.
+ */
+ public void getWidgetInset(DeviceProfile grid, Rect out) {
+ if (!grid.shouldInsetWidgets()) {
+ out.setEmpty();
+ return;
+ }
+ AppWidgetProviderInfo info = getAppWidgetInfo();
+ if (info == null) {
+ out.set(grid.inv.defaultWidgetPadding);
+ } else {
+ AppWidgetHostView.getDefaultPaddingForWidget(getContext(), info.provider, out);
+ }
}
}
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index bef9a08..ee0b84e 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -20,7 +20,6 @@
import android.appwidget.AppWidgetHostView;
import android.os.Bundle;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PendingAddItemInfo;
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index ca47728..4b113d8 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -29,6 +29,7 @@
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
+import android.util.SizeF;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.View;
@@ -46,6 +47,8 @@
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
+import java.util.List;
+
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
implements OnClickListener, ItemInfoUpdateReceiver {
private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
@@ -109,6 +112,11 @@
}
@Override
+ public void updateAppWidgetSize(Bundle newOptions, List<SizeF> sizes) {
+ // No-op
+ }
+
+ @Override
protected View getDefaultView() {
View defaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
defaultView.setOnClickListener(this);
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 3c11274..6e83836 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -25,6 +25,8 @@
import android.view.View;
import android.widget.RemoteViews;
+import androidx.annotation.Nullable;
+
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.Launcher;
@@ -33,7 +35,6 @@
import com.android.launcher3.R;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView;
-import com.android.launcher3.dragndrop.LivePreviewWidgetCell;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.icons.LauncherIcons;
@@ -48,14 +49,14 @@
private final PendingAddItemInfo mAddInfo;
private int[] mEstimatedCellSize;
- private RemoteViews mPreview;
+ @Nullable private RemoteViews mPreview;
public PendingItemDragHelper(View view) {
super(view);
mAddInfo = (PendingAddItemInfo) view.getTag();
}
- public void setPreview(RemoteViews preview) {
+ public void setPreview(@Nullable RemoteViews preview) {
mPreview = preview;
}
@@ -90,7 +91,7 @@
int[] previewSizeBeforeScale = new int[1];
if (mPreview != null) {
- preview = LivePreviewWidgetCell.generateFromRemoteViews(launcher, mPreview,
+ preview = WidgetCell.generateFromRemoteViews(launcher, mPreview,
createWidgetInfo.info, maxWidth, previewSizeBeforeScale);
}
if (preview == null) {
diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
index ebc2a25..1ac5a33 100644
--- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
+++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java
@@ -21,7 +21,6 @@
import android.os.Parcelable;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.PendingRequestArgs;
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index bef91d2..229df50 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -16,7 +16,11 @@
package com.android.launcher3.widget;
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
+import android.appwidget.AppWidgetHostView;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.CancellationSignal;
import android.util.AttributeSet;
@@ -24,10 +28,11 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.widget.RemoteViews;
import android.widget.TextView;
import com.android.launcher3.BaseActivity;
@@ -36,6 +41,7 @@
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.model.WidgetItem;
/**
@@ -60,12 +66,15 @@
/** Widget preview width is calculated by multiplying this factor to the widget cell width. */
private static final float PREVIEW_SCALE = 0.8f;
+ protected int mPreviewWidth;
+ protected int mPreviewHeight;
protected int mPresetPreviewSize;
private int mCellSize;
private WidgetImageView mWidgetImage;
private TextView mWidgetName;
private TextView mWidgetDims;
+ private TextView mWidgetDescription;
protected WidgetItem mItem;
@@ -81,6 +90,9 @@
protected final DeviceProfile mDeviceProfile;
private final CheckLongPressHelper mLongPressHelper;
+ private RemoteViews mPreview;
+ private AppWidgetHostView mPreviewAppWidgetHostView;
+
public WidgetCell(Context context) {
this(context, null);
}
@@ -106,15 +118,25 @@
private void setContainerWidth() {
mCellSize = (int) (mDeviceProfile.allAppsIconSizePx * WIDTH_SCALE);
mPresetPreviewSize = (int) (mCellSize * PREVIEW_SCALE);
+ mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview);
- mWidgetName = ((TextView) findViewById(R.id.widget_name));
- mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
+ mWidgetImage = findViewById(R.id.widget_preview);
+ mWidgetName = findViewById(R.id.widget_name);
+ mWidgetDims = findViewById(R.id.widget_dims);
+ mWidgetDescription = findViewById(R.id.widget_description);
+ }
+
+ public void setPreview(RemoteViews view) {
+ mPreview = view;
+ }
+
+ public RemoteViews getPreview() {
+ return mPreview;
}
/**
@@ -128,22 +150,38 @@
mWidgetImage.setBitmap(null, null);
mWidgetName.setText(null);
mWidgetDims.setText(null);
+ mWidgetDescription.setText(null);
+ mWidgetDescription.setVisibility(GONE);
+ mPreviewWidth = mPreviewHeight = mPresetPreviewSize;
if (mActiveRequest != null) {
mActiveRequest.cancel();
mActiveRequest = null;
}
+ mPreview = null;
+ mPreviewAppWidgetHostView = null;
}
public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
+ applyPreviewLayout(item);
+
mItem = item;
mWidgetName.setText(mItem.label);
mWidgetDims.setText(getContext().getString(R.string.widget_dims_format,
mItem.spanX, mItem.spanY));
mWidgetDims.setContentDescription(getContext().getString(
R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
- mWidgetPreviewLoader = loader;
+ if (ATLEAST_S && mItem.widgetInfo != null) {
+ CharSequence description = mItem.widgetInfo.loadDescription(getContext());
+ if (description != null && description.length() > 0) {
+ mWidgetDescription.setText(description);
+ mWidgetDescription.setVisibility(VISIBLE);
+ } else {
+ mWidgetDescription.setVisibility(GONE);
+ }
+ }
+ mWidgetPreviewLoader = loader;
if (item.activityInfo != null) {
setTag(new PendingAddShortcutInfo(item.activityInfo));
} else {
@@ -151,6 +189,27 @@
}
}
+ private void applyPreviewLayout(WidgetItem item) {
+ if (ATLEAST_S
+ && mPreview == null
+ && item.widgetInfo != null
+ && item.widgetInfo.previewLayout != Resources.ID_NULL) {
+ mPreviewAppWidgetHostView = new AppWidgetHostView(getContext());
+ LauncherAppWidgetProviderInfo launcherAppWidgetProviderInfo =
+ LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(),
+ item.widgetInfo.clone());
+ // A hack to force the initial layout to be the preview layout since there is no API for
+ // rendering a preview layout for work profile apps yet. For non-work profile layout, a
+ // proper solution is to use RemoteViews(PackageName, LayoutId).
+ launcherAppWidgetProviderInfo.initialLayout = item.widgetInfo.previewLayout;
+ mPreviewAppWidgetHostView.setAppWidget(/* appWidgetId= */ -1,
+ launcherAppWidgetProviderInfo);
+ mPreviewAppWidgetHostView.setPadding(/* left= */ 0, /* top= */0, /* right= */
+ 0, /* bottom= */ 0);
+ mPreviewAppWidgetHostView.updateAppWidget(/* remoteViews= */ null);
+ }
+ }
+
public WidgetImageView getWidgetView() {
return mWidgetImage;
}
@@ -181,6 +240,11 @@
return;
}
if (bitmap != null) {
+ LayoutParams layoutParams = (LayoutParams) mWidgetImage.getLayoutParams();
+ layoutParams.width = bitmap.getWidth();
+ layoutParams.height = bitmap.getHeight();
+ mWidgetImage.setLayoutParams(layoutParams);
+
mWidgetImage.setBitmap(bitmap, mWidgetPreviewLoader.getBadgeForUser(mItem.user,
BaseIconFactory.getBadgeSizeForIconSize(mDeviceProfile.allAppsIconSizePx)));
if (mAnimatePreview) {
@@ -194,11 +258,36 @@
}
public void ensurePreview() {
+ if (mPreview != null && mActiveRequest == null) {
+ Bitmap preview = generateFromRemoteViews(
+ mActivity, mPreview, mItem.widgetInfo, mPresetPreviewSize, new int[1]);
+ if (preview != null) {
+ applyPreview(preview);
+ return;
+ }
+ }
+
+ if (mPreviewAppWidgetHostView != null) {
+ Bitmap preview = generateFromView(mActivity, mPreviewAppWidgetHostView,
+ mItem.widgetInfo, mPreviewWidth, new int[1]);
+ if (preview != null) {
+ applyPreview(preview);
+ return;
+ }
+ }
if (mActiveRequest != null) {
return;
}
- mActiveRequest = mWidgetPreviewLoader.getPreview(
- mItem, mPresetPreviewSize, mPresetPreviewSize, this);
+ mActiveRequest = mWidgetPreviewLoader.getPreview(mItem, mPreviewWidth, mPreviewHeight,
+ this);
+ }
+
+ /** Sets the widget preview image size in number of cells. */
+ public void setPreviewSize(int spanX, int spanY) {
+ int padding = 2 * getResources()
+ .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
+ mPreviewWidth = mDeviceProfile.cellWidthPx * spanX + padding;
+ mPreviewHeight = mDeviceProfile.cellHeightPx * spanY + padding;
}
@Override
@@ -233,12 +322,6 @@
}
@Override
- public void setLayoutParams(ViewGroup.LayoutParams params) {
- params.width = params.height = mCellSize;
- super.setLayoutParams(params);
- }
-
- @Override
public CharSequence getAccessibilityClassName() {
return WidgetCell.class.getName();
}
@@ -248,4 +331,53 @@
super.onInitializeAccessibilityNodeInfo(info);
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK);
}
+
+ /**
+ * Generates a bitmap by inflating {@param views}.
+ * @see com.android.launcher3.WidgetPreviewLoader#generateWidgetPreview
+ *
+ * TODO: Consider moving this to the background thread.
+ */
+ public static Bitmap generateFromRemoteViews(BaseActivity activity, RemoteViews views,
+ LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
+ try {
+ return generateFromView(activity, views.apply(activity, new FrameLayout(activity)),
+ info, previewSize, preScaledWidthOut);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static Bitmap generateFromView(BaseActivity activity, View v,
+ LauncherAppWidgetProviderInfo info, int previewSize, int[] preScaledWidthOut) {
+
+ DeviceProfile dp = activity.getDeviceProfile();
+ int viewWidth = dp.cellWidthPx * info.spanX;
+ int viewHeight = dp.cellHeightPx * info.spanY;
+
+ v.measure(MeasureSpec.makeMeasureSpec(viewWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(viewHeight, MeasureSpec.EXACTLY));
+
+ viewWidth = v.getMeasuredWidth();
+ viewHeight = v.getMeasuredHeight();
+ v.layout(0, 0, viewWidth, viewHeight);
+
+ preScaledWidthOut[0] = viewWidth;
+ final int bitmapWidth, bitmapHeight;
+ final float scale;
+ if (viewWidth > previewSize) {
+ scale = ((float) previewSize) / viewWidth;
+ bitmapWidth = previewSize;
+ bitmapHeight = (int) (viewHeight * scale);
+ } else {
+ scale = 1;
+ bitmapWidth = viewWidth;
+ bitmapHeight = viewHeight;
+ }
+
+ return BitmapRenderer.createSoftwareBitmap(bitmapWidth, bitmapHeight, c -> {
+ c.scale(scale, scale);
+ v.draw(c);
+ });
+ }
}
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index c022374..12e0d43 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -7,17 +7,20 @@
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
+import android.util.SizeF;
import android.view.View;
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.util.Thunk;
+import java.util.ArrayList;
+import java.util.stream.Collectors;
+
public class WidgetHostViewLoader implements DragController.DragListener {
private static final String TAG = "WidgetHostViewLoader";
private static final boolean LOGD = false;
@@ -152,24 +155,28 @@
}
public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
- Rect rect = new Rect();
- AppWidgetResizeFrame.getWidgetSizeRanges(context, info.spanX, info.spanY, rect);
+ ArrayList<SizeF> sizes = AppWidgetResizeFrame
+ .getWidgetSizes(context, info.spanX, info.spanY);
+
Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
info.componentName, null);
-
float density = context.getResources().getDisplayMetrics().density;
- int xPaddingDips = (int) ((padding.left + padding.right) / density);
- int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
+ float xPaddingDips = (padding.left + padding.right) / density;
+ float yPaddingDips = (padding.top + padding.bottom) / density;
+
+ ArrayList<SizeF> paddedSizes = sizes.stream().map(
+ size -> new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips),
+ Math.max(0.f, size.getHeight() - yPaddingDips))).collect(
+ Collectors.toCollection(ArrayList::new));
+
+ Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes, null /* outRect */);
Bundle options = new Bundle();
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- rect.left - xPaddingDips);
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- rect.top - yPaddingDips);
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- rect.right - xPaddingDips);
- options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- rect.bottom - yPaddingDips);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
+ options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
+ options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
return options;
}
}
diff --git a/src/com/android/launcher3/widget/WidgetListRowEntry.java b/src/com/android/launcher3/widget/WidgetListRowEntry.java
deleted file mode 100644
index 17e4673..0000000
--- a/src/com/android/launcher3/widget/WidgetListRowEntry.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget;
-
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.data.ItemInfo;
-import com.android.launcher3.model.data.PackageItemInfo;
-
-import java.util.ArrayList;
-
-/**
- * Holder class to store all the information related to a single row in the widget list
- */
-public class WidgetListRowEntry {
-
- public final PackageItemInfo pkgItem;
-
- public final ArrayList<WidgetItem> widgets;
-
- /**
- * Character that is used as a section name for the {@link ItemInfo#title}.
- * (e.g., "G" will be stored if title is "Google")
- */
- public String titleSectionName;
-
- public WidgetListRowEntry(PackageItemInfo pkgItem, ArrayList<WidgetItem> items) {
- this.pkgItem = pkgItem;
- this.widgets = items;
- }
-
- @Override
- public String toString() {
- return pkgItem.packageName + ":" + widgets.size();
- }
-}
diff --git a/src/com/android/launcher3/widget/WidgetManagerHelper.java b/src/com/android/launcher3/widget/WidgetManagerHelper.java
index 4b6c569..15fa844 100644
--- a/src/com/android/launcher3/widget/WidgetManagerHelper.java
+++ b/src/com/android/launcher3/widget/WidgetManagerHelper.java
@@ -23,24 +23,18 @@
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
-import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.Nullable;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Utilities;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.UserCache;
-import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.custom.CustomWidgetManager;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -84,22 +78,8 @@
return allWidgetsSteam(mContext).collect(Collectors.toList());
}
- if (Utilities.ATLEAST_OREO) {
- return mAppWidgetManager.getInstalledProvidersForPackage(
- packageUser.mPackageName, packageUser.mUser);
- }
-
- String pkg = packageUser.mPackageName;
- return Stream.concat(
- // Only get providers for the given package/user.
- mAppWidgetManager.getInstalledProvidersForProfile(packageUser.mUser)
- .stream()
- .filter(w -> w.provider.equals(pkg)),
- Process.myUserHandle().equals(packageUser.mUser)
- && mContext.getPackageName().equals(pkg)
- ? CustomWidgetManager.INSTANCE.get(mContext).stream()
- : Stream.empty())
- .collect(Collectors.toList());
+ return mAppWidgetManager.getInstalledProvidersForPackage(
+ packageUser.mPackageName, packageUser.mUser);
}
/**
@@ -138,15 +118,6 @@
appWidgetId).getBoolean(WIDGET_OPTION_RESTORE_COMPLETED);
}
- public static Map<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap(Context context) {
- if (WidgetsModel.GO_DISABLE_WIDGETS) {
- return Collections.emptyMap();
- }
- return allWidgetsSteam(context).collect(
- Collectors.toMap(info -> new ComponentKey(info.provider, info.getProfile()),
- Function.identity()));
- }
-
private static Stream<AppWidgetProviderInfo> allWidgetsSteam(Context context) {
AppWidgetManager awm = context.getSystemService(AppWidgetManager.class);
return Stream.concat(
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 30be7a6..e6d54a9 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -29,16 +29,20 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
+import android.widget.ScrollView;
+import android.widget.TableLayout;
+import android.widget.TableRow;
import android.widget.TextView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.ResourceUtils;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
import java.util.List;
@@ -64,6 +68,8 @@
private static final int DEFAULT_CLOSE_DURATION = 200;
private ItemInfo mOriginalItemInfo;
private Rect mInsets;
+ private final int mMaxTableHeight;
+ private int mMaxHorizontalSpan = 4;
public WidgetsBottomSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -74,12 +80,41 @@
setWillNotDraw(false);
mInsets = new Rect();
mContent = this;
+ DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
+ // Set the max table height to 2 / 3 of the grid height so that the bottom picker won't
+ // take over the entire view vertically.
+ mMaxTableHeight = deviceProfile.inv.numRows * 2 / 3 * deviceProfile.cellHeightPx;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ int paddingPx = 2 * getResources().getDimensionPixelOffset(
+ R.dimen.widget_cell_horizontal_padding);
+ int maxHorizontalSpan = findViewById(R.id.widgets_table).getMeasuredWidth()
+ / (mLauncher.getDeviceProfile().cellWidthPx + paddingPx);
+
+ if (mMaxHorizontalSpan != maxHorizontalSpan) {
+ // Ensure the table layout is showing widgets in the right column after measure.
+ mMaxHorizontalSpan = maxHorizontalSpan;
+ onWidgetsBound();
+ }
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
setTranslationShift(mTranslationShift);
+
+ // Ensure the scroll view height is not larger than mMaxTableHeight, which is a value
+ // smaller than the entire screen height.
+ ScrollView widgetsTableScrollView = findViewById(R.id.widgets_table_scroll_view);
+ if (widgetsTableScrollView.getMeasuredHeight() > mMaxTableHeight) {
+ ViewGroup.LayoutParams layoutParams = widgetsTableScrollView.getLayoutParams();
+ layoutParams.height = mMaxTableHeight;
+ widgetsTableScrollView.setLayoutParams(layoutParams);
+ }
}
public void populateAndShow(ItemInfo itemInfo) {
@@ -100,47 +135,31 @@
mOriginalItemInfo.getTargetComponent().getPackageName(),
mOriginalItemInfo.user));
- ViewGroup widgetRow = findViewById(R.id.widgets);
- ViewGroup widgetCells = widgetRow.findViewById(R.id.widgets_cell_list);
+ TableLayout widgetsTable = findViewById(R.id.widgets_table);
+ widgetsTable.removeAllViews();
- widgetCells.removeAllViews();
-
- for (int i = 0; i < widgets.size(); i++) {
- WidgetCell widget = addItemCell(widgetCells);
- widget.applyFromCellItem(widgets.get(i), LauncherAppState.getInstance(mLauncher)
- .getWidgetCache());
- widget.ensurePreview();
- widget.setVisibility(View.VISIBLE);
- if (i < widgets.size() - 1) {
- addDivider(widgetCells);
- }
- }
-
- if (widgets.size() == 1) {
- // If there is only one widget, we want to center it instead of left-align.
- WidgetsBottomSheet.LayoutParams params = (WidgetsBottomSheet.LayoutParams)
- widgetRow.getLayoutParams();
- params.gravity = Gravity.CENTER_HORIZONTAL;
- } else {
- // Otherwise, add an empty view to the start as padding (but still scroll edge to edge).
- View leftPaddingView = LayoutInflater.from(getContext()).inflate(
- R.layout.widget_list_divider, widgetRow, false);
- leftPaddingView.getLayoutParams().width = ResourceUtils.pxFromDp(
- 16, getResources().getDisplayMetrics());
- widgetCells.addView(leftPaddingView, 0);
- }
- }
-
- private void addDivider(ViewGroup parent) {
- LayoutInflater.from(getContext()).inflate(R.layout.widget_list_divider, parent, true);
+ WidgetsTableUtils.groupWidgetItemsIntoTable(widgets, mMaxHorizontalSpan).forEach(row -> {
+ TableRow tableRow = new TableRow(getContext());
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ row.forEach(widgetItem -> {
+ WidgetCell widget = addItemCell(tableRow);
+ widget.setPreviewSize(widgetItem.spanX, widgetItem.spanY);
+ widget.applyFromCellItem(widgetItem, LauncherAppState.getInstance(mLauncher)
+ .getWidgetCache());
+ widget.ensurePreview();
+ widget.setVisibility(View.VISIBLE);
+ });
+ widgetsTable.addView(tableRow);
+ });
}
protected WidgetCell addItemCell(ViewGroup parent) {
- WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext()).inflate(
- R.layout.widget_cell, parent, false);
+ WidgetCell widget = (WidgetCell) LayoutInflater.from(getContext())
+ .inflate(R.layout.widget_cell, parent, false);
- widget.setOnClickListener(this);
- widget.setOnLongClickListener(this);
+ WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+ preview.setOnClickListener(this);
+ preview.setOnLongClickListener(this);
widget.setAnimatePreview(false);
parent.addView(widget);
@@ -180,11 +199,6 @@
}
@Override
- protected int getElementsRowCount() {
- return 1;
- }
-
- @Override
protected Pair<View, String> getAccessibilityTarget() {
return Pair.create(findViewById(R.id.title), getContext().getString(
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
diff --git a/src/com/android/launcher3/widget/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/WidgetsDiffReporter.java
deleted file mode 100644
index df6e2c3..0000000
--- a/src/com/android/launcher3/widget/WidgetsDiffReporter.java
+++ /dev/null
@@ -1,143 +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.widget;
-
-import android.util.Log;
-
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.model.data.PackageItemInfo;
-import com.android.launcher3.widget.WidgetsListAdapter.WidgetListRowEntryComparator;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-
-/**
- * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
- * methods accordingly.
- */
-public class WidgetsDiffReporter {
- private static final boolean DEBUG = false;
- private static final String TAG = "WidgetsDiffReporter";
-
- private final IconCache mIconCache;
- private final RecyclerView.Adapter mListener;
-
- public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) {
- mIconCache = iconCache;
- mListener = listener;
- }
-
- public void process(ArrayList<WidgetListRowEntry> currentEntries,
- ArrayList<WidgetListRowEntry> newEntries, WidgetListRowEntryComparator comparator) {
- if (DEBUG) {
- Log.d(TAG, "process oldEntries#=" + currentEntries.size()
- + " newEntries#=" + newEntries.size());
- }
- // Early exit if either of the list is empty
- if (currentEntries.isEmpty() || newEntries.isEmpty()) {
- // Skip if both list are empty.
- // On rotation, we open the widget tray with empty. Then try to fetch the list again
- // when the animation completes (which still gives empty). And we get the final result
- // when the bind actually completes.
- if (currentEntries.size() != newEntries.size()) {
- currentEntries.clear();
- currentEntries.addAll(newEntries);
- mListener.notifyDataSetChanged();
- }
- return;
- }
- ArrayList<WidgetListRowEntry> orgEntries =
- (ArrayList<WidgetListRowEntry>) currentEntries.clone();
- Iterator<WidgetListRowEntry> orgIter = orgEntries.iterator();
- Iterator<WidgetListRowEntry> newIter = newEntries.iterator();
-
- WidgetListRowEntry orgRowEntry = orgIter.next();
- WidgetListRowEntry newRowEntry = newIter.next();
-
- do {
- int diff = comparePackageName(orgRowEntry, newRowEntry, comparator);
- if (DEBUG) {
- Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
- diff, orgRowEntry != null? orgRowEntry.toString() : null,
- newRowEntry != null? newRowEntry.toString() : null));
- }
- int index = -1;
- if (diff < 0) {
- index = currentEntries.indexOf(orgRowEntry);
- mListener.notifyItemRemoved(index);
- if (DEBUG) {
- Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
- orgRowEntry.titleSectionName));
- }
- currentEntries.remove(index);
- orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
- } else if (diff > 0) {
- index = orgRowEntry != null? currentEntries.indexOf(orgRowEntry):
- currentEntries.size();
- currentEntries.add(index, newRowEntry);
- if (DEBUG) {
- Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
- newRowEntry.titleSectionName));
- }
- newRowEntry = newIter.hasNext() ? newIter.next() : null;
- mListener.notifyItemInserted(index);
-
- } else {
- // same package name but,
- // did the icon, title, etc, change?
- // or did the widget size and desc, span, etc change?
- if (!isSamePackageItemInfo(orgRowEntry.pkgItem, newRowEntry.pkgItem) ||
- !orgRowEntry.widgets.equals(newRowEntry.widgets)) {
- index = currentEntries.indexOf(orgRowEntry);
- currentEntries.set(index, newRowEntry);
- mListener.notifyItemChanged(index);
- if (DEBUG) {
- Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
- newRowEntry.titleSectionName));
- }
- }
- orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
- newRowEntry = newIter.hasNext() ? newIter.next() : null;
- }
- } while(orgRowEntry != null || newRowEntry != null);
- }
-
- /**
- * Compare package name using the same comparator as in {@link WidgetsListAdapter}.
- * Also handle null row pointers.
- */
- private int comparePackageName(WidgetListRowEntry curRow, WidgetListRowEntry newRow,
- WidgetListRowEntryComparator comparator) {
- if (curRow == null && newRow == null) {
- throw new IllegalStateException("Cannot compare PackageItemInfo if both rows are null.");
- }
-
- if (curRow == null && newRow != null) {
- return 1; // new row needs to be inserted
- } else if (curRow != null && newRow == null) {
- return -1; // old row needs to be deleted
- }
- return comparator.compare(curRow, newRow);
- }
-
- private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
- return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
- && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
- }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
deleted file mode 100644
index 68a3ec5..0000000
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ /dev/null
@@ -1,262 +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.widget;
-
-import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
-import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
-import com.android.launcher3.R;
-import com.android.launcher3.anim.PendingAnimation;
-import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.views.RecyclerViewFastScroller;
-import com.android.launcher3.views.TopRoundedCornerView;
-
-/**
- * Popup for showing the full list of available widgets
- */
-public class WidgetsFullSheet extends BaseWidgetSheet
- implements Insettable, ProviderChangedListener {
-
- private static final long DEFAULT_OPEN_DURATION = 267;
- private static final long FADE_IN_DURATION = 150;
- private static final float VERTICAL_START_POSITION = 0.3f;
-
- private final Rect mInsets = new Rect();
-
- private final WidgetsListAdapter mAdapter;
-
- private WidgetsRecyclerView mRecyclerView;
-
- public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- LauncherAppState apps = LauncherAppState.getInstance(context);
- mAdapter = new WidgetsListAdapter(context,
- LayoutInflater.from(context), apps.getWidgetCache(), apps.getIconCache(),
- this, this);
-
- }
-
- public WidgetsFullSheet(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mContent = findViewById(R.id.container);
-
- mRecyclerView = findViewById(R.id.widgets_list_view);
- mRecyclerView.setAdapter(mAdapter);
- mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
-
- TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
- springLayout.addSpringView(R.id.widgets_list_view);
- mRecyclerView.setEdgeEffectFactory(springLayout.createEdgeEffectFactory());
- onWidgetsBound();
- }
-
- @VisibleForTesting
- public WidgetsRecyclerView getRecyclerView() {
- return mRecyclerView;
- }
-
- @Override
- protected Pair<View, String> getAccessibilityTarget() {
- return Pair.create(mRecyclerView, getContext().getString(
- mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mLauncher.getAppWidgetHost().addProviderChangeListener(this);
- notifyWidgetProvidersChanged();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
- }
-
- @Override
- public void setInsets(Rect insets) {
- mInsets.set(insets);
-
- mRecyclerView.setPadding(
- mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
- mRecyclerView.getPaddingRight(), insets.bottom);
- if (insets.bottom > 0) {
- setupNavBarColor();
- } else {
- clearNavBarColor();
- }
-
- ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
- requestLayout();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int widthUsed;
- if (mInsets.bottom > 0) {
- widthUsed = mInsets.left + mInsets.right;
- } else {
- Rect padding = mLauncher.getDeviceProfile().workspacePadding;
- widthUsed = Math.max(padding.left + padding.right,
- 2 * (mInsets.left + mInsets.right));
- }
-
- int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
- measureChildWithMargins(mContent, widthMeasureSpec,
- widthUsed, heightMeasureSpec, heightUsed);
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
- MeasureSpec.getSize(heightMeasureSpec));
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int width = r - l;
- int height = b - t;
-
- // Content is laid out as center bottom aligned
- int contentWidth = mContent.getMeasuredWidth();
- int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
- mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
- contentLeft + contentWidth, height);
-
- setTranslationShift(mTranslationShift);
- }
-
- @Override
- public void notifyWidgetProvidersChanged() {
- mLauncher.refreshAndBindWidgetsForPackageUser(null);
- }
-
- @Override
- public void onWidgetsBound() {
- mAdapter.setWidgets(mLauncher.getPopupDataProvider().getAllWidgets());
- }
-
- private void open(boolean animate) {
- if (animate) {
- if (getPopupContainer().getInsets().bottom > 0) {
- mContent.setAlpha(0);
- setTranslationShift(VERTICAL_START_POSITION);
- }
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator
- .setDuration(DEFAULT_OPEN_DURATION)
- .setInterpolator(AnimationUtils.loadInterpolator(
- getContext(), android.R.interpolator.linear_out_slow_in));
- mRecyclerView.setLayoutFrozen(true);
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRecyclerView.setLayoutFrozen(false);
- mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
- mOpenCloseAnimator.removeListener(this);
- }
- });
- post(() -> {
- mOpenCloseAnimator.start();
- mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
- });
- } else {
- setTranslationShift(TRANSLATION_SHIFT_OPENED);
- mAdapter.setApplyBitmapDeferred(false, mRecyclerView);
- post(this::announceAccessibilityChanges);
- }
- }
-
- @Override
- protected void handleClose(boolean animate) {
- handleClose(animate, DEFAULT_OPEN_DURATION);
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- // Disable swipe down when recycler view is scrolling
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = false;
- RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
- if (scroller.getThumbOffsetY() >= 0 &&
- getPopupContainer().isEventOverView(scroller, ev)) {
- mNoIntercept = true;
- } else if (getPopupContainer().isEventOverView(mContent, ev)) {
- mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, getPopupContainer());
- }
- }
- return super.onControllerInterceptTouchEvent(ev);
- }
-
- public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
- WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
- .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
- sheet.attachToContainer();
- sheet.mIsOpen = true;
- sheet.open(animate);
- return sheet;
- }
-
- @VisibleForTesting
- public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
- return launcher.findViewById(R.id.widgets_list_view);
- }
-
- @Override
- protected int getElementsRowCount() {
- return mAdapter.getItemCount();
- }
-
- @Override
- public void addHintCloseAnim(
- float distanceToMove, Interpolator interpolator, PendingAnimation target) {
- target.setFloat(mRecyclerView, VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
- target.setViewAlpha(mRecyclerView, 0.5f, interpolator);
- }
-
- @Override
- protected void onCloseComplete() {
- super.onCloseComplete();
- AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
- }
-}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
deleted file mode 100644
index a45521d..0000000
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ /dev/null
@@ -1,219 +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.widget;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-
-import com.android.launcher3.icons.IconCache;
-import com.android.launcher3.R;
-import com.android.launcher3.WidgetPreviewLoader;
-import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.util.LabelComparator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-
-/**
- * List view adapter for the widget tray.
- *
- * <p>Memory vs. Performance:
- * The less number of types of views are inserted into a {@link RecyclerView}, the more recycling
- * happens and less memory is consumed. {@link #getItemViewType} was not overridden as there is
- * only a single type of view.
- */
-public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
-
- private static final String TAG = "WidgetsListAdapter";
- private static final boolean DEBUG = false;
-
- private final WidgetPreviewLoader mWidgetPreviewLoader;
- private final LayoutInflater mLayoutInflater;
-
- private final OnClickListener mIconClickListener;
- private final OnLongClickListener mIconLongClickListener;
- private final int mIndent;
- private ArrayList<WidgetListRowEntry> mEntries = new ArrayList<>();
- private final WidgetsDiffReporter mDiffReporter;
-
- private boolean mApplyBitmapDeferred;
-
- public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
- WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
- OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
- mLayoutInflater = layoutInflater;
- mWidgetPreviewLoader = widgetPreviewLoader;
- mIconClickListener = iconClickListener;
- mIconLongClickListener = iconLongClickListener;
- mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
- mDiffReporter = new WidgetsDiffReporter(iconCache, this);
- }
-
- /**
- * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}
- *
- * @see WidgetCell#setApplyBitmapDeferred(boolean)
- */
- public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
- mApplyBitmapDeferred = isDeferred;
-
- for (int i = rv.getChildCount() - 1; i >= 0; i--) {
- WidgetsRowViewHolder holder = (WidgetsRowViewHolder)
- rv.getChildViewHolder(rv.getChildAt(i));
- for (int j = holder.cellContainer.getChildCount() - 1; j >= 0; j--) {
- View v = holder.cellContainer.getChildAt(j);
- if (v instanceof WidgetCell) {
- ((WidgetCell) v).setApplyBitmapDeferred(mApplyBitmapDeferred);
- }
- }
- }
- }
-
- /**
- * Update the widget list.
- */
- public void setWidgets(ArrayList<WidgetListRowEntry> tempEntries) {
- WidgetListRowEntryComparator rowComparator = new WidgetListRowEntryComparator();
- Collections.sort(tempEntries, rowComparator);
- mDiffReporter.process(mEntries, tempEntries, rowComparator);
- }
-
- @Override
- public int getItemCount() {
- return mEntries.size();
- }
-
- public String getSectionName(int pos) {
- return mEntries.get(pos).titleSectionName;
- }
-
- @Override
- public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
- WidgetListRowEntry entry = mEntries.get(pos);
- List<WidgetItem> infoList = entry.widgets;
-
- ViewGroup row = holder.cellContainer;
- if (DEBUG) {
- Log.d(TAG, String.format(
- "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]",
- pos, infoList.size(), row.getChildCount()));
- }
-
- // Add more views.
- // if there are too many, hide them.
- int expectedChildCount = infoList.size() + Math.max(0, infoList.size() - 1);
- int childCount = row.getChildCount();
-
- if (expectedChildCount > childCount) {
- for (int i = childCount ; i < expectedChildCount; i++) {
- if ((i & 1) == 1) {
- // Add a divider for odd index
- mLayoutInflater.inflate(R.layout.widget_list_divider, row);
- } else {
- // Add cell for even index
- WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
- R.layout.widget_cell, row, false);
-
- // set up touch.
- widget.setOnClickListener(mIconClickListener);
- widget.setOnLongClickListener(mIconLongClickListener);
- row.addView(widget);
- }
- }
- } else if (expectedChildCount < childCount) {
- for (int i = expectedChildCount ; i < childCount; i++) {
- row.getChildAt(i).setVisibility(View.GONE);
- }
- }
-
- // Bind the views in the application info section.
- holder.title.applyFromPackageItemInfo(entry.pkgItem);
-
- // Bind the view in the widget horizontal tray region.
- for (int i=0; i < infoList.size(); i++) {
- WidgetCell widget = (WidgetCell) row.getChildAt(2*i);
- widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader);
- widget.setApplyBitmapDeferred(mApplyBitmapDeferred);
- widget.ensurePreview();
- widget.setVisibility(View.VISIBLE);
-
- if (i > 0) {
- row.getChildAt(2*i - 1).setVisibility(View.VISIBLE);
- }
- }
- }
-
- @Override
- public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- if (DEBUG) {
- Log.v(TAG, "\nonCreateViewHolder");
- }
-
- ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
- R.layout.widgets_list_row_view, parent, false);
-
- // if the end padding is 0, then container view (horizontal scroll view) doesn't respect
- // the end of the linear layout width + the start padding and doesn't allow scrolling.
- container.findViewById(R.id.widgets_cell_list).setPaddingRelative(mIndent, 0, 1, 0);
-
- return new WidgetsRowViewHolder(container);
- }
-
- @Override
- public void onViewRecycled(WidgetsRowViewHolder holder) {
- int total = holder.cellContainer.getChildCount();
- for (int i = 0; i < total; i+=2) {
- WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i);
- widget.clear();
- }
- }
-
- public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) {
- // If child views are animating, then the RecyclerView may choose not to recycle the view,
- // causing extraneous onCreateViewHolder() calls. It is safe in this case to continue
- // recycling this view, and take care in onViewRecycled() to cancel any existing
- // animations.
- return true;
- }
-
- @Override
- public long getItemId(int pos) {
- return pos;
- }
-
- /**
- * Comparator for sorting WidgetListRowEntry based on package title
- */
- public static class WidgetListRowEntryComparator implements Comparator<WidgetListRowEntry> {
-
- private final LabelComparator mComparator = new LabelComparator();
-
- @Override
- public int compare(WidgetListRowEntry a, WidgetListRowEntry b) {
- return mComparator.compare(a.pkgItem.title.toString(), b.pkgItem.title.toString());
- }
- }
-}
diff --git a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
index 1086987..8b3bbce 100644
--- a/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/custom/CustomAppWidgetProviderInfo.java
@@ -22,8 +22,9 @@
import android.os.Parcel;
import android.os.Parcelable;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
/**
* Custom app widget provider info that can be used as a widget, but provide extra functionality
@@ -57,7 +58,7 @@
}
@Override
- public void initSpans(Context context) { }
+ public void initSpans(Context context, InvariantDeviceProfile idp) { }
@Override
public String getLabel(PackageManager packageManager) {
diff --git a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
index 0b66ec0..329a444 100644
--- a/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
+++ b/src/com/android/launcher3/widget/custom/CustomWidgetManager.java
@@ -16,7 +16,7 @@
package com.android.launcher3.widget.custom;
-import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
+import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -29,12 +29,12 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.systemui.plugins.CustomWidgetPlugin;
import com.android.systemui.plugins.PluginListener;
diff --git a/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
new file mode 100644
index 0000000..09517e1
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListBaseEntry.java
@@ -0,0 +1,60 @@
+/*
+ * 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.model;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import androidx.annotation.IntDef;
+
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.lang.annotation.Retention;
+
+/** Holder class to store the package information of an entry shown in the widgets list. */
+public abstract class WidgetsListBaseEntry {
+ public final PackageItemInfo mPkgItem;
+
+ /**
+ * Character that is used as a section name for the {@link ItemInfo#title}.
+ * (e.g., "G" will be stored if title is "Google")
+ */
+ public final String mTitleSectionName;
+
+ public WidgetsListBaseEntry(PackageItemInfo pkgItem, String titleSectionName) {
+ mPkgItem = pkgItem;
+ mTitleSectionName = titleSectionName;
+ }
+
+ /**
+ * Returns the ranking of this entry in the
+ * {@link com.android.launcher3.widget.picker.WidgetsListAdapter}.
+ *
+ * <p>Entries with smaller value should be shown first. See
+ * {@link com.android.launcher3.widget.picker.WidgetsDiffReporter} for more details.
+ */
+ @Rank
+ public abstract int getRank();
+
+ @Retention(SOURCE)
+ @IntDef({RANK_WIDGETS_LIST_HEADER, RANK_WIDGETS_LIST_CONTENT})
+ public @interface Rank {
+ }
+
+ public static final int RANK_WIDGETS_LIST_HEADER = 1;
+ public static final int RANK_WIDGETS_LIST_CONTENT = 2;
+}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
new file mode 100644
index 0000000..b0cb8c7
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListContentEntry.java
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+package com.android.launcher3.widget.model;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.widget.WidgetItemComparator;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Holder class to store all the information related to a list of widgets from the same app which is
+ * shown in the {@link com.android.launcher3.widget.picker.WidgetsFullSheet}.
+ */
+public final class WidgetsListContentEntry extends WidgetsListBaseEntry {
+
+ public final List<WidgetItem> mWidgets;
+
+ public WidgetsListContentEntry(PackageItemInfo pkgItem, String titleSectionName,
+ List<WidgetItem> items) {
+ super(pkgItem, titleSectionName);
+ this.mWidgets =
+ items.stream().sorted(new WidgetItemComparator()).collect(Collectors.toList());
+ }
+
+ @Override
+ public String toString() {
+ return mPkgItem.packageName + ":" + mWidgets.size();
+ }
+
+ @Override
+ @Rank
+ public int getRank() {
+ return RANK_WIDGETS_LIST_CONTENT;
+ }
+}
diff --git a/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
new file mode 100644
index 0000000..6899647
--- /dev/null
+++ b/src/com/android/launcher3/widget/model/WidgetsListHeaderEntry.java
@@ -0,0 +1,64 @@
+/*
+ * 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.model;
+
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.Collection;
+
+/** An information holder for an app which has widgets or/and shortcuts. */
+public final class WidgetsListHeaderEntry extends WidgetsListBaseEntry {
+
+ public final int widgetsCount;
+ public final int shortcutsCount;
+
+ private boolean mIsWidgetListShown = false;
+ private boolean mHasEntryUpdated = false;
+
+ public WidgetsListHeaderEntry(PackageItemInfo pkgItem, String titleSectionName,
+ Collection<WidgetItem> items) {
+ super(pkgItem, titleSectionName);
+ widgetsCount = (int) items.stream().filter(item -> item.widgetInfo != null).count();
+ shortcutsCount = Math.max(0, items.size() - widgetsCount);
+ }
+
+ /** Sets if the widgets list associated with this header is shown. */
+ public void setIsWidgetListShown(boolean isWidgetListShown) {
+ if (mIsWidgetListShown != isWidgetListShown) {
+ this.mIsWidgetListShown = isWidgetListShown;
+ mHasEntryUpdated = true;
+ } else {
+ mHasEntryUpdated = false;
+ }
+ }
+
+ /** Returns {@code true} if the widgets list associated with this header is shown. */
+ public boolean isWidgetListShown() {
+ return mIsWidgetListShown;
+ }
+
+ /** Returns {@code true} if this entry has been updated due to user interactions. */
+ public boolean hasEntryUpdated() {
+ return mHasEntryUpdated;
+ }
+
+ @Override
+ @Rank
+ public int getRank() {
+ return RANK_WIDGETS_LIST_HEADER;
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
new file mode 100644
index 0000000..95fa05f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -0,0 +1,188 @@
+/*
+ * 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.view.View;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.RelativeLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.widget.picker.WidgetsFullSheet.SearchAndRecommendationViewHolder;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
+
+/**
+ * A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
+ * vertical displacement upon scrolling.
+ */
+final class SearchAndRecommendationsScrollController implements
+ RecyclerViewFastScroller.OnFastScrollChangeListener {
+ private final boolean mHasWorkProfile;
+ private final SearchAndRecommendationViewHolder mViewHolder;
+ private final WidgetsRecyclerView mPrimaryRecyclerView;
+
+ // The following are only non null if mHasWorkProfile is true.
+ @Nullable private final WidgetsRecyclerView mWorkRecyclerView;
+ @Nullable private final View mPrimaryWorkTabsView;
+ @Nullable private final PersonalWorkPagedView mPrimaryWorkViewPager;
+
+ private WidgetsRecyclerView mCurrentRecyclerView;
+ private int mMaxCollapsibleHeight = 0;
+
+ SearchAndRecommendationsScrollController(
+ boolean hasWorkProfile,
+ SearchAndRecommendationViewHolder viewHolder,
+ WidgetsRecyclerView primaryRecyclerView,
+ @Nullable WidgetsRecyclerView workRecyclerView,
+ @Nullable View personalWorkTabsView,
+ @Nullable PersonalWorkPagedView primaryWorkViewPager) {
+ mHasWorkProfile = hasWorkProfile;
+ mViewHolder = viewHolder;
+ mPrimaryRecyclerView = primaryRecyclerView;
+ mWorkRecyclerView = workRecyclerView;
+ mPrimaryWorkTabsView = personalWorkTabsView;
+ mPrimaryWorkViewPager = primaryWorkViewPager;
+ mCurrentRecyclerView = mPrimaryRecyclerView;
+ }
+
+ /** Sets the current active {@link WidgetsRecyclerView}. */
+ public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
+ mCurrentRecyclerView = currentRecyclerView;
+ }
+
+ /**
+ * Updates the margin and padding of {@link WidgetsFullSheet} to accumulate collapsible views.
+ */
+ public void updateMarginAndPadding() {
+ // The maximum vertical distance, in pixels, until the last collapsible element is not
+ // visible from the screen when the user scrolls down the recycler view.
+ mMaxCollapsibleHeight = mViewHolder.mContainer.getPaddingTop()
+ + measureHeightWithVerticalMargins(mViewHolder.mCollapseHandle)
+ + measureHeightWithVerticalMargins(mViewHolder.mHeaderTitle);
+
+ int topContainerHeight = measureHeightWithVerticalMargins(mViewHolder.mContainer);
+ if (mHasWorkProfile) {
+ // In a work profile setup, the full widget sheet contains the following views:
+ // ------- -|
+ // Widgets -|---> LinearLayout for search & recommendations
+ // Search bar -|
+ // Personal | Work
+ // View Pager
+ //
+ // Views after the search & recommendations are not bound by RelativelyLayout param.
+ // To position them on the expected location, padding & margin are added to these views
+
+ // Tabs should have a padding of the height of the search & recommendations container.
+ mPrimaryWorkTabsView.setPadding(
+ mPrimaryWorkTabsView.getPaddingLeft(),
+ topContainerHeight,
+ mPrimaryWorkTabsView.getPaddingRight(),
+ mPrimaryWorkTabsView.getPaddingBottom());
+
+ // Instead of setting the top offset directly, we split the top offset into two values:
+ // 1. topOffsetAfterAllViewsCollapsed: this is the top offset after all collapsible
+ // views are no longer visible on the screen.
+ // This value is set as the margin for the view pager.
+ // 2. mMaxCollapsibleDistance
+ // This value is set as the padding for the recycler views in order to work with
+ // clipToPadding="false", which is an attribute for not showing top / bottom padding
+ // when a recycler view has not reached the top or bottom of the list.
+ // e.g. a list of 10 entries, only 3 entries are visible at a time.
+ // case 1: recycler view is scrolled to the top. Top padding is visible/
+ // (top padding)
+ // item 1
+ // item 2
+ // item 3
+ //
+ // case 2: recycler view is scrolled to the middle. No padding is visible.
+ // item 4
+ // item 5
+ // item 6
+ //
+ // case 3: recycler view is scrolled to the end. bottom padding is visible.
+ // item 8
+ // item 9
+ // item 10
+ // (bottom padding): not set in this case.
+ //
+ // When the views are first inflated, the sum of topOffsetAfterAllViewsCollapsed and
+ // mMaxCollapsibleDistance should equal to the top container height.
+ int tabsViewActualHeight = measureHeightWithVerticalMargins(mPrimaryWorkTabsView)
+ - mPrimaryWorkTabsView.getPaddingTop();
+ int topOffsetAfterAllViewsCollapsed =
+ topContainerHeight + tabsViewActualHeight - mMaxCollapsibleHeight;
+
+ RelativeLayout.LayoutParams layoutParams =
+ (RelativeLayout.LayoutParams) mPrimaryWorkViewPager.getLayoutParams();
+ layoutParams.setMargins(0, topOffsetAfterAllViewsCollapsed, 0, 0);
+ mPrimaryWorkViewPager.setLayoutParams(layoutParams);
+ mPrimaryWorkViewPager.requestLayout();
+
+ mPrimaryRecyclerView.setPadding(
+ mPrimaryRecyclerView.getPaddingLeft(),
+ mMaxCollapsibleHeight,
+ mPrimaryRecyclerView.getPaddingRight(),
+ mPrimaryRecyclerView.getPaddingBottom());
+ mWorkRecyclerView.setPadding(
+ mWorkRecyclerView.getPaddingLeft(),
+ mMaxCollapsibleHeight,
+ mWorkRecyclerView.getPaddingRight(),
+ mWorkRecyclerView.getPaddingBottom());
+ } else {
+ mPrimaryRecyclerView.setPadding(
+ mPrimaryRecyclerView.getPaddingLeft(),
+ topContainerHeight,
+ mPrimaryRecyclerView.getPaddingRight(),
+ mPrimaryRecyclerView.getPaddingBottom());
+ }
+ }
+
+ /**
+ * Changes the displacement of collapsible views (e.g. title & widget recommendations) and fixed
+ * views (e.g. recycler views, tabs) upon scrolling.
+ */
+ @Override
+ public void onThumbOffsetYChanged(int unused) {
+ // Always use the recycler view offset because fast scroller offset has a different scale.
+ int recyclerViewYOffset = mCurrentRecyclerView.getCurrentScrollY();
+ if (recyclerViewYOffset < 0) return;
+ if (mMaxCollapsibleHeight > 0) {
+ int yDisplacement = Math.max(-recyclerViewYOffset, -mMaxCollapsibleHeight);
+ mViewHolder.mHeaderTitle.setTranslationY(yDisplacement);
+ mViewHolder.mSearchBar.setTranslationY(yDisplacement);
+ if (mHasWorkProfile) {
+ mPrimaryWorkTabsView.setTranslationY(yDisplacement);
+ }
+ }
+ }
+
+ /** Resets any previous view translation. */
+ public void reset() {
+ mViewHolder.mHeaderTitle.setTranslationY(0);
+ mViewHolder.mSearchBar.setTranslationY(0);
+ if (mHasWorkProfile) {
+ mPrimaryWorkTabsView.setTranslationY(0);
+ }
+ }
+
+ /** private the height, in pixel, + the vertical margins of a given view. */
+ private static int measureHeightWithVerticalMargins(View view) {
+ MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
+ return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+ + marginLayoutParams.topMargin;
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
new file mode 100644
index 0000000..dbd1bdf
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsDiffReporter.java
@@ -0,0 +1,189 @@
+/*
+ * 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.widget.picker;
+
+import android.util.Log;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.model.data.PackageItemInfo;
+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.picker.WidgetsListAdapter.WidgetListBaseRowEntryComparator;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Do diff on widget's tray list items and call the {@link RecyclerView.Adapter}
+ * methods accordingly.
+ */
+public class WidgetsDiffReporter {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "WidgetsDiffReporter";
+
+ private final IconCache mIconCache;
+ private final RecyclerView.Adapter mListener;
+
+ public WidgetsDiffReporter(IconCache iconCache, RecyclerView.Adapter listener) {
+ mIconCache = iconCache;
+ mListener = listener;
+ }
+
+ /**
+ * Notifies the difference between {@code currentEntries} & {@code newEntries} by calling the
+ * relevant {@link androidx.recyclerview.widget.RecyclerView.RecyclerViewDataObserver} methods.
+ */
+ public void process(ArrayList<WidgetsListBaseEntry> currentEntries,
+ List<WidgetsListBaseEntry> newEntries,
+ WidgetListBaseRowEntryComparator comparator) {
+ if (DEBUG) {
+ Log.d(TAG, "process oldEntries#=" + currentEntries.size()
+ + " newEntries#=" + newEntries.size());
+ }
+ // Early exit if either of the list is empty
+ if (currentEntries.isEmpty() || newEntries.isEmpty()) {
+ // Skip if both list are empty.
+ // On rotation, we open the widget tray with empty. Then try to fetch the list again
+ // when the animation completes (which still gives empty). And we get the final result
+ // when the bind actually completes.
+ if (currentEntries.size() != newEntries.size()) {
+ currentEntries.clear();
+ currentEntries.addAll(newEntries);
+ mListener.notifyDataSetChanged();
+ }
+ return;
+ }
+ ArrayList<WidgetsListBaseEntry> orgEntries =
+ (ArrayList<WidgetsListBaseEntry>) currentEntries.clone();
+ Iterator<WidgetsListBaseEntry> orgIter = orgEntries.iterator();
+ Iterator<WidgetsListBaseEntry> newIter = newEntries.iterator();
+
+ WidgetsListBaseEntry orgRowEntry = orgIter.next();
+ WidgetsListBaseEntry newRowEntry = newIter.next();
+
+ do {
+ int diff = compareAppNameAndType(orgRowEntry, newRowEntry, comparator);
+ if (DEBUG) {
+ Log.d(TAG, String.format("diff=%d orgRowEntry (%s) newRowEntry (%s)",
+ diff, orgRowEntry != null ? orgRowEntry.toString() : null,
+ newRowEntry != null ? newRowEntry.toString() : null));
+ }
+ int index = -1;
+ if (diff < 0) {
+ index = currentEntries.indexOf(orgRowEntry);
+ mListener.notifyItemRemoved(index);
+ if (DEBUG) {
+ Log.d(TAG, String.format("notifyItemRemoved called (%d)%s", index,
+ orgRowEntry.mTitleSectionName));
+ }
+ currentEntries.remove(index);
+ orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+ } else if (diff > 0) {
+ index = orgRowEntry != null ? currentEntries.indexOf(orgRowEntry)
+ : currentEntries.size();
+ currentEntries.add(index, newRowEntry);
+ if (DEBUG) {
+ Log.d(TAG, String.format("notifyItemInserted called (%d)%s", index,
+ newRowEntry.mTitleSectionName));
+ }
+ newRowEntry = newIter.hasNext() ? newIter.next() : null;
+ mListener.notifyItemInserted(index);
+
+ } else {
+ // same app name & type but,
+ // did the icon, title, etc, change?
+ // or did the header view changed due to user interactions?
+ // or did the widget size and desc, span, etc change?
+ if (!isSamePackageItemInfo(orgRowEntry.mPkgItem, newRowEntry.mPkgItem)
+ || hasHeaderUpdated(newRowEntry)
+ || hasWidgetsListChanged(orgRowEntry, newRowEntry)) {
+ index = currentEntries.indexOf(orgRowEntry);
+ currentEntries.set(index, newRowEntry);
+ mListener.notifyItemChanged(index);
+ if (DEBUG) {
+ Log.d(TAG, String.format("notifyItemChanged called (%d)%s", index,
+ newRowEntry.mTitleSectionName));
+ }
+ }
+ orgRowEntry = orgIter.hasNext() ? orgIter.next() : null;
+ newRowEntry = newIter.hasNext() ? newIter.next() : null;
+ }
+ } while(orgRowEntry != null || newRowEntry != null);
+ }
+
+ /**
+ * Compares the app name and then entry type for the given {@link WidgetsListBaseEntry}s.
+ *
+ * @Return 0 if both entries' order is the same. Negative integer if {@code newRowEntry} should
+ * order before {@code orgRowEntry}. Positive integer if {@code orgRowEntry} should
+ * order before {@code newRowEntry}.
+ */
+ private int compareAppNameAndType(WidgetsListBaseEntry curRow, WidgetsListBaseEntry newRow,
+ WidgetListBaseRowEntryComparator comparator) {
+ if (curRow == null && newRow == null) {
+ throw new IllegalStateException(
+ "Cannot compare PackageItemInfo if both rows are null.");
+ }
+
+ if (curRow == null && newRow != null) {
+ return 1; // new row needs to be inserted
+ } else if (curRow != null && newRow == null) {
+ return -1; // old row needs to be deleted
+ }
+ int diff = comparator.compare(curRow, newRow);
+ if (diff == 0) {
+ return newRow.getRank() - curRow.getRank();
+ }
+ return diff;
+ }
+
+ /**
+ * Returns {@code true} if both {@code curRow} & {@code newRow} are
+ * {@link WidgetsListContentEntry}s with a different list of widgets.
+ */
+ private boolean hasWidgetsListChanged(WidgetsListBaseEntry curRow,
+ WidgetsListBaseEntry newRow) {
+ if (!(curRow instanceof WidgetsListContentEntry)
+ || !(newRow instanceof WidgetsListContentEntry)) {
+ return false;
+ }
+ WidgetsListContentEntry orgRowEntry = (WidgetsListContentEntry) curRow;
+ WidgetsListContentEntry newRowEntry = (WidgetsListContentEntry) newRow;
+ return !orgRowEntry.mWidgets.equals(newRowEntry.mWidgets);
+ }
+
+ /**
+ * Returns {@code true} if {@code newRow} is {@link WidgetsListHeaderEntry} and its content has
+ * been changed due to user interactions.
+ */
+ private boolean hasHeaderUpdated(WidgetsListBaseEntry newRow) {
+ if (!(newRow instanceof WidgetsListHeaderEntry)) {
+ return false;
+ }
+ WidgetsListHeaderEntry newRowEntry = (WidgetsListHeaderEntry) newRow;
+ return newRowEntry.hasEntryUpdated();
+ }
+
+ private boolean isSamePackageItemInfo(PackageItemInfo curInfo, PackageItemInfo newInfo) {
+ return curInfo.bitmap.icon.equals(newInfo.bitmap.icon)
+ && !mIconCache.isDefaultIcon(curInfo.bitmap, curInfo.user);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
new file mode 100644
index 0000000..330175f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -0,0 +1,450 @@
+/*
+ * 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.widget.picker;
+
+import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
+import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.graphics.Rect;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.TopRoundedCornerView;
+import com.android.launcher3.widget.BaseWidgetSheet;
+import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
+import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * Popup for showing the full list of available widgets
+ */
+public class WidgetsFullSheet extends BaseWidgetSheet
+ implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
+ WidgetsRecyclerView.HeaderViewDimensionsProvider {
+
+ private static final long DEFAULT_OPEN_DURATION = 267;
+ private static final long FADE_IN_DURATION = 150;
+ private static final float VERTICAL_START_POSITION = 0.3f;
+
+ private final Rect mInsets = new Rect();
+ private final boolean mHasWorkProfile;
+ private final SparseArray<AdapterHolder> mAdapters = new SparseArray();
+ private final UserHandle mCurrentUser = Process.myUserHandle();
+ private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter = entry ->
+ mCurrentUser.equals(entry.mPkgItem.user);
+ private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
+ mPrimaryWidgetsFilter.negate();
+
+ @Nullable private PersonalWorkPagedView mViewPager;
+ private int mInitialTabsHeight = 0;
+ private View mTabsView;
+ private TextView mNoWidgetsView;
+ private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
+ private SearchAndRecommendationsScrollController mSearchAndRecommendationsScrollController;
+
+ public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
+ mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
+ mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
+ }
+
+ public WidgetsFullSheet(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mContent = findViewById(R.id.container);
+ TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
+
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
+ : R.layout.widgets_full_sheet_recyclerview;
+ layoutInflater.inflate(contentLayoutRes, springLayout, true);
+
+ RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
+ if (mHasWorkProfile) {
+ mViewPager = findViewById(R.id.widgets_view_pager);
+ mViewPager.initParentViews(this);
+ mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
+ mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
+ mTabsView = findViewById(R.id.tabs);
+ findViewById(R.id.tab_personal)
+ .setOnClickListener((View view) -> mViewPager.snapToPage(0));
+ findViewById(R.id.tab_work)
+ .setOnClickListener((View view) -> mViewPager.snapToPage(1));
+ fastScroller.setIsRecyclerViewFirstChildInParent(false);
+ springLayout.addSpringView(R.id.primary_widgets_list_view);
+ springLayout.addSpringView(R.id.work_widgets_list_view);
+ } else {
+ mViewPager = null;
+ springLayout.addSpringView(R.id.primary_widgets_list_view);
+ }
+
+ layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
+ true);
+ springLayout.addSpringView(R.id.search_and_recommendations_container);
+
+ mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
+ findViewById(R.id.search_and_recommendations_container));
+ mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
+ mHasWorkProfile,
+ mSearchAndRecommendationViewHolder,
+ findViewById(R.id.primary_widgets_list_view),
+ mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
+ mTabsView,
+ mViewPager);
+ fastScroller.setOnFastScrollChangeListener(mSearchAndRecommendationsScrollController);
+
+ mNoWidgetsView = findViewById(R.id.no_widgets_text);
+
+ onWidgetsBound();
+ }
+
+ @Override
+ public void onActivePageChanged(int currentActivePage) {
+ AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage);
+ WidgetsRecyclerView currentRecyclerView = currentAdapterHolder.mWidgetsRecyclerView;
+ currentRecyclerView.bindFastScrollbar();
+ mSearchAndRecommendationsScrollController.setCurrentRecyclerView(currentRecyclerView);
+
+ updateNoWidgetsView(currentAdapterHolder);
+
+ reset();
+ }
+
+ private void updateNoWidgetsView(AdapterHolder adapterHolder) {
+ boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.getItemCount() > 0;
+ adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
+
+ // Always resets the text in case this is updated by search.
+ mNoWidgetsView.setText(R.string.no_widgets_available);
+ mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
+ }
+
+ private void reset() {
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
+ if (mHasWorkProfile) {
+ mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
+ }
+ mSearchAndRecommendationsScrollController.reset();
+ }
+
+ @VisibleForTesting
+ public WidgetsRecyclerView getRecyclerView() {
+ if (!mHasWorkProfile || mViewPager.getCurrentPage() == AdapterHolder.PRIMARY) {
+ return mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+ }
+ return mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView;
+ }
+
+ @Override
+ protected Pair<View, String> getAccessibilityTarget() {
+ return Pair.create(getRecyclerView(), getContext().getString(
+ mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mLauncher.getAppWidgetHost().addProviderChangeListener(this);
+ notifyWidgetProvidersChanged();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+
+ setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
+ if (mHasWorkProfile) {
+ setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
+ }
+ if (insets.bottom > 0) {
+ setupNavBarColor();
+ } else {
+ clearNavBarColor();
+ }
+
+ ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
+ requestLayout();
+ }
+
+ private void setBottomPadding(RecyclerView recyclerView, int bottomPadding) {
+ recyclerView.setPadding(
+ recyclerView.getPaddingLeft(),
+ recyclerView.getPaddingTop(),
+ recyclerView.getPaddingRight(),
+ bottomPadding);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
+ int widthUsed;
+ if (mInsets.bottom > 0) {
+ widthUsed = mInsets.left + mInsets.right;
+ } else {
+ Rect padding = deviceProfile.workspacePadding;
+ widthUsed = Math.max(padding.left + padding.right,
+ 2 * (mInsets.left + mInsets.right));
+ }
+
+ int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
+ measureChildWithMargins(mContent, widthMeasureSpec,
+ widthUsed, heightMeasureSpec, heightUsed);
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
+ MeasureSpec.getSize(heightMeasureSpec));
+
+ int paddingPx = 2 * getResources().getDimensionPixelOffset(
+ R.dimen.widget_cell_horizontal_padding);
+ int maxSpansPerRow = getMeasuredWidth() / (deviceProfile.cellWidthPx + paddingPx);
+ mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+ maxSpansPerRow);
+ if (mHasWorkProfile) {
+ mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
+ maxSpansPerRow);
+ }
+
+ if (mInitialTabsHeight == 0 && mTabsView != null) {
+ mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView);
+ }
+
+ mSearchAndRecommendationsScrollController.updateMarginAndPadding();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ int width = r - l;
+ int height = b - t;
+
+ // Content is laid out as center bottom aligned
+ int contentWidth = mContent.getMeasuredWidth();
+ int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
+ mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
+ contentLeft + contentWidth, height);
+
+ setTranslationShift(mTranslationShift);
+ }
+
+ @Override
+ public void notifyWidgetProvidersChanged() {
+ mLauncher.refreshAndBindWidgetsForPackageUser(null);
+ }
+
+ @Override
+ public void onWidgetsBound() {
+ List<WidgetsListBaseEntry> allWidgets = mLauncher.getPopupDataProvider().getAllWidgets();
+
+ AdapterHolder primaryUserAdapterHolder = mAdapters.get(AdapterHolder.PRIMARY);
+ primaryUserAdapterHolder.setup(findViewById(R.id.primary_widgets_list_view));
+ primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
+ updateNoWidgetsView(primaryUserAdapterHolder);
+
+ if (mHasWorkProfile) {
+ AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
+ workUserAdapterHolder.setup(findViewById(R.id.work_widgets_list_view));
+ workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
+ onActivePageChanged(mViewPager.getCurrentPage());
+ }
+ }
+
+ private void open(boolean animate) {
+ if (animate) {
+ if (getPopupContainer().getInsets().bottom > 0) {
+ mContent.setAlpha(0);
+ setTranslationShift(VERTICAL_START_POSITION);
+ }
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator
+ .setDuration(DEFAULT_OPEN_DURATION)
+ .setInterpolator(AnimationUtils.loadInterpolator(
+ getContext(), android.R.interpolator.linear_out_slow_in));
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator.removeListener(this);
+ }
+ });
+ post(() -> {
+ mOpenCloseAnimator.start();
+ mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
+ });
+ } else {
+ setTranslationShift(TRANSLATION_SHIFT_OPENED);
+ post(this::announceAccessibilityChanges);
+ }
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ handleClose(animate, DEFAULT_OPEN_DURATION);
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ // Disable swipe down when recycler view is scrolling
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = false;
+ RecyclerViewFastScroller scroller = getRecyclerView().getScrollbar();
+ if (scroller.getThumbOffsetY() >= 0
+ && getPopupContainer().isEventOverView(scroller, ev)) {
+ mNoIntercept = true;
+ } else if (getPopupContainer().isEventOverView(mContent, ev)) {
+ mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
+ }
+ }
+ return super.onControllerInterceptTouchEvent(ev);
+ }
+
+ /** Shows the {@link WidgetsFullSheet} on the launcher. */
+ public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
+ WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
+ .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+ sheet.attachToContainer();
+ sheet.mIsOpen = true;
+ sheet.open(animate);
+ return sheet;
+ }
+
+ /** Gets the {@link WidgetsRecyclerView} which shows all widgets in {@link WidgetsFullSheet}. */
+ @VisibleForTesting
+ public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
+ return launcher.findViewById(R.id.primary_widgets_list_view);
+ }
+
+ @Override
+ public void addHintCloseAnim(
+ float distanceToMove, Interpolator interpolator, PendingAnimation target) {
+ target.setFloat(getRecyclerView(), VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
+ target.setViewAlpha(getRecyclerView(), 0.5f, interpolator);
+ }
+
+ @Override
+ protected void onCloseComplete() {
+ super.onCloseComplete();
+ AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
+ }
+
+ @Override
+ public int getHeaderViewHeight() {
+ // No need to check work profile here because mInitialTabHeight is always 0 if there is no
+ // work profile.
+ return mInitialTabsHeight
+ + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mContainer);
+ }
+
+ /** private the height, in pixel, + the vertical margins of a given view. */
+ private static int measureHeightWithVerticalMargins(View view) {
+ MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
+ return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
+ + marginLayoutParams.topMargin;
+ }
+
+ /** A holder class for holding adapters & their corresponding recycler view. */
+ private final class AdapterHolder {
+ static final int PRIMARY = 0;
+ static final int WORK = 1;
+
+ private final int mAdapterType;
+ private final WidgetsListAdapter mWidgetsListAdapter;
+
+ private WidgetsRecyclerView mWidgetsRecyclerView;
+
+ AdapterHolder(int adapterType) {
+ mAdapterType = adapterType;
+
+ Context context = getContext();
+ LauncherAppState apps = LauncherAppState.getInstance(context);
+ mWidgetsListAdapter = new WidgetsListAdapter(
+ context,
+ LayoutInflater.from(context),
+ apps.getWidgetCache(),
+ apps.getIconCache(),
+ /* iconClickListener= */ WidgetsFullSheet.this,
+ /* iconLongClickListener= */ WidgetsFullSheet.this);
+ mWidgetsListAdapter.setFilter(
+ mAdapterType == PRIMARY ? mPrimaryWidgetsFilter : mWorkWidgetsFilter);
+ }
+
+ void setup(WidgetsRecyclerView recyclerView) {
+ mWidgetsRecyclerView = recyclerView;
+ mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
+ mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
+ mWidgetsRecyclerView.setEdgeEffectFactory(
+ ((TopRoundedCornerView) mContent).createEdgeEffectFactory());
+ mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
+ }
+ }
+
+ final class SearchAndRecommendationViewHolder {
+ final View mContainer;
+ final View mCollapseHandle;
+ final EditText mSearchBar;
+ final TextView mHeaderTitle;
+
+ SearchAndRecommendationViewHolder(View searchAndRecommendationContainer) {
+ mContainer = searchAndRecommendationContainer;
+ mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
+ mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
+ mHeaderTitle = mContainer.findViewById(R.id.title);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
new file mode 100644
index 0000000..8b49d1e
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -0,0 +1,235 @@
+/*
+ * 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.widget.picker;
+
+import android.content.Context;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.TableRow;
+
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.icons.IconCache;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.util.LabelComparator;
+import com.android.launcher3.widget.WidgetCell;
+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.picker.WidgetsListHeaderViewHolderBinder.OnHeaderClickListener;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+/**
+ * Recycler view adapter for the widget tray.
+ *
+ * <p>This adapter supports view binding of subclasses of {@link WidgetsListBaseEntry}. There are 2
+ * subclasses: {@link WidgetsListHeader} & {@link WidgetsListContentEntry}.
+ * {@link WidgetsListHeader} entries are always visible in the recycler view. At most one
+ * {@link WidgetsListContentEntry} is shown in the recycler view at any time. Clicking a
+ * {@link WidgetsListHeader} will result in expanding / collapsing a corresponding
+ * {@link WidgetsListContentEntry} of the same app.
+ */
+public class WidgetsListAdapter extends Adapter<ViewHolder> implements OnHeaderClickListener {
+
+ private static final String TAG = "WidgetsListAdapter";
+ private static final boolean DEBUG = false;
+
+ /** Uniquely identifies widgets list view type within the app. */
+ private static final int VIEW_TYPE_WIDGETS_LIST = R.layout.widgets_list_row_view;
+ private static final int VIEW_TYPE_WIDGETS_HEADER = R.layout.widgets_list_row_header;
+
+ private final WidgetsDiffReporter mDiffReporter;
+ private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
+ private final WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
+ private final WidgetListBaseRowEntryComparator mRowComparator =
+ new WidgetListBaseRowEntryComparator();
+
+ private List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
+ private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
+ @Nullable private String mWidgetsContentVisiblePackage = null;
+
+ private Predicate<WidgetsListBaseEntry> mHeaderAndSelectedContentFilter = entry ->
+ entry instanceof WidgetsListHeaderEntry
+ || entry.mPkgItem.packageName.equals(mWidgetsContentVisiblePackage);
+ @Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
+
+ public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
+ WidgetPreviewLoader widgetPreviewLoader, IconCache iconCache,
+ OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
+ mDiffReporter = new WidgetsDiffReporter(iconCache, this);
+ mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
+ layoutInflater, iconClickListener, iconLongClickListener, widgetPreviewLoader);
+ mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
+ mViewHolderBinders.put(VIEW_TYPE_WIDGETS_HEADER,
+ new WidgetsListHeaderViewHolderBinder(layoutInflater, this::onHeaderClicked));
+ }
+
+ public void setFilter(Predicate<WidgetsListBaseEntry> filter) {
+ mFilter = filter;
+ }
+
+ /**
+ * Defers applying bitmap on all the {@link WidgetCell} in the {@param rv}.
+ *
+ * @see WidgetCell#setApplyBitmapDeferred(boolean)
+ */
+ public void setApplyBitmapDeferred(boolean isDeferred, RecyclerView rv) {
+ mWidgetsListTableViewHolderBinder.setApplyBitmapDeferred(isDeferred);
+
+ for (int i = rv.getChildCount() - 1; i >= 0; i--) {
+ ViewHolder viewHolder = rv.getChildViewHolder(rv.getChildAt(i));
+ if (viewHolder.getItemViewType() == VIEW_TYPE_WIDGETS_LIST) {
+ WidgetsRowViewHolder holder = (WidgetsRowViewHolder) viewHolder;
+ for (int j = holder.mTableContainer.getChildCount() - 1; j >= 0; j--) {
+ TableRow row = (TableRow) holder.mTableContainer.getChildAt(j);
+ for (int k = row.getChildCount() - 1; k >= 0; k--) {
+ ((WidgetCell) row.getChildAt(k)).setApplyBitmapDeferred(isDeferred);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mVisibleEntries.size();
+ }
+
+ /** Gets the section name for {@link com.android.launcher3.views.RecyclerViewFastScroller}. */
+ public String getSectionName(int pos) {
+ return mVisibleEntries.get(pos).mTitleSectionName;
+ }
+
+ /** Updates the widget list. */
+ public void setWidgets(List<WidgetsListBaseEntry> tempEntries) {
+ mAllEntries = tempEntries.stream().sorted(mRowComparator)
+ .collect(Collectors.toList());
+ updateVisibleEntries();
+ }
+
+ private void updateVisibleEntries() {
+ mAllEntries.forEach(entry -> {
+ if (entry instanceof WidgetsListHeaderEntry) {
+ ((WidgetsListHeaderEntry) entry).setIsWidgetListShown(
+ entry.mPkgItem.packageName.equals(mWidgetsContentVisiblePackage));
+ }
+ });
+ List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
+ .filter(entry -> (mFilter == null || mFilter.test(entry))
+ && mHeaderAndSelectedContentFilter.test(entry))
+ .collect(Collectors.toList());
+ mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int pos) {
+ ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
+ viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos));
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if (DEBUG) {
+ Log.v(TAG, "\nonCreateViewHolder");
+ }
+
+ return mViewHolderBinders.get(viewType).newViewHolder(parent);
+ }
+
+ @Override
+ public void onViewRecycled(ViewHolder holder) {
+ mViewHolderBinders.get(holder.getItemViewType()).unbindViewHolder(holder);
+ }
+
+ @Override
+ public boolean onFailedToRecycleView(ViewHolder holder) {
+ // If child views are animating, then the RecyclerView may choose not to recycle the view,
+ // causing extraneous onCreateViewHolder() calls. It is safe in this case to continue
+ // recycling this view, and take care in onViewRecycled() to cancel any existing
+ // animations.
+ return true;
+ }
+
+ @Override
+ public long getItemId(int pos) {
+ return pos;
+ }
+
+ @Override
+ public int getItemViewType(int pos) {
+ WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
+ if (entry instanceof WidgetsListContentEntry) {
+ return VIEW_TYPE_WIDGETS_LIST;
+ } else if (entry instanceof WidgetsListHeaderEntry) {
+ return VIEW_TYPE_WIDGETS_HEADER;
+ }
+ throw new UnsupportedOperationException("ViewHolderBinder not found for " + entry);
+ }
+
+ @Override
+ public void onHeaderClicked(boolean showWidgets, String expandedPackage) {
+ if (showWidgets) {
+ mWidgetsContentVisiblePackage = expandedPackage;
+ updateVisibleEntries();
+ } else if (expandedPackage.equals(mWidgetsContentVisiblePackage)) {
+ mWidgetsContentVisiblePackage = null;
+ updateVisibleEntries();
+ }
+ }
+
+ /**
+ * Sets the max horizontal spans that are allowed for grouping more than one widgets in a table
+ * row.
+ *
+ * <p>If there is only one widget in a row, that widget horizontal span is allowed to exceed
+ * {@code maxHorizontalSpans}.
+ * <p>Let's say the max horizontal spans is set to 5. Widgets can be grouped in the same row if
+ * their total horizontal spans added don't exceed 5.
+ * Example 1: Row 1: 2x2, 2x3, 1x1. Total horizontal spans is 5. This is okay.
+ * Example 2: Row 1: 2x2, 4x3, 1x1. the total horizontal spans is 7. This is wrong.
+ * 4x3 and 1x1 should be moved to a new row.
+ * Example 3: Row 1: 6x4. This is okay because this is the only item in the row.
+ */
+ public void setMaxHorizontalSpansPerRow(int maxHorizontalSpans) {
+ mWidgetsListTableViewHolderBinder.setMaxSpansPerRow(maxHorizontalSpans);
+ }
+
+ /** Comparator for sorting WidgetListRowEntry based on package title. */
+ public static class WidgetListBaseRowEntryComparator implements
+ Comparator<WidgetsListBaseEntry> {
+
+ private final LabelComparator mComparator = new LabelComparator();
+
+ @Override
+ public int compare(WidgetsListBaseEntry a, WidgetsListBaseEntry b) {
+ return mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
new file mode 100644
index 0000000..070a9aa
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -0,0 +1,220 @@
+/*
+ * 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.FastBitmapDrawable.newIcon;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.graphics.PlaceHolderIconDrawable;
+import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.icons.cache.HandlerRunnable;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.views.ActivityContext;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+/**
+ * A UI represents a header of an app shown in the full widgets tray.
+ *
+ * It is a {@link LinearLayout} which contains an app icon, an app name, a subtitle and a checkbox
+ * which indicates if the widgets content view underneath this header should be shown.
+ */
+public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpdateReceiver {
+
+ private boolean mEnableIconUpdateAnimation = false;
+
+ @Nullable private HandlerRunnable mIconLoadRequest;
+ @Nullable private Drawable mIconDrawable;
+ private final int mIconSize;
+
+ private ImageView mAppIcon;
+ private TextView mTitle;
+ private TextView mSubtitle;
+
+ private CheckBox mExpandToggle;
+ private boolean mIsExpanded = false;
+
+ public WidgetsListHeader(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public WidgetsListHeader(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, /* defStyle= */ 0);
+ }
+
+ public WidgetsListHeader(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ ActivityContext activity = ActivityContext.lookupContext(context);
+ DeviceProfile grid = activity.getDeviceProfile();
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
+ mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
+ grid.iconSizePx);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mAppIcon = findViewById(R.id.app_icon);
+ mTitle = findViewById(R.id.app_title);
+ mSubtitle = findViewById(R.id.app_subtitle);
+ mExpandToggle = findViewById(R.id.toggle);
+ }
+
+ /**
+ * Sets a {@link OnExpansionChangeListener} to get a callback when this app widgets section
+ * expands / collapses.
+ */
+ @UiThread
+ public void setOnExpandChangeListener(
+ @Nullable OnExpansionChangeListener onExpandChangeListener) {
+ // Use the entire touch area of this view to expand / collapse an app widgets section.
+ setOnClickListener(view -> {
+ setExpanded(!mIsExpanded);
+ onExpandChangeListener.onExpansionChange(mIsExpanded);
+ });
+ }
+
+ /** Sets the expand toggle to expand / collapse. */
+ @UiThread
+ public void setExpanded(boolean isExpanded) {
+ this.mIsExpanded = isExpanded;
+ mExpandToggle.setChecked(isExpanded);
+ }
+
+ /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
+ @UiThread
+ public void applyFromItemInfoWithIcon(WidgetsListHeaderEntry entry) {
+ applyIconAndLabel(entry);
+ }
+
+ @UiThread
+ private void applyIconAndLabel(WidgetsListHeaderEntry entry) {
+ PackageItemInfo info = entry.mPkgItem;
+ setIcon(info);
+ setTitles(entry);
+ setExpanded(entry.isWidgetListShown());
+
+ super.setTag(info);
+
+ verifyHighRes();
+ }
+
+ private void setIcon(PackageItemInfo info) {
+ FastBitmapDrawable icon = newIcon(getContext(), info);
+ applyDrawables(icon);
+ mIconDrawable = icon;
+ if (mIconDrawable != null) {
+ mIconDrawable.setVisible(
+ /* visible= */ getWindowVisibility() == VISIBLE && isShown(),
+ /* restart= */ false);
+ }
+ }
+
+ private void applyDrawables(Drawable icon) {
+ icon.setBounds(0, 0, mIconSize, mIconSize);
+
+ mAppIcon.setImageDrawable(icon);
+
+ // If the current icon is a placeholder color, animate its update.
+ if (mIconDrawable != null
+ && mIconDrawable instanceof PlaceHolderIconDrawable
+ && mEnableIconUpdateAnimation) {
+ ((PlaceHolderIconDrawable) mIconDrawable).animateIconUpdate(icon);
+ }
+ }
+
+ private void setTitles(WidgetsListHeaderEntry entry) {
+ mTitle.setText(entry.mPkgItem.title);
+
+ Resources resources = getContext().getResources();
+ if (entry.widgetsCount == 0 && entry.shortcutsCount == 0) {
+ mSubtitle.setVisibility(GONE);
+ return;
+ }
+
+ String subtitle;
+ if (entry.widgetsCount > 0 && entry.shortcutsCount > 0) {
+ String widgetsCount = resources.getQuantityString(R.plurals.widgets_count,
+ entry.widgetsCount, entry.widgetsCount);
+ String shortcutsCount = resources.getQuantityString(R.plurals.shortcuts_count,
+ entry.shortcutsCount, entry.shortcutsCount);
+ subtitle = resources.getString(R.string.widgets_and_shortcuts_count, widgetsCount,
+ shortcutsCount);
+ } else if (entry.widgetsCount > 0) {
+ subtitle = resources.getQuantityString(R.plurals.widgets_count,
+ entry.widgetsCount, entry.widgetsCount);
+ } else {
+ subtitle = resources.getQuantityString(R.plurals.shortcuts_count,
+ entry.shortcutsCount, entry.shortcutsCount);
+ }
+ mSubtitle.setText(subtitle);
+ mSubtitle.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void reapplyItemInfo(ItemInfoWithIcon info) {
+ if (getTag() == info) {
+ mIconLoadRequest = null;
+ mEnableIconUpdateAnimation = true;
+
+ // Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
+ info.bitmap.icon.prepareToDraw();
+
+ setIcon((PackageItemInfo) info);
+
+ mEnableIconUpdateAnimation = false;
+ }
+ }
+
+ /** Verifies that the current icon is high-res otherwise posts a request to load the icon. */
+ public void verifyHighRes() {
+ if (mIconLoadRequest != null) {
+ mIconLoadRequest.cancel();
+ mIconLoadRequest = null;
+ }
+ if (getTag() instanceof ItemInfoWithIcon) {
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ if (info.usingLowResIcon()) {
+ mIconLoadRequest = LauncherAppState.getInstance(getContext()).getIconCache()
+ .updateIconInBackground(this, info);
+ }
+ }
+ }
+
+ /** A listener for the widget section expansion / collapse events. */
+ public interface OnExpansionChangeListener {
+ /** Notifies that the widget section is expanded or collapsed. */
+ void onExpansionChange(boolean isExpanded);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderHolder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderHolder.java
new file mode 100644
index 0000000..d4e1b1c
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderHolder.java
@@ -0,0 +1,32 @@
+/*
+ * 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 androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+/**
+ * A {@link ViewHolder} for {@link WidgetsListHeader} of an app, which renders the app icon, the app
+ * name, label and a button for showing / hiding widgets.
+ */
+public final class WidgetsListHeaderHolder extends ViewHolder {
+ final WidgetsListHeader mWidgetsListHeader;
+
+ public WidgetsListHeaderHolder(WidgetsListHeader view) {
+ super(view);
+
+ mWidgetsListHeader = view;
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
new file mode 100644
index 0000000..ed53e6f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -0,0 +1,61 @@
+/*
+ * 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.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.launcher3.R;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+
+/**
+ * Binds data from {@link WidgetsListHeaderEntry} to UI elements in {@link WidgetsListHeaderHolder}.
+ */
+public final class WidgetsListHeaderViewHolderBinder implements
+ ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
+ private final LayoutInflater mLayoutInflater;
+ private final OnHeaderClickListener mOnHeaderClickListener;
+
+ public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
+ OnHeaderClickListener onHeaderClickListener) {
+ mLayoutInflater = layoutInflater;
+ mOnHeaderClickListener = onHeaderClickListener;
+ }
+
+ @Override
+ public WidgetsListHeaderHolder newViewHolder(ViewGroup parent) {
+ WidgetsListHeader header = (WidgetsListHeader) mLayoutInflater.inflate(
+ R.layout.widgets_list_row_header, parent, false);
+
+ return new WidgetsListHeaderHolder(header);
+ }
+
+ @Override
+ public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data) {
+ WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+ widgetsListHeader.applyFromItemInfoWithIcon(data);
+ widgetsListHeader.setExpanded(data.isWidgetListShown());
+ widgetsListHeader.setOnExpandChangeListener(isExpanded ->
+ mOnHeaderClickListener.onHeaderClicked(isExpanded, data.mPkgItem.packageName));
+ }
+
+ /** A listener to be invoked when {@link WidgetsListHeader} is clicked. */
+ public interface OnHeaderClickListener {
+ /** Calls when {@link WidgetsListHeader} is clicked to show / hide widgets for a package. */
+ void onHeaderClicked(boolean showWidgets, String packageName);
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
new file mode 100644
index 0000000..47fa71a
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -0,0 +1,182 @@
+/*
+ * 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.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.widget.TableLayout;
+import android.widget.TableRow;
+
+import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.widget.WidgetCell;
+import com.android.launcher3.widget.WidgetImageView;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.util.WidgetsTableUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Binds data from {@link WidgetsListContentEntry} to UI elements in {@link WidgetsRowViewHolder}.
+ */
+public final class WidgetsListTableViewHolderBinder
+ implements ViewHolderBinder<WidgetsListContentEntry, WidgetsRowViewHolder> {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "WidgetsListRowViewHolderBinder";
+
+ private int mMaxSpansPerRow = 4;
+ private final LayoutInflater mLayoutInflater;
+ private final int mIndent;
+ private final OnClickListener mIconClickListener;
+ private final OnLongClickListener mIconLongClickListener;
+ private final WidgetPreviewLoader mWidgetPreviewLoader;
+ private boolean mApplyBitmapDeferred = false;
+
+ public WidgetsListTableViewHolderBinder(
+ Context context,
+ LayoutInflater layoutInflater,
+ OnClickListener iconClickListener,
+ OnLongClickListener iconLongClickListener,
+ WidgetPreviewLoader widgetPreviewLoader) {
+ mLayoutInflater = layoutInflater;
+ mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
+ mIconClickListener = iconClickListener;
+ mIconLongClickListener = iconLongClickListener;
+ mWidgetPreviewLoader = widgetPreviewLoader;
+ }
+
+ /**
+ * Defers applying bitmap on all the {@link WidgetCell} at
+ * {@link #bindViewHolder(WidgetsRowViewHolder, WidgetsListContentEntry)} if
+ * {@code applyBitmapDeferred} is {@code true}.
+ */
+ public void setApplyBitmapDeferred(boolean applyBitmapDeferred) {
+ mApplyBitmapDeferred = applyBitmapDeferred;
+ }
+
+ public void setMaxSpansPerRow(int maxSpansPerRow) {
+ mMaxSpansPerRow = maxSpansPerRow;
+ }
+
+ @Override
+ public WidgetsRowViewHolder newViewHolder(ViewGroup parent) {
+ if (DEBUG) {
+ Log.v(TAG, "\nonCreateViewHolder");
+ }
+
+ ViewGroup container = (ViewGroup) mLayoutInflater.inflate(
+ R.layout.widgets_table_container, parent, false);
+
+ // if the end padding is 0, then container view (horizontal scroll view) doesn't respect
+ // the end of the linear layout width + the start padding and doesn't allow scrolling.
+ container.findViewById(R.id.widgets_table).setPaddingRelative(mIndent, 0, 1, 0);
+
+ return new WidgetsRowViewHolder(container);
+ }
+
+ @Override
+ public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry) {
+ TableLayout table = holder.mTableContainer;
+ if (DEBUG) {
+ Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
+ entry.mWidgets.size(), table.getChildCount()));
+ }
+
+ List<ArrayList<WidgetItem>> widgetItemsTable =
+ WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);
+ recycleTableBeforeBinding(table, widgetItemsTable);
+ // Bind the widget items.
+ for (int i = 0; i < widgetItemsTable.size(); i++) {
+ List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ TableRow row = (TableRow) table.getChildAt(i);
+ row.setVisibility(View.VISIBLE);
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.clear();
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ widget.setPreviewSize(widgetItem.spanX, widgetItem.spanY);
+ widget.applyFromCellItem(widgetItem, mWidgetPreviewLoader);
+ widget.setApplyBitmapDeferred(mApplyBitmapDeferred);
+ widget.ensurePreview();
+ widget.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ /**
+ * Adds and hides table rows and columns from {@code table} to ensure there is sufficient room
+ * to display {@code widgetItemsTable}.
+ *
+ * <p>Instead of recreating all UI elements in {@code table}, this function recycles all
+ * existing UI elements. Instead of deleting excessive elements, it hides them.
+ */
+ private void recycleTableBeforeBinding(TableLayout table,
+ List<ArrayList<WidgetItem>> widgetItemsTable) {
+ // Hide extra table rows.
+ for (int i = widgetItemsTable.size(); i < table.getChildCount(); i++) {
+ table.getChildAt(i).setVisibility(View.GONE);
+ }
+
+ for (int i = 0; i < widgetItemsTable.size(); i++) {
+ List<WidgetItem> widgetItems = widgetItemsTable.get(i);
+ TableRow tableRow;
+ if (i < table.getChildCount()) {
+ tableRow = (TableRow) table.getChildAt(i);
+ } else {
+ tableRow = new TableRow(table.getContext());
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ table.addView(tableRow);
+ }
+ if (tableRow.getChildCount() > widgetItems.size()) {
+ for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+ tableRow.getChildAt(j).setVisibility(View.GONE);
+ }
+ } else {
+ for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+ WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+ R.layout.widget_cell, tableRow, false);
+ // set up touch.
+ WidgetImageView preview = widget.findViewById(R.id.widget_preview);
+ preview.setOnClickListener(mIconClickListener);
+ preview.setOnLongClickListener(mIconLongClickListener);
+ tableRow.addView(widget);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void unbindViewHolder(WidgetsRowViewHolder holder) {
+ int numOfRows = holder.mTableContainer.getChildCount();
+ for (int i = 0; i < numOfRows; i++) {
+ TableRow tableRow = (TableRow) holder.mTableContainer.getChildAt(i);
+ int numOfCols = tableRow.getChildCount();
+ for (int j = 0; j < numOfCols; j++) {
+ WidgetCell widget = (WidgetCell) tableRow.getChildAt(j);
+ widget.clear();
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
similarity index 81%
rename from src/com/android/launcher3/widget/WidgetsRecyclerView.java
rename to src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 82d4110..d65a809 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -14,24 +14,21 @@
* limitations under the License.
*/
-package com.android.launcher3.widget;
+package com.android.launcher3.widget.picker;
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
-import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.testing.TestProtocol;
-
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.R;
+
/**
* The widgets recycler view.
*/
@@ -43,6 +40,7 @@
private final Point mFastScrollerOffset = new Point();
private boolean mTouchDownOnScroller;
+ private HeaderViewDimensionsProvider mHeaderViewDimensionsProvider;
public WidgetsRecyclerView(Context context) {
this(context, null);
@@ -92,7 +90,7 @@
LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
- int posInt = (int) ((touchFraction == 1)? pos -1 : pos);
+ int posInt = (int) ((touchFraction == 1) ? pos - 1 : pos);
return mAdapter.getSectionName(posInt);
}
@@ -138,8 +136,8 @@
@Override
protected int getAvailableScrollHeight() {
View child = getChildAt(0);
- return child.getMeasuredHeight() * mAdapter.getItemCount() - getScrollbarTrackHeight()
- - mScrollbarTop;
+ return child.getMeasuredHeight() * mAdapter.getItemCount() + getScrollBarTop()
+ + getPaddingBottom() - mScrollbar.getHeight();
}
private boolean isModelNotReady() {
@@ -148,7 +146,9 @@
@Override
public int getScrollBarTop() {
- return mScrollbarTop;
+ return mHeaderViewDimensionsProvider == null
+ ? mScrollbarTop
+ : mHeaderViewDimensionsProvider.getHeaderViewHeight() + mScrollbarTop;
}
@Override
@@ -158,7 +158,8 @@
mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
}
if (mTouchDownOnScroller) {
- return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ final boolean result = mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ return result;
}
return false;
}
@@ -173,4 +174,21 @@
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
-}
\ No newline at end of file
+
+ public void setHeaderViewDimensionsProvider(
+ HeaderViewDimensionsProvider headerViewDimensionsProvider) {
+ mHeaderViewDimensionsProvider = headerViewDimensionsProvider;
+ }
+
+ /**
+ * Provides dimensions of the header view that is shown at the top of a
+ * {@link WidgetsRecyclerView}.
+ */
+ public interface HeaderViewDimensionsProvider {
+ /**
+ * Returns the height, in pixels, of the header view that is shown at the top of a
+ * {@link WidgetsRecyclerView}.
+ */
+ int getHeaderViewHeight();
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
similarity index 68%
rename from src/com/android/launcher3/widget/WidgetsRowViewHolder.java
rename to src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index d26edb6..aef1103 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -13,25 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.widget;
+package com.android.launcher3.widget.picker;
import android.view.ViewGroup;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.R;
+import android.widget.TableLayout;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-public class WidgetsRowViewHolder extends ViewHolder {
+import com.android.launcher3.R;
- public final ViewGroup cellContainer;
- public final BubbleTextView title;
+/** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
+public final class WidgetsRowViewHolder extends ViewHolder {
+
+ public final TableLayout mTableContainer;
public WidgetsRowViewHolder(ViewGroup v) {
super(v);
- cellContainer = v.findViewById(R.id.widgets_cell_list);
- title = v.findViewById(R.id.section);
- title.setAccessibilityDelegate(null);
+ mTableContainer = v.findViewById(R.id.widgets_table);
}
}
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
new file mode 100644
index 0000000..15d2454
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchAlgorithm.java
@@ -0,0 +1,63 @@
+/*
+ * 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.search;
+
+import android.os.Handler;
+import android.util.Log;
+
+import com.android.launcher3.search.SearchAlgorithm;
+import com.android.launcher3.search.SearchCallback;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import java.util.ArrayList;
+
+/**
+ * Implementation of {@link SearchAlgorithm} that posts a task to query on the main thread.
+ */
+public final class SimpleWidgetsSearchAlgorithm implements SearchAlgorithm<WidgetsListBaseEntry> {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SimpleWidgetsSearchAlgo";
+ private static final String DELIM = "\t";
+
+ private final Handler mResultHandler;
+ private final WidgetsPickerSearchPipeline mSearchPipeline;
+
+ public SimpleWidgetsSearchAlgorithm(WidgetsPickerSearchPipeline searchPipeline) {
+ mResultHandler = new Handler();
+ mSearchPipeline = searchPipeline;
+ }
+
+ @Override
+ public void doSearch(String query, SearchCallback<WidgetsListBaseEntry> callback) {
+ long startTime = System.currentTimeMillis();
+ String queryToken = query + DELIM + startTime;
+ if (DEBUG) {
+ Log.d(TAG, "doSearch queryToken:" + queryToken);
+ }
+ mSearchPipeline.query(query,
+ results -> mResultHandler.post(
+ () -> callback.onSearchResult(queryToken, new ArrayList(results))));
+ }
+
+ @Override
+ public void cancel(boolean interruptActiveRequests) {
+ if (interruptActiveRequests) {
+ mResultHandler.removeCallbacksAndMessages(/*token= */null);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipeline.java b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipeline.java
new file mode 100644
index 0000000..9911495
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/SimpleWidgetsSearchPipeline.java
@@ -0,0 +1,88 @@
+/*
+ * 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.search;
+
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Implementation of {@link WidgetsPickerSearchPipeline} that performs search by prefix matching on
+ * app names and widget labels.
+ */
+public final class SimpleWidgetsSearchPipeline implements WidgetsPickerSearchPipeline {
+
+ private final List<WidgetsListBaseEntry> mAllEntries;
+
+ public SimpleWidgetsSearchPipeline(List<WidgetsListBaseEntry> allEntries) {
+ mAllEntries = allEntries;
+ }
+
+ @Override
+ public void query(String input, Consumer<List<WidgetsListBaseEntry>> callback) {
+ StringMatcher matcher = StringMatcher.getInstance();
+ ArrayList<WidgetsListBaseEntry> results = new ArrayList<>();
+ // TODO(b/157286785): Filter entries based on query prefix matching on widget labels also.
+ for (WidgetsListBaseEntry e : mAllEntries) {
+ if (matcher.matches(input, e.mPkgItem.title.toString())) {
+ results.add(e);
+ }
+ }
+ callback.accept(results);
+ }
+
+ /**
+ * Performs locale sensitive string comparison using {@link Collator}.
+ */
+ public static class StringMatcher {
+
+ private static final char MAX_UNICODE = '\uFFFF';
+
+ private final Collator mCollator;
+
+ StringMatcher() {
+ mCollator = Collator.getInstance();
+ mCollator.setStrength(Collator.PRIMARY);
+ mCollator.setDecomposition(Collator.CANONICAL_DECOMPOSITION);
+ }
+
+ /**
+ * Returns true if {@param query} is a prefix of {@param target}.
+ */
+ public boolean matches(String query, String target) {
+ switch (mCollator.compare(query, target)) {
+ case 0:
+ return true;
+ case -1:
+ // The target string can contain a modifier which would make it larger than
+ // the query string (even though the length is same). If the query becomes
+ // larger after appending a unicode character, it was originally a prefix of
+ // the target string and hence should match.
+ return mCollator.compare(query + MAX_UNICODE, target) > -1;
+ default:
+ return false;
+ }
+ }
+
+ public static StringMatcher getInstance() {
+ return new StringMatcher();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsPickerSearchPipeline.java b/src/com/android/launcher3/widget/picker/search/WidgetsPickerSearchPipeline.java
new file mode 100644
index 0000000..d12782c
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsPickerSearchPipeline.java
@@ -0,0 +1,44 @@
+/*
+ * 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.search;
+
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * An interface for a pipeline to handle widgets search.
+ */
+public interface WidgetsPickerSearchPipeline {
+
+ /**
+ * Performs a search query asynchronically. Invokes {@code callback} when the search is
+ * complete.
+ */
+ void query(String input, Consumer<List<WidgetsListBaseEntry>> callback);
+
+ /**
+ * Cancels any ongoing search request.
+ */
+ default void cancel() {};
+
+ /**
+ * Cleans up after search is no longer needed.
+ */
+ default void destroy() {};
+}
diff --git a/src/com/android/launcher3/widget/util/WidgetsTableUtils.java b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
new file mode 100644
index 0000000..e73d661
--- /dev/null
+++ b/src/com/android/launcher3/widget/util/WidgetsTableUtils.java
@@ -0,0 +1,92 @@
+/*
+ * 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.util;
+
+import com.android.launcher3.model.WidgetItem;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** An utility class which groups {@link WidgetItem}s into a table. */
+public final class WidgetsTableUtils {
+
+ /**
+ * Groups widgets in the following order:
+ * 1. Widgets always go before shortcuts.
+ * 2. Widgets with smaller horizontal spans will be shown first.
+ * 3. If widgets have the same horizontal spans, then widgets with a smaller vertical spans will
+ * go first.
+ * 4. If both widgets have the same horizontal and vertical spans, they will use the same order
+ * from the given {@code widgetItems}.
+ */
+ private static final Comparator<WidgetItem> WIDGET_SHORTCUT_COMPARATOR = (item, otherItem) -> {
+ if (item.widgetInfo != null && otherItem.widgetInfo == null) return -1;
+
+ if (item.widgetInfo == null && otherItem.widgetInfo != null) return 1;
+ if (item.spanX == otherItem.spanX) {
+ if (item.spanY == otherItem.spanY) return 0;
+ return item.spanY > otherItem.spanY ? 1 : -1;
+ }
+ return item.spanX > otherItem.spanX ? 1 : -1;
+ };
+
+
+ /**
+ * Groups widgets items into a 2D array which matches their appearance in a UI table.
+ *
+ * <p>Grouping:
+ * 1. Widgets and shortcuts never group together in the same row.
+ * 2. The ordered widgets are grouped together in the same row until their total horizontal
+ * spans exceed the {@code maxSpansPerRow}.
+ * 3. The order shortcuts are grouped together in the same row until their total horizontal
+ * spans exceed the {@code maxSpansPerRow}.
+ */
+ public static List<ArrayList<WidgetItem>> groupWidgetItemsIntoTable(
+ List<WidgetItem> widgetItems, final int maxSpansPerRow) {
+ List<WidgetItem> sortedWidgetItems = widgetItems.stream().sorted(WIDGET_SHORTCUT_COMPARATOR)
+ .collect(Collectors.toList());
+ List<ArrayList<WidgetItem>> widgetItemsTable = new ArrayList<>();
+ ArrayList<WidgetItem> widgetItemsAtRow = null;
+ for (WidgetItem widgetItem : sortedWidgetItems) {
+ if (widgetItemsAtRow == null) {
+ widgetItemsAtRow = new ArrayList<>();
+ widgetItemsTable.add(widgetItemsAtRow);
+ }
+ int numOfWidgetItems = widgetItemsAtRow.size();
+ int totalHorizontalSpan = widgetItemsAtRow.stream().map(item -> item.spanX)
+ .reduce(/* default= */ 0, Integer::sum);
+ if (numOfWidgetItems == 0) {
+ widgetItemsAtRow.add(widgetItem);
+ } else if (widgetItem.spanX + totalHorizontalSpan <= maxSpansPerRow
+ && widgetItem.hasSameType(widgetItemsAtRow.get(numOfWidgetItems - 1))) {
+ // Group items in the same row if
+ // 1. they are with the same type, i.e. a row can only have widgets or shortcuts but
+ // never a mix of both.
+ // 2. the total number of horizontal spans are smaller than or equal to
+ // MAX_SPAN_PER_ROW. If an item has a horizontal span > MAX_SPAN_PER_ROW, we just
+ // place it in its own row regardless of the horizontal span limit.
+ widgetItemsAtRow.add(widgetItem);
+ } else {
+ widgetItemsAtRow = new ArrayList<>();
+ widgetItemsTable.add(widgetItemsAtRow);
+ widgetItemsAtRow.add(widgetItem);
+ }
+ }
+ return widgetItemsTable;
+ }
+}
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkPagedView.java b/src/com/android/launcher3/workprofile/PersonalWorkPagedView.java
new file mode 100644
index 0000000..8b05a0d
--- /dev/null
+++ b/src/com/android/launcher3/workprofile/PersonalWorkPagedView.java
@@ -0,0 +1,92 @@
+/*
+ * 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.workprofile;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.launcher3.PagedView;
+
+/**
+ * A {@link PagedView} for showing different views for the personal and work profile respectively.
+ */
+public class PersonalWorkPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
+
+ static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
+ static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
+ static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
+
+ public PersonalWorkPagedView(Context context) {
+ this(context, null);
+ }
+
+ public PersonalWorkPagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PersonalWorkPagedView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ protected String getCurrentPageDescription() {
+ // Not necessary, tab-bar already has two tabs with their own descriptions.
+ return "";
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ mPageIndicator.setScroll(l, mMaxScroll);
+ }
+
+ @Override
+ protected void determineScrollingStart(MotionEvent ev) {
+ float absDeltaX = Math.abs(ev.getX() - getDownMotionX());
+ float absDeltaY = Math.abs(ev.getY() - getDownMotionY());
+
+ if (Float.compare(absDeltaX, 0f) == 0) return;
+
+ float slope = absDeltaY / absDeltaX;
+ float theta = (float) Math.atan(slope);
+
+ if (absDeltaX > mTouchSlop || absDeltaY > mTouchSlop) {
+ cancelCurrentPageLongPress();
+ }
+
+ if (theta > MAX_SWIPE_ANGLE) {
+ return;
+ } else if (theta > START_DAMPING_TOUCH_SLOP_ANGLE) {
+ theta -= START_DAMPING_TOUCH_SLOP_ANGLE;
+ float extraRatio = (float)
+ Math.sqrt((theta / (MAX_SWIPE_ANGLE - START_DAMPING_TOUCH_SLOP_ANGLE)));
+ super.determineScrollingStart(ev, 1 + TOUCH_SLOP_DAMPING_FACTOR * extraRatio);
+ } else {
+ super.determineScrollingStart(ev);
+ }
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return false;
+ }
+
+ @Override
+ protected boolean canScroll(float absVScroll, float absHScroll) {
+ return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
+ }
+}
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
similarity index 85%
rename from src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
rename to src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index 2de425e..3a3028f 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.workprofile;
import android.content.Context;
import android.graphics.Canvas;
@@ -35,9 +35,6 @@
* Supports two indicator colors, dedicated for personal and work tabs.
*/
public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageIndicator {
- private static final int POSITION_PERSONAL = 0;
- private static final int POSITION_WORK = 1;
-
private final Paint mSelectedIndicatorPaint;
private final Paint mDividerPaint;
@@ -47,7 +44,7 @@
private float mScrollOffset;
private int mSelectedPosition = 0;
- private AllAppsContainerView mContainerView;
+ private OnActivePageChangedListener mOnActivePageChangedListener;
private int mLastActivePage = 0;
private boolean mIsRtl;
@@ -123,7 +120,7 @@
float y = getHeight() - mDividerPaint.getStrokeWidth();
canvas.drawLine(getPaddingLeft(), y, getWidth() - getPaddingRight(), y, mDividerPaint);
canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
- mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
+ mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
}
@Override
@@ -135,15 +132,15 @@
@Override
public void setActiveMarker(int activePage) {
updateTabTextColor(activePage);
- if (mContainerView != null && mLastActivePage != activePage) {
+ if (mOnActivePageChangedListener != null && mLastActivePage != activePage) {
updateIndicatorPosition(activePage);
- mContainerView.onTabChanged(activePage);
+ mOnActivePageChangedListener.onActivePageChanged(activePage);
}
mLastActivePage = activePage;
}
- public void setContainerView(AllAppsContainerView containerView) {
- mContainerView = containerView;
+ public void setOnActivePageChangedListener(OnActivePageChangedListener listener) {
+ mOnActivePageChangedListener = listener;
}
@Override
@@ -153,4 +150,12 @@
public boolean hasOverlappingRendering() {
return false;
}
+
+ /**
+ * Interface definition for a callback to be invoked when an active page has been changed.
+ */
+ public interface OnActivePageChangedListener {
+ /** Called when the active page has been changed. */
+ void onActivePageChanged(int currentActivePage);
+ }
}
diff --git a/src_build_config/BuildConfig.java b/src_build_config/com/android/launcher3/BuildConfig.java
similarity index 100%
rename from src_build_config/BuildConfig.java
rename to src_build_config/com/android/launcher3/BuildConfig.java
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
deleted file mode 100644
index c57f07d..0000000
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ /dev/null
@@ -1,50 +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.systemui.plugins;
-
-import android.app.Activity;
-import android.view.ViewGroup;
-import android.widget.EditText;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this plugin interface to replace the all apps recycler view of the all apps drawer.
- */
-@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
-public interface AllAppsSearchPlugin extends Plugin {
- String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
- int VERSION = 3;
-
- /** Following are the order that these methods should be called. */
- void setup(ViewGroup parent, Activity activity, float allAppsContainerHeight);
-
- /**
- * When drag starts, pass window inset related fields and the progress to indicate
- * whether user is swiping down or swiping up
- */
- void onDragStart(float progress);
-
- /** progress is between [0, 1] 1: down, 0: up */
- void setProgress(float progress);
-
- /** Called when container animation stops, so that plugin can perform cleanups */
- void onAnimationEnd(float progress);
-
- /** pass over the search box object */
- void setEditText(EditText editText);
-}
diff --git a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java b/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java
deleted file mode 100644
index 15a0ffa..0000000
--- a/src_plugins/com/android/systemui/plugins/AppLaunchEventsPlugin.java
+++ /dev/null
@@ -1,54 +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.systemui.plugins;
-
-import android.content.ComponentName;
-import android.os.UserHandle;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Plugin interface which sends app launch events.
- */
-@ProvidesInterface(action = AppLaunchEventsPlugin.ACTION, version = AppLaunchEventsPlugin.VERSION)
-public interface AppLaunchEventsPlugin extends Plugin {
- String ACTION = "com.android.systemui.action.PLUGIN_APP_EVENTS";
- int VERSION = 1;
-
- /**
- * Receives onStartShortcut event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onStartShortcut(String packageName, String shortcutId, UserHandle user, String container);
-
- /**
- * Receives onStartApp event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onStartApp(ComponentName componentName, UserHandle user, String container);
-
- /**
- * Receives onDismissApp event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onDismissApp(ComponentName componentName, UserHandle user, String container);
-
- /**
- * Receives onReturnedToHome event from
- * {@link com.android.launcher3.appprediction.PredictionAppTracker}.
- */
- void onReturnedToHome();
-}
diff --git a/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..f8a9a04
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * 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.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+ String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+ int VERSION = 1;
+
+ /** Register a listener to get Smartspace data. */
+ void registerListener(SmartspaceTargetListener listener);
+
+ /** Unregister a listener. */
+ void unregisterListener(SmartspaceTargetListener listener);
+
+ /** Provides Smartspace data to registered listeners. */
+ interface SmartspaceTargetListener {
+ /** Each Parcelable is a SmartspaceTarget that represents a card. */
+ void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
+ }
+}
diff --git a/src_plugins/com/android/systemui/plugins/OWNERS b/src_plugins/com/android/systemui/plugins/OWNERS
deleted file mode 100644
index 0514999..0000000
--- a/src_plugins/com/android/systemui/plugins/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# When changing interface for this plugin OR when increasing version code, please add Alex
-# Only add other owners if Alex is not available
-per-file AllAppsSearchPlugin.java, globs = set noparent
-per-file AllAppsSearchPlugin.java = alexmang@google.com, hyunyoungs@google.com, sunnygoyal@google.com, twickham@google.com
diff --git a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java b/src_plugins/com/android/systemui/plugins/UserEventPlugin.java
deleted file mode 100644
index 0e3664a..0000000
--- a/src_plugins/com/android/systemui/plugins/UserEventPlugin.java
+++ /dev/null
@@ -1,34 +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.systemui.plugins;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-
-/**
- * Implement this plugin interface to access user event log on the device for prototype purpose.
- * NOTE: plugin is for internal prototype only and is not visible in production environment.
- */
-@ProvidesInterface(action = UserEventPlugin.ACTION, version = UserEventPlugin.VERSION)
-public interface UserEventPlugin extends Plugin {
- String ACTION = "com.android.launcher3.action.PLUGIN_USER_EVENT_LOG";
- int VERSION = 1;
-
- /**
- * Callback to be triggered whenever an user event occurs.
- */
- void onUserEvent(Object event);
-}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
index dcb4636..abce2a2 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/LoaderResults.java
@@ -21,11 +21,10 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.model.WidgetsListBaseEntry;
-import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
@@ -34,12 +33,7 @@
public LoaderResults(LauncherAppState app, BgDataModel dataModel,
AllAppsList allAppsList, Callbacks[] callbacks) {
- this(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
- }
-
- public LoaderResults(LauncherAppState app, BgDataModel dataModel,
- AllAppsList allAppsList, Callbacks[] callbacks, LooperExecutor executor) {
- super(app, dataModel, allAppsList, callbacks, executor);
+ super(app, dataModel, allAppsList, callbacks, MAIN_EXECUTOR);
}
@Override
@@ -53,8 +47,8 @@
@Override
public void bindWidgets() {
- final ArrayList<WidgetListRowEntry> widgets =
- mBgDataModel.widgetsModel.getWidgetsList(mApp.getContext());
+ final List<WidgetsListBaseEntry> widgets =
+ mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
}
}
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 9d87788..3ea4766 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -5,11 +5,12 @@
import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
+import static java.util.stream.Collectors.toList;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -18,7 +19,6 @@
import com.android.launcher3.AppFilter;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -26,21 +26,23 @@
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
-import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.widget.WidgetItemComparator;
-import com.android.launcher3.widget.WidgetListRowEntry;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
+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.picker.WidgetsDiffReporter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
@@ -51,38 +53,53 @@
// True is the widget support is disabled.
public static final boolean GO_DISABLE_WIDGETS = false;
+ public static final boolean GO_DISABLE_NOTIFICATION_DOTS = false;
private static final String TAG = "WidgetsModel";
private static final boolean DEBUG = false;
/* Map of widgets and shortcuts that are tracked per package. */
- private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
-
- private AppFilter mAppFilter;
+ private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
/**
- * Returns a list of {@link WidgetListRowEntry}. All {@link WidgetItem} in a single row
- * are sorted (based on label and user), but the overall list of {@link WidgetListRowEntry}s
- * is not sorted. This list is sorted at the UI when using
- * {@link com.android.launcher3.widget.WidgetsDiffReporter}
+ * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row
+ * are sorted (based on label and user), but the overall list of
+ * {@link WidgetsListBaseEntry}s is not sorted. This list is sorted at the UI when using
+ * {@link WidgetsDiffReporter}
*
- * @see com.android.launcher3.widget.WidgetsListAdapter#setWidgets(ArrayList)
+ * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
*/
- public synchronized ArrayList<WidgetListRowEntry> getWidgetsList(Context context) {
- ArrayList<WidgetListRowEntry> result = new ArrayList<>();
+ public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
+ ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
- WidgetItemComparator widgetComparator = new WidgetItemComparator();
- for (Map.Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
- WidgetListRowEntry row = new WidgetListRowEntry(entry.getKey(), entry.getValue());
- row.titleSectionName = (row.pkgItem.title == null) ? "" :
- indexer.computeSectionName(row.pkgItem.title);
- Collections.sort(row.widgets, widgetComparator);
- result.add(row);
+ for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
+ PackageItemInfo pkgItem = entry.getKey();
+ List<WidgetItem> widgetItems = entry.getValue();
+ String sectionName = (pkgItem.title == null) ? "" :
+ indexer.computeSectionName(pkgItem.title);
+ result.add(new WidgetsListHeaderEntry(pkgItem, sectionName, widgetItems));
+ result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
}
return result;
}
+ /** Returns a mapping of packages to their widgets without static shortcuts. */
+ public synchronized Map<PackageUserKey, List<WidgetItem>> getAllWidgetsWithoutShortcuts() {
+ Map<PackageUserKey, List<WidgetItem>> packagesToWidgets = new HashMap<>();
+ mWidgetsList.forEach((packageItemInfo, widgetsAndShortcuts) -> {
+ List<WidgetItem> widgets = widgetsAndShortcuts.stream()
+ .filter(item -> item.widgetInfo != null)
+ .collect(toList());
+ if (widgets.size() > 0) {
+ packagesToWidgets.put(
+ new PackageUserKey(packageItemInfo.packageName, packageItemInfo.user),
+ widgets);
+ }
+ });
+ return packagesToWidgets;
+ }
+
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
@@ -115,7 +132,7 @@
widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache(), pm));
updatedItems.add(info);
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, app);
} catch (Exception e) {
if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -132,90 +149,31 @@
}
private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ LauncherAppState app) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
// Temporary list for {@link PackageItemInfos} to avoid having to go through
// {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
- HashMap<String, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
+ HashMap<PackageUserKey, PackageItemInfo> tmpPackageItemInfos = new HashMap<>();
// clear the lists.
- if (packageUser == null) {
- mWidgetsList.clear();
- } else {
- // Only clear the widgets for the given package/user.
- PackageItemInfo packageItem = null;
- for (PackageItemInfo item : mWidgetsList.keySet()) {
- if (item.packageName.equals(packageUser.mPackageName)) {
- packageItem = item;
- break;
- }
- }
- if (packageItem != null) {
- // We want to preserve the user that was on the packageItem previously,
- // so add it to tmpPackageItemInfos here to avoid creating a new entry.
- tmpPackageItemInfos.put(packageItem.packageName, packageItem);
-
- Iterator<WidgetItem> widgetItemIterator = mWidgetsList.get(packageItem).iterator();
- while (widgetItemIterator.hasNext()) {
- WidgetItem nextWidget = widgetItemIterator.next();
- if (nextWidget.componentName.getPackageName().equals(packageUser.mPackageName)
- && nextWidget.user.equals(packageUser.mUser)) {
- widgetItemIterator.remove();
- }
- }
- }
- }
-
- InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
- UserHandle myUser = Process.myUserHandle();
-
+ mWidgetsList.clear();
// add and update.
- for (WidgetItem item : rawWidgetsShortcuts) {
- if (item.widgetInfo != null) {
- if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
- // Widget is hidden from picker
- continue;
- }
-
- // Ensure that all widgets we show can be added on a workspace of this size
- int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
- int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
- if (minSpanX > idp.numColumns || minSpanY > idp.numRows) {
- if (DEBUG) {
- Log.d(TAG, String.format(
- "Widget %s : (%d X %d) can't fit on this device",
- item.componentName, minSpanX, minSpanY));
+ mWidgetsList.putAll(rawWidgetsShortcuts.stream()
+ .filter(new WidgetValidityCheck(app))
+ .collect(Collectors.groupingBy(item -> {
+ PackageUserKey packageUserKey = new PackageUserKey(
+ item.componentName.getPackageName(), item.user);
+ PackageItemInfo pInfo = tmpPackageItemInfos.get(packageUserKey);
+ if (pInfo == null) {
+ pInfo = new PackageItemInfo(packageUserKey.mPackageName);
+ pInfo.user = item.user;
+ tmpPackageItemInfos.put(packageUserKey, pInfo);
}
- continue;
- }
- }
-
- if (mAppFilter == null) {
- mAppFilter = AppFilter.newInstance(app.getContext());
- }
- if (!mAppFilter.shouldShowApp(item.componentName)) {
- if (DEBUG) {
- Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
- item.componentName));
- }
- continue;
- }
-
- String packageName = item.componentName.getPackageName();
- PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
- if (pInfo == null) {
- pInfo = new PackageItemInfo(packageName);
- pInfo.user = item.user;
- tmpPackageItemInfos.put(packageName, pInfo);
- } else if (!myUser.equals(pInfo.user)) {
- // Keep updating the user, until we get the primary user.
- pInfo.user = item.user;
- }
- mWidgetsList.addToList(pInfo, item);
- }
+ return pInfo;
+ })));
// Update each package entry
IconCache iconCache = app.getIconCache();
@@ -226,9 +184,9 @@
public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
LauncherAppState app) {
- for (Entry<PackageItemInfo, ArrayList<WidgetItem>> entry : mWidgetsList.entrySet()) {
+ for (Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
if (packageNames.contains(entry.getKey().packageName)) {
- ArrayList<WidgetItem> items = entry.getValue();
+ List<WidgetItem> items = entry.getValue();
int count = items.size();
for (int i = 0; i < count; i++) {
WidgetItem item = items.get(i);
@@ -248,7 +206,7 @@
public WidgetItem getWidgetProviderInfoByProviderName(
ComponentName providerName) {
- ArrayList<WidgetItem> widgetsList = mWidgetsList.get(
+ List<WidgetItem> widgetsList = mWidgetsList.get(
new PackageItemInfo(providerName.getPackageName()));
if (widgetsList == null) {
return null;
@@ -261,4 +219,46 @@
}
return null;
}
+
+ private static class WidgetValidityCheck implements Predicate<WidgetItem> {
+
+ private final InvariantDeviceProfile mIdp;
+ private final AppFilter mAppFilter;
+
+ WidgetValidityCheck(LauncherAppState app) {
+ mIdp = app.getInvariantDeviceProfile();
+ mAppFilter = new AppFilter(app.getContext());
+ }
+
+ @Override
+ public boolean test(WidgetItem item) {
+ if (item.widgetInfo != null) {
+ if ((item.widgetInfo.getWidgetFeatures() & WIDGET_FEATURE_HIDE_FROM_PICKER) != 0) {
+ // Widget is hidden from picker
+ return false;
+ }
+
+ // Ensure that all widgets we show can be added on a workspace of this size
+ int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
+ int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
+ if (minSpanX > mIdp.numColumns || minSpanY > mIdp.numRows) {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "Widget %s : (%d X %d) can't fit on this device",
+ item.componentName, minSpanX, minSpanY));
+ }
+ return false;
+ }
+ }
+ if (!mAppFilter.shouldShowApp(item.componentName)) {
+ if (DEBUG) {
+ Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
+ item.componentName));
+ }
+ return false;
+ }
+
+ return true;
+ }
+ }
}
\ No newline at end of file
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
index 5a1f9ca..780a0f0 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/ColorExtractionAlgorithm.java
@@ -21,16 +21,16 @@
import android.util.Pair;
import android.util.Range;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.graphics.ColorUtils;
+
import com.android.launcher3.Utilities;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.graphics.ColorUtils;
-
/**
* Implementation of tonal color extraction
**/
@@ -69,7 +69,7 @@
// palettes. The best fit is tweaked to be closer to the source color
// and replaces the original palette
- // Get the most preeminent, non-blacklisted color.
+ // Get the most preeminent, non-disallowed color.
Integer bestColor = 0;
final float[] hsl = new float[3];
for (int i = 0; i < mainColorsSize; i++) {
@@ -78,7 +78,7 @@
Color.blue(colorValue), hsl);
// Stop when we find a color that meets our criteria
- if (!isBlacklisted(hsl)) {
+ if (!isDisallowed(hsl)) {
bestColor = colorValue;
break;
}
@@ -167,12 +167,12 @@
}
/**
- * Checks if a given color exists in the blacklist
+ * Checks if a given color exists in the disallowed_colors list.
* @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1)
* @return true if color should be avoided
*/
- private boolean isBlacklisted(float[] hsl) {
- for (ColorRange badRange: BLACKLISTED_COLORS) {
+ private boolean isDisallowed(float[] hsl) {
+ for (ColorRange badRange: DISALLOWED_COLORS) {
if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
return true;
}
@@ -592,7 +592,7 @@
);
@SuppressWarnings("WeakerAccess")
- static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
+ static final ColorRange[] DISALLOWED_COLORS = new ColorRange[] {
// Red
new ColorRange(
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
index 0fd0a35..9dbe47c 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompat.java
@@ -17,8 +17,7 @@
package com.android.launcher3.uioverrides.dynamicui;
import android.content.Context;
-
-import com.android.launcher3.Utilities;
+import android.os.Build;
import androidx.annotation.Nullable;
@@ -32,7 +31,7 @@
if (sInstance == null) {
context = context.getApplicationContext();
- if (Utilities.ATLEAST_OREO_MR1) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
try {
sInstance = new WallpaperManagerCompatVOMR1(context);
} catch (Throwable e) {
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 ec3f93f..a4e53a1 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -16,13 +16,13 @@
package com.android.launcher3.uioverrides.states;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_ALLAPPS;
import android.content.Context;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* Definition for AllApps state
@@ -41,7 +41,7 @@
};
public AllAppsState(int id) {
- super(id, ContainerType.ALLAPPS, STATE_FLAGS);
+ super(id, LAUNCHER_STATE_ALLAPPS, STATE_FLAGS);
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index 7a6332c..da5a94f 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -15,10 +15,11 @@
*/
package com.android.launcher3.uioverrides.states;
+import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
+
import android.content.Context;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* Definition for overview state
@@ -26,7 +27,7 @@
public class OverviewState extends LauncherState {
public OverviewState(int id) {
- super(id, ContainerType.WORKSPACE, FLAG_DISABLE_RESTORE);
+ super(id, LAUNCHER_STATE_OVERVIEW, FLAG_DISABLE_RESTORE);
}
@Override
@@ -38,10 +39,6 @@
return new OverviewState(id);
}
- public static OverviewState newPeekState(int id) {
- return new OverviewState(id);
- }
-
public static OverviewState newSwitchState(int id) {
return new OverviewState(id);
}
diff --git a/tests/Android.mk b/tests/Android.mk
index e7e1a49..2c7d30a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -23,6 +23,7 @@
androidx.annotation_annotation \
androidx.test.runner \
androidx.test.rules \
+ androidx.preference_preference \
androidx.test.uiautomator_uiautomator
ifneq (,$(wildcard frameworks/base))
@@ -31,7 +32,6 @@
LOCAL_SRC_FILES := $(call all-java-files-under, tapl) \
../src/com/android/launcher3/ResourceUtils.java \
- ../src/com/android/launcher3/util/SecureSettingsObserver.java \
../src/com/android/launcher3/testing/TestProtocol.java
endif
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 1c8f095..7f6c8f8 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -29,6 +29,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetNoConfig"
+ android:exported="true"
android:label="No Config">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -39,6 +40,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWdigetHidden"
+ android:exported="true"
android:label="Hidden widget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -49,6 +51,7 @@
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
+ android:exported="true"
android:label="With Config">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
@@ -57,13 +60,26 @@
android:resource="@xml/appwidget_with_config"/>
</receiver>
+ <receiver
+ android:name="com.android.launcher3.testcomponent.AppWidgetDynamicColors"
+ android:exported="true"
+ android:label="Dynamic Colors">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/appwidget_dynamic_colors"/>
+ </receiver>
+
<activity
- android:name="com.android.launcher3.testcomponent.WidgetConfigActivity">
+ android:name="com.android.launcher3.testcomponent.WidgetConfigActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
</intent-filter>
</activity>
- <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity">
+ <activity android:name="com.android.launcher3.testcomponent.CustomShortcutConfigActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
@@ -72,6 +88,7 @@
<activity
android:name="com.android.launcher3.testcomponent.RequestPinItemActivity"
android:icon="@drawable/test_drawable_pin_item"
+ android:exported="true"
android:label="Test Pin Item">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -92,7 +109,7 @@
<activity
android:name="com.android.launcher3.testcomponent.TestLauncherActivity"
android:clearTaskOnLaunch="true"
- android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout"
android:enabled="false"
android:label="Test launcher"
android:launchMode="singleTask"
@@ -102,6 +119,7 @@
android:stateNotNeeded="true"
android:taskAffinity=""
android:theme="@android:style/Theme.DeviceDefault.Light"
+ android:exported="true"
android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -114,6 +132,7 @@
<activity
android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
android:label="LauncherTestApp"
+ android:exported="true"
android:taskAffinity="com.android.launcher3.testcomponent.Affinity1">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -128,6 +147,7 @@
</activity>
<activity-alias android:name="Activity2"
android:label="TestActivity2"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -136,6 +156,7 @@
</activity-alias>
<activity-alias android:name="Activity3"
android:label="TestActivity3"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -144,6 +165,7 @@
</activity-alias>
<activity-alias android:name="Activity4"
android:label="TestActivity4"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -152,6 +174,7 @@
</activity-alias>
<activity-alias android:name="Activity5"
android:label="TestActivity5"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -160,6 +183,7 @@
</activity-alias>
<activity-alias android:name="Activity6"
android:label="TestActivity6"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -168,6 +192,7 @@
</activity-alias>
<activity-alias android:name="Activity7"
android:label="TestActivity7"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -176,6 +201,7 @@
</activity-alias>
<activity-alias android:name="Activity8"
android:label="TestActivity8"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -184,6 +210,7 @@
</activity-alias>
<activity-alias android:name="Activity9"
android:label="TestActivity9"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -192,6 +219,7 @@
</activity-alias>
<activity-alias android:name="Activity10"
android:label="TestActivity10"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -200,6 +228,7 @@
</activity-alias>
<activity-alias android:name="Activity11"
android:label="TestActivity11"
+ android:exported="true"
android:targetActivity="com.android.launcher3.testcomponent.BaseTestingActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/dummy_app/AndroidManifest.xml b/tests/dummy_app/AndroidManifest.xml
index f00138c..d5e2320 100644
--- a/tests/dummy_app/AndroidManifest.xml
+++ b/tests/dummy_app/AndroidManifest.xml
@@ -26,6 +26,7 @@
<activity
android:name="Activity1"
android:icon="@mipmap/ic_launcher1"
+ android:exported="true"
android:label="Aardwolf">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/res/layout/test_layout_appwidget_dynamic_colors.xml b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
new file mode 100644
index 0000000..c5ab030
--- /dev/null
+++ b/tests/res/layout/test_layout_appwidget_dynamic_colors.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:background="?android:attr/colorBackground"
+ android:padding="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ android:orientation = "horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="prim"/>
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:background="@android:color/system_primary_500"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation = "horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="second"/>
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:background="@android:color/system_secondary_500"/>
+ </LinearLayout>
+ <LinearLayout
+ android:orientation = "horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="neutral"/>
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:background="@android:color/system_neutral_500"/>
+ </LinearLayout>
+
+ </LinearLayout>
\ No newline at end of file
diff --git a/tests/res/xml/appwidget_dynamic_colors.xml b/tests/res/xml/appwidget_dynamic_colors.xml
new file mode 100644
index 0000000..f6b9a04
--- /dev/null
+++ b/tests/res/xml/appwidget_dynamic_colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="1dp"
+ android:minHeight="1dp"
+ android:updatePeriodMillis="0"
+ android:initialLayout="@layout/test_layout_appwidget_dynamic_colors"
+ android:resizeMode="horizontal|vertical"
+ android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/res/xml/appwidget_hidden.xml b/tests/res/xml/appwidget_hidden.xml
index 6f0e006..f6cffb5 100644
--- a/tests/res/xml/appwidget_hidden.xml
+++ b/tests/res/xml/appwidget_hidden.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="180dp"
- android:minHeight="110dp"
+ android:minWidth="1dp"
+ android:minHeight="1dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/test_layout_appwidget_blue"
android:resizeMode="horizontal|vertical"
diff --git a/tests/res/xml/appwidget_no_config.xml b/tests/res/xml/appwidget_no_config.xml
index d24dfe3..0d932dc 100644
--- a/tests/res/xml/appwidget_no_config.xml
+++ b/tests/res/xml/appwidget_no_config.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="180dp"
- android:minHeight="110dp"
+ android:minWidth="1dp"
+ android:minHeight="1dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/test_layout_appwidget_red"
android:resizeMode="horizontal|vertical"
diff --git a/tests/res/xml/appwidget_with_config.xml b/tests/res/xml/appwidget_with_config.xml
index 8403689..813e6df 100644
--- a/tests/res/xml/appwidget_with_config.xml
+++ b/tests/res/xml/appwidget_with_config.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
- android:minWidth="180dp"
- android:minHeight="110dp"
+ android:minWidth="1dp"
+ android:minHeight="1dp"
android:updatePeriodMillis="86400000"
android:initialLayout="@layout/test_layout_appwidget_blue"
android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"
diff --git a/proto_overrides/launcher_log_extension.proto b/tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
similarity index 62%
rename from proto_overrides/launcher_log_extension.proto
rename to tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
index 2995aa2..5fb3454 100644
--- a/proto_overrides/launcher_log_extension.proto
+++ b/tests/src/com/android/launcher3/testcomponent/AppWidgetDynamicColors.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,18 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-syntax = "proto2";
-option java_package = "com.android.launcher3.userevent";
-option java_outer_classname = "LauncherLogExtensions";
+package com.android.launcher3.testcomponent;
-package userevent;
+import android.appwidget.AppWidgetProvider;
-//
-// Use this to add any app specific extensions to the proto.
-//
-message LauncherEventExtension {
-}
-
-message TargetExtension {
+/**
+ * A simple app widget showing a primary, secondary and neutral color.
+ */
+public class AppWidgetDynamicColors extends AppWidgetProvider {
}
diff --git a/tests/src/com/android/launcher3/testcomponent/ListViewService.java b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
index 3da20e0..9e3a492 100644
--- a/tests/src/com/android/launcher3/testcomponent/ListViewService.java
+++ b/tests/src/com/android/launcher3/testcomponent/ListViewService.java
@@ -89,7 +89,7 @@
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return SimpleViewsFactory.this;
}
- }.onBind(new Intent("dummy_intent"));
+ }.onBind(new Intent("stub_intent"));
}
}
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 858e183..0edfbed 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -53,7 +53,6 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.Utilities;
import com.android.launcher3.common.WidgetUtils;
-import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.tapl.LauncherInstrumentation;
@@ -64,7 +63,6 @@
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.FailureRewriterRule;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.launcher3.util.rule.LauncherActivityRule;
import com.android.launcher3.util.rule.ShellCommandRule;
@@ -101,6 +99,7 @@
private static final String TAG = "AbstractLauncherUiTest";
private static String sStrictmodeDetectedActivityLeak;
+ private static boolean sDumpWasGenerated = false;
private static boolean sActivityLeakReported;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
protected static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker();
@@ -116,8 +115,7 @@
if (TestHelpers.isInLauncherProcess()) {
StrictMode.VmPolicy.Builder builder =
new StrictMode.VmPolicy.Builder()
-// b/154772063
-// .detectActivityLeaks()
+ .detectActivityLeaks()
.penaltyLog()
.penaltyListener(Runnable::run, violation -> {
if (sStrictmodeDetectedActivityLeak == null) {
@@ -152,16 +150,31 @@
return "Activity leak detector has found leaked activities, " + dumpHprofData() + ".";
}
- private static String dumpHprofData() {
- try {
- final String fileName = getInstrumentation().getTargetContext().getFilesDir().getPath()
- + "/ActivityLeakHeapDump.hprof";
- Debug.dumpHprofData(fileName);
- return "memory dump filename: " + fileName;
- } catch (Throwable e) {
- Log.e(TAG, "dumpHprofData failed", e);
- return "failed to save memory dump";
+ public static String dumpHprofData() {
+ String result;
+ if (sDumpWasGenerated) {
+ result = "dump has already been generated by another test";
+ } else {
+ try {
+ final String fileName =
+ getInstrumentation().getTargetContext().getFilesDir().getPath()
+ + "/ActivityLeakHeapDump.hprof";
+ if (TestHelpers.isInLauncherProcess()) {
+ Debug.dumpHprofData(fileName);
+ } else {
+ final UiDevice device = UiDevice.getInstance(getInstrumentation());
+ device.executeShellCommand(
+ "am dumpheap " + device.getLauncherPackageName() + " " + fileName);
+ }
+ sDumpWasGenerated = true;
+ result = "memory dump filename: " + fileName;
+ } catch (Throwable e) {
+ Log.e(TAG, "dumpHprofData failed", e);
+ result = "failed to save memory dump";
+ }
}
+ return result
+ + ". Full list of activities: " + ACTIVITY_LEAK_TRACKER.getActivitiesList();
}
protected AbstractLauncherUiTest() {
@@ -226,9 +239,8 @@
}
@Rule
- public TestRule mOrderSensitiveRules = RuleChain.
- outerRule(new FailureRewriterRule())
- .around(new TestStabilityRule())
+ public TestRule mOrderSensitiveRules = RuleChain
+ .outerRule(new TestStabilityRule())
.around(mActivityMonitor)
.around(getRulesInsideActivityMonitor());
@@ -236,25 +248,11 @@
return mDevice;
}
- private boolean hasSystemUiObject(String resId) {
- return mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, resId));
- }
-
@Before
public void setUp() throws Exception {
- Log.d(TAG, "Before disabling battery defender");
- mDevice.executeShellCommand("setprop vendor.battery.defender.disable 1");
- Log.d(TAG, "Before enabling stay awake");
- mDevice.executeShellCommand("settings put global stay_on_while_plugged_in 3");
- for (int i = 0; i < 10 && hasSystemUiObject("keyguard_status_view"); ++i) {
- Log.d(TAG, "Before unlocking the phone");
- mDevice.executeShellCommand("input keyevent 82");
- mDevice.waitForIdle();
- }
- Assert.assertTrue("Keyguard still visible",
- mDevice.wait(
+ Assert.assertTrue("Keyguard is visible",
+ TestHelpers.wait(
Until.gone(By.res(SYSTEMUI_PACKAGE, "keyguard_status_view")), 60000));
- Log.d(TAG, "Keyguard is not visible");
final String launcherPackageName = mDevice.getLauncherPackageName();
try {
@@ -272,8 +270,6 @@
}
mLauncherPid = 0;
- // Disable app tracker
- AppLaunchTracker.INSTANCE.initializeForTesting(new AppLaunchTracker());
mTargetContext = InstrumentationRegistry.getTargetContext();
mTargetPackage = mTargetContext.getPackageName();
@@ -283,6 +279,8 @@
if (userManager != null) {
for (UserHandle userHandle : userManager.getUserProfiles()) {
if (!userHandle.isSystem()) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED,
+ "removing user " + userHandle.getIdentifier());
mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier());
}
}
@@ -486,8 +484,7 @@
}
getInstrumentation().getTargetContext().startActivity(intent);
assertTrue("App didn't start: " + selector,
- UiDevice.getInstance(getInstrumentation())
- .wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
+ TestHelpers.wait(Until.hasObject(selector), DEFAULT_UI_TIMEOUT));
}
public static ActivityInfo resolveSystemAppInfo(String category) {
diff --git a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
index 202dcb1..2db7472 100644
--- a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
+++ b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
@@ -25,6 +25,7 @@
import com.android.launcher3.tapl.TestHelpers;
import java.util.WeakHashMap;
+import java.util.stream.Collectors;
public class ActivityLeakTracker implements Application.ActivityLifecycleCallbacks {
private final WeakHashMap<Activity, Boolean> mActivities = new WeakHashMap<>();
@@ -73,20 +74,17 @@
}
public boolean noLeakedActivities() {
- int liveActivities = 0;
- int destroyedActivities = 0;
-
for (Activity activity : mActivities.keySet()) {
if (activity.isDestroyed()) {
- ++destroyedActivities;
- } else {
- ++liveActivities;
+ return false;
}
}
- if (liveActivities > 2) return false;
+ return mActivities.size() <= 2;
+ }
- // It's OK to have 1 leaked activity if no active activities exist.
- return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
+ public String getActivitiesList() {
+ return mActivities.keySet().stream().map(a -> a.getClass().getSimpleName())
+ .collect(Collectors.joining(","));
}
}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 34e425d..4c9a8e7 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -37,10 +37,11 @@
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
import com.android.launcher3.views.OptionsPopupView;
-import com.android.launcher3.widget.WidgetsFullSheet;
-import com.android.launcher3.widget.WidgetsRecyclerView;
+import com.android.launcher3.widget.picker.WidgetsFullSheet;
+import com.android.launcher3.widget.picker.WidgetsRecyclerView;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -101,7 +102,7 @@
mLauncher.pressHome();
}
- @Test
+ @Ignore
public void testOpenHomeSettingsFromWorkspace() {
mDevice.pressMenu();
mDevice.waitForIdle();
@@ -298,7 +299,9 @@
launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
isOptionsPopupVisible(launcher)));
- menu.getMenuItem(1).launch(getAppPackageName());
+ final AppIconMenuItem menuItem = menu.getMenuItem(1);
+ assertEquals("Wrong menu item", "Shortcut 2", menuItem.getText());
+ menuItem.launch(getAppPackageName());
} finally {
allApps.unfreeze();
}
@@ -341,6 +344,7 @@
openMenu().
getMenuItem(0);
final String shortcutName = menuItem.getText();
+ assertEquals("Wrong menu item", "Shortcut 3", shortcutName);
menuItem.dragToWorkspace(false, false);
mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
diff --git a/tests/src/com/android/launcher3/ui/TestViewHelpers.java b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
index eceff34..083f580 100644
--- a/tests/src/com/android/launcher3/ui/TestViewHelpers.java
+++ b/tests/src/com/android/launcher3/ui/TestViewHelpers.java
@@ -24,9 +24,9 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import java.util.concurrent.Callable;
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index f5f93c4..a6c7c03 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -17,6 +17,8 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+import static com.android.launcher3.tapl.LauncherInstrumentation.LONG_WAIT_TIME_MS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -45,6 +47,7 @@
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
@LargeTest
@RunWith(AndroidJUnit4.class)
@@ -55,7 +58,9 @@
private static final int WORK_PAGE = AllAppsContainerView.AdapterHolder.WORK;
@Before
- public void createWorkProfile() throws Exception {
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
String output =
mDevice.executeShellCommand(
"pm create-user --profileOf 0 --managed TestProfile");
@@ -63,20 +68,34 @@
String[] tokens = output.split("\\s+");
mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
-
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Created new user uid" + mProfileUserId);
mDevice.executeShellCommand("am start-user " + mProfileUserId);
}
@After
public void removeWorkProfile() throws Exception {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "(teardown) removing uid" + mProfileUserId,
+ new Exception());
mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
}
+ @After
+ public void resumeAppStoreUpdate() {
+ executeOnLauncher(launcher -> {
+ if (launcher == null || launcher.getAppsView() == null) {
+ return;
+ }
+ launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "resuming AppStore updates");
+ });
+ }
+
@Test
public void workTabExists() {
mDevice.pressHome();
waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+ waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
waitForLauncherCondition("Personal tab is missing",
launcher -> launcher.getAppsView().isPersonalTabVisible(), 60000);
waitForLauncherCondition("Work tab is missing",
@@ -122,9 +141,13 @@
WorkEduView.KEY_WORK_EDU_STEP).remove(
WorkEduView.KEY_LEGACY_WORK_EDU_SEEN).commit());
- waitForLauncherCondition("Work tab not setup",
- launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
- 60000);
+ waitForLauncherCondition("Work tab not setup", launcher -> {
+ if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
+ launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
+ return true;
+ }
+ return false;
+ }, LONG_WAIT_TIME_MS);
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
WorkEduView workEduView = getEduView();
@@ -135,15 +158,22 @@
workEduView.findViewById(R.id.proceed).callOnClick();
});
- executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
- "Work profile status: " + launcher.getAppsView().isPersonalTabVisible()));
-
+ AtomicInteger attempt = new AtomicInteger(0);
// verify work edu is seen next
- waitForLauncherCondition("Launcher did not show the next edu screen", l ->
- ((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage() == WORK_PAGE
- && ((TextView) workEduView.findViewById(
- R.id.content_text)).getText().equals(
- l.getResources().getString(R.string.work_profile_edu_work_apps)));
+ waitForLauncherCondition("Launcher did not show the next edu screen", l -> {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED,
+ "running test attempt" + attempt.getAndIncrement());
+ if (!(l.getAppsView().getContentView() instanceof AllAppsPagedView)) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work tab not setup. Skipping test");
+ return false;
+ }
+ if (((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage()
+ != WORK_PAGE) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work page not highlighted");
+ }
+ return ((TextView) workEduView.findViewById(R.id.content_text)).getText().equals(
+ l.getResources().getString(R.string.work_profile_edu_work_apps));
+ });
}
@Test
@@ -170,6 +200,10 @@
// open work tab
executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
waitForState("Launcher did not switch to all apps", () -> ALL_APPS);
+ waitForLauncherCondition("Work tab not setup",
+ launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
+ 60000);
+
executeOnLauncher(launcher -> {
AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
pagedView.setCurrentPage(WORK_PAGE);
@@ -189,7 +223,7 @@
DragLayer dragLayer = l.getDragLayer();
return dragLayer.getChildCount() > 0 && dragLayer.getChildAt(
dragLayer.getChildCount() - 1) instanceof WorkEduView;
- });
+ }, 6000);
return getFromLauncher(launcher -> (WorkEduView) launcher.getDragLayer().getChildAt(
launcher.getDragLayer().getChildCount() - 1));
}
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 9d4ccff..b421b0e 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -28,7 +28,6 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Workspace;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -39,6 +38,7 @@
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.Wait.Condition;
import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import org.junit.Before;
import org.junit.Rule;
@@ -92,9 +92,8 @@
// Drag widget to homescreen
WidgetConfigStartupMonitor monitor = new WidgetConfigStartupMonitor();
- widgets.
- getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager())).
- dragToWorkspace(true, false);
+ widgets.getWidget(mWidgetInfo.getLabel(mTargetContext.getPackageManager()))
+ .dragToWorkspace(true, false);
// Widget id for which the config activity was opened
mWidgetId = monitor.getWidgetId();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
index f146db5..714b11b 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddWidgetTest.java
@@ -23,12 +23,12 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.Widget;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import org.junit.Rule;
import org.junit.Test;
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index a4aa9f2..9c6c317 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -37,7 +37,6 @@
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -47,6 +46,7 @@
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.ui.TestViewHelpers;
import com.android.launcher3.util.rule.ShellCommandRule;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
import org.junit.After;
@@ -192,7 +192,7 @@
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
executeOnLauncher(l -> l.getAppWidgetHost().startListening());
verifyWidgetPresent(info);
- assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT));
+ assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
}
@Test
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0e43d81..822fefc 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -30,7 +30,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -126,9 +125,6 @@
private void runTest(String activityMethod, boolean isWidget, ItemOperator itemMatcher,
Intent... commandIntents) throws Throwable {
- if (!Utilities.ATLEAST_OREO) {
- return;
- }
clearHomescreen();
mDevice.pressHome();
diff --git a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java b/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
deleted file mode 100644
index e4f520f..0000000
--- a/tests/src/com/android/launcher3/util/rule/FailureInvestigator.java
+++ /dev/null
@@ -1,127 +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.launcher3.util.rule;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
-import android.os.SystemClock;
-
-import androidx.test.uiautomator.UiDevice;
-
-import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.regex.Pattern;
-
-class FailureInvestigator {
- private static boolean matches(String regex, CharSequence string) {
- return Pattern.compile(regex).matcher(string).find();
- }
-
- static class LogcatMatch {
- String logcatPattern;
- int bug;
-
- LogcatMatch(String logcatPattern, int bug) {
- this.logcatPattern = logcatPattern;
- this.bug = bug;
- }
- }
-
- static class ExceptionMatch {
- String exceptionPattern;
- LogcatMatch[] logcatMatches;
-
- ExceptionMatch(String exceptionPattern, LogcatMatch[] logcatMatches) {
- this.exceptionPattern = exceptionPattern;
- this.logcatMatches = logcatMatches;
- }
- }
-
- private static final ExceptionMatch[] EXCEPTION_MATCHES = {
- new ExceptionMatch(
- "java.lang.AssertionError: http://go/tapl : Tests are broken by a "
- + "non-Launcher system error: (Phone is locked|Screen is empty)",
- new LogcatMatch[]{
- new LogcatMatch(
- "BroadcastQueue: Can't deliver broadcast to com.android"
- + ".systemui.*Crashing it",
- 147845913),
- new LogcatMatch(
- "Attempt to invoke virtual method 'boolean android\\"
- + ".graphics\\.Bitmap\\.isRecycled\\(\\)' on a null "
- + "object reference",
- 148424291),
- new LogcatMatch(
- "java\\.lang\\.IllegalArgumentException\\: Ranking map "
- + "doesn't contain key",
- 148570537),
- }),
- new ExceptionMatch("Launcher didn't initialize",
- new LogcatMatch[]{
- new LogcatMatch(
- "ActivityManager: Reason: executing service com.google"
- + ".android.apps.nexuslauncher/com.android.launcher3"
- + ".notification.NotificationListener",
- 148238677),
- }),
- };
-
- static int getBugForFailure(CharSequence exception) {
- if ("com.google.android.setupwizard".equals(
- UiDevice.getInstance(getInstrumentation()).getLauncherPackageName())) {
- return 145935261;
- }
-
- if (matches("java\\.lang\\.AssertionError\\: http\\:\\/\\/go\\/tapl \\: want to get "
- + "workspace object; Presence of recents button doesn't match the interaction "
- + "mode, mode\\=ZERO_BUTTON, hasRecents\\=true", exception)) {
- return 148422894;
- }
-
- final String logSinceBoot;
- try {
- final String systemBootTime =
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(
- new Date(System.currentTimeMillis() - SystemClock.elapsedRealtime()));
-
- logSinceBoot =
- UiDevice.getInstance(getInstrumentation())
- .executeShellCommand("logcat -d -t " + systemBootTime.replace(" ", ""));
- } catch (IOException | OutOfMemoryError e) {
- return 0;
- }
-
- if (matches("android\\:\\:uirenderer\\:\\:renderthread\\:\\:EglManager\\:\\:swapBuffers",
- logSinceBoot)) {
- return 148529608;
- }
-
- for (ExceptionMatch exceptionMatch : EXCEPTION_MATCHES) {
- if (matches(exceptionMatch.exceptionPattern, exception)) {
- for (LogcatMatch logcatMatch : exceptionMatch.logcatMatches) {
- if (matches(logcatMatch.logcatPattern, logSinceBoot)) {
- return logcatMatch.bug;
- }
- }
- break;
- }
- }
-
- return 0;
- }
-}
diff --git a/tests/src/com/android/launcher3/util/rule/FailureRewriterRule.java b/tests/src/com/android/launcher3/util/rule/FailureRewriterRule.java
deleted file mode 100644
index 99ddee4..0000000
--- a/tests/src/com/android/launcher3/util/rule/FailureRewriterRule.java
+++ /dev/null
@@ -1,47 +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.launcher3.util.rule;
-
-import android.util.Log;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-public class FailureRewriterRule implements TestRule {
- private static final String TAG = "FailureRewriter";
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- try {
- base.evaluate();
- } catch (Throwable e) {
- final int bug = FailureInvestigator.getBugForFailure(e.toString());
- if (bug == 0) throw e;
-
- Log.e(TAG, "Known bug found for the original failure "
- + android.util.Log.getStackTraceString(e));
- throw new AssertionError(
- "Detected a failure that matches a known bug b/" + bug);
- }
- }
- };
- }
-}
diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
index c0913bf..ffad93f 100644
--- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java
+++ b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
@@ -23,12 +23,12 @@
import android.content.Context;
import android.os.Bundle;
-import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.widget.LauncherAppWidgetHost;
+import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetManagerHelper;
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 816cd7d..d317783 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -16,8 +16,9 @@
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.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import android.graphics.Point;
@@ -87,13 +88,9 @@
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
: LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
- // b/156044202
- mLauncher.log("Hierarchy before swiping up to overview:");
- mLauncher.dumpViewHierarchy();
-
mLauncher.sendPointer(
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
- mLauncher.executeAndWaitForEvent(
+ mLauncher.executeAndWaitForLauncherEvent(
() -> mLauncher.movePointer(
downTime,
downTime,
@@ -147,126 +144,97 @@
private void expectSwitchToOverviewEvents() {
}
- /**
- * Swipes right or double presses the square button to switch to the previous app.
- */
+ @NonNull
public Background quickSwitchToPreviousApp() {
- try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
- LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
- "want to quick switch to the previous app")) {
- verifyActiveContainer();
- quickSwitchToPreviousApp(getExpectedStateForQuickSwitch());
- return new Background(mLauncher);
- }
+ boolean toRight = true;
+ quickSwitch(toRight);
+ return new Background(mLauncher);
}
- protected int getExpectedStateForQuickSwitch() {
- return BACKGROUND_APP_STATE_ORDINAL;
- }
-
- protected void quickSwitchToPreviousApp(int expectedState) {
- final boolean launcherWasVisible = mLauncher.isLauncherVisible();
- boolean transposeInLandscape = false;
- switch (mLauncher.getNavigationModel()) {
- case TWO_BUTTON:
- transposeInLandscape = true;
- // Fall through, zero button and two button modes behave the same.
- case ZERO_BUTTON: {
- final int startX;
- final int startY;
- final int endX;
- final int endY;
- if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
- // Swipe from the bottom left to the bottom right of the screen.
- startX = 0;
- startY = getSwipeStartY();
- endX = mLauncher.getDevice().getDisplayWidth();
- endY = startY;
- } else {
- // Swipe from the bottom right to the top right of the screen.
- startX = getSwipeStartX();
- startY = mLauncher.getRealDisplaySize().y - 1;
- endX = startX;
- endY = 0;
- }
- final boolean isZeroButton = mLauncher.getNavigationModel()
- == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
- mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
- launcherWasVisible && isZeroButton
- ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
- : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
- break;
- }
-
- case THREE_BUTTON:
- // Double press the recents button.
- UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
- mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
- mLauncher.getOverview();
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
- recentsButton.click();
- break;
- }
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
- }
- /** Swipes left to switch to the previous app. */
+ @NonNull
public Background quickSwitchToPreviousAppSwipeLeft() {
- try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
- LauncherInstrumentation.Closable c =
- mLauncher.addContextLayer("want to quick switch to the previous app")) {
- verifyActiveContainer();
- quickSwitchToPreviousAppSwipeLeft(getExpectedStateForQuickSwitch());
- return new Background(mLauncher);
- }
+ boolean toRight = false;
+ quickSwitch(toRight);
+ return new Background(mLauncher);
}
- protected void quickSwitchToPreviousAppSwipeLeft(int expectedState) {
- final boolean launcherWasVisible = mLauncher.isLauncherVisible();
- boolean transposeInLandscape = false;
- switch (mLauncher.getNavigationModel()) {
- case TWO_BUTTON:
- transposeInLandscape = true;
- // Fall through, zero button and two button modes behave the same.
- case ZERO_BUTTON: {
- final int startX;
- final int startY;
- final int endX;
- final int endY;
- if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
- // Swipe from the bottom right to the bottom left of the screen.
- startX = mLauncher.getDevice().getDisplayWidth();
- startY = getSwipeStartY();
- endX = 0;
- endY = startY;
- } else {
- // Swipe from the bottom right to the top right of the screen.
- startX = getSwipeStartX();
- startY = mLauncher.getRealDisplaySize().y - 1;
- endX = startX;
- endY = 0;
- }
- final boolean isZeroButton =
- mLauncher.getNavigationModel()
- == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
- mLauncher.swipeToState(startX, startY, endX, endY, 20, expectedState,
- launcherWasVisible && isZeroButton
- ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
- : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER);
- break;
- }
+ @NonNull
+ private void quickSwitch(boolean toRight) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to quick switch to the previous app")) {
+ verifyActiveContainer();
+ final boolean launcherWasVisible = mLauncher.isLauncherVisible();
+ boolean transposeInLandscape = false;
+ switch (mLauncher.getNavigationModel()) {
+ case TWO_BUTTON:
+ transposeInLandscape = true;
+ // Fall through, zero button and two button modes behave the same.
+ case ZERO_BUTTON: {
+ final int startX;
+ final int startY;
+ final int endX;
+ final int endY;
+ if (toRight) {
+ if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+ // Swipe from the bottom left to the bottom right of the screen.
+ startX = 0;
+ startY = getSwipeStartY();
+ endX = mLauncher.getDevice().getDisplayWidth();
+ endY = startY;
+ } else {
+ // Swipe from the bottom right to the top right of the screen.
+ startX = getSwipeStartX();
+ startY = mLauncher.getRealDisplaySize().y - 1;
+ endX = startX;
+ endY = 0;
+ }
+ } else {
+ if (mLauncher.getDevice().isNaturalOrientation() || !transposeInLandscape) {
+ // Swipe from the bottom right to the bottom left of the screen.
+ startX = mLauncher.getDevice().getDisplayWidth();
+ startY = getSwipeStartY();
+ endX = 0;
+ endY = startY;
+ } else {
+ // Swipe from the bottom left to the top left of the screen.
+ startX = getSwipeStartX();
+ startY = 0;
+ endX = startX;
+ endY = mLauncher.getRealDisplaySize().y - 1;
+ }
+ }
- case THREE_BUTTON:
- // Double press the recents button.
- UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
- mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
- mLauncher.getOverview();
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
- recentsButton.click();
- break;
+ final boolean isZeroButton = mLauncher.getNavigationModel()
+ == LauncherInstrumentation.NavigationModel.ZERO_BUTTON;
+ LauncherInstrumentation.GestureScope gestureScope =
+ launcherWasVisible && isZeroButton
+ ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
+ : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
+ mLauncher.executeAndWaitForEvent(
+ () -> mLauncher.linearGesture(
+ startX, startY, endX, endY, 20, false, gestureScope),
+ event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ () -> "Quick switch gesture didn't change window state");
+ break;
+ }
+
+ case THREE_BUTTON:
+ // Double press the recents button.
+ UiObject2 recentsButton = mLauncher.waitForSystemUiObject("recent_apps");
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+ mLauncher.runToState(() -> recentsButton.click(), OVERVIEW_STATE_ORDINAL);
+ mLauncher.getOverview();
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, SQUARE_BUTTON_EVENT);
+ mLauncher.executeAndWaitForEvent(
+ () -> recentsButton.click(),
+ event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
+ () -> "Pressing recents button didn't change window state");
+ break;
+ }
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
+ return;
}
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
}
protected String getSwipeHeightRequestName() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index c06e254..0060844 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -16,8 +16,6 @@
package com.android.launcher3.tapl;
-import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
-
import androidx.annotation.NonNull;
/**
@@ -65,8 +63,4 @@
return true;
}
- @Override
- protected int getExpectedStateForQuickSwitch() {
- return QUICK_SWITCH_STATE_ORDINAL;
- }
}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 13ecfb8..c4a566b 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,17 +56,19 @@
private Background launch(BySelector selector) {
LauncherInstrumentation.log("Launchable.launch before click " +
mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+ final String label = mObject.getText();
mLauncher.executeAndWaitForEvent(
- () -> mLauncher.clickLauncherObject(mObject),
+ () -> {
+ mLauncher.clickLauncherObject(mObject);
+ expectActivityStartEvents();
+ },
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
- () -> "Launching an app didn't open a new window: " + mObject.getText());
- expectActivityStartEvents();
+ () -> "Launching an app didn't open a new window: " + label);
mLauncher.assertTrue(
- "App didn't start: " + selector,
- mLauncher.getDevice().wait(Until.hasObject(selector),
- LauncherInstrumentation.WAIT_TIME_MS));
+ "App didn't start: " + label + " (" + selector + ")",
+ TestHelpers.wait(Until.hasObject(selector), LauncherInstrumentation.WAIT_TIME_MS));
return new Background(mLauncher);
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index be3cb4e..f279a82 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -64,6 +64,7 @@
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.testing.TestProtocol;
+import com.android.systemui.shared.system.ContextUtils;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Assert;
@@ -103,6 +104,8 @@
static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+ private final String mLauncherPackage;
+ private final boolean mIsLauncher3;
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
@@ -150,9 +153,10 @@
private static final String WORKSPACE_RES_ID = "workspace";
private static final String APPS_RES_ID = "apps_view";
private static final String OVERVIEW_RES_ID = "overview_panel";
- private static final String WIDGETS_RES_ID = "widgets_list_view";
+ private static final String WIDGETS_RES_ID = "primary_widgets_list_view";
private static final String CONTEXT_MENU_RES_ID = "deep_shortcuts_container";
public static final int WAIT_TIME_MS = 10000;
+ public static final int LONG_WAIT_TIME_MS = 60000;
private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
private static final String ANDROID_PACKAGE = "android";
@@ -173,8 +177,7 @@
private Runnable mOnLauncherCrashed;
private static Pattern getTouchEventPattern(String prefix, String action) {
- // The pattern includes sanity checks that we don't get a multi-touch events or other
- // surprises.
+ // The pattern includes checks that we don't get a multi-touch events or other surprises.
return Pattern.compile(
prefix + ": MotionEvent.*?action=" + action + ".*?id\\[0\\]=0"
+ ".*?toolType\\[0\\]=TOOL_TYPE_FINGER.*?buttonState=0.*?pointerCount=1");
@@ -203,6 +206,7 @@
public LauncherInstrumentation(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
mDevice = UiDevice.getInstance(instrumentation);
+ mIsLauncher3 = "com.android.launcher3".equals(getLauncherPackageName());
// Launcher should run in test harness so that custom accessibility protocol between
// Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
@@ -216,11 +220,11 @@
// 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.
- final String authorityPackage = testPackage.equals(targetPackage) ?
- getLauncherPackageName() :
- targetPackage;
+ mLauncherPackage = testPackage.equals(targetPackage)
+ ? getLauncherPackageName()
+ : targetPackage;
- String testProviderAuthority = authorityPackage + ".TestInfo";
+ String testProviderAuthority = mLauncherPackage + ".TestInfo";
mTestProviderUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(testProviderAuthority)
@@ -237,11 +241,12 @@
if (pm.getComponentEnabledSetting(cn) != COMPONENT_ENABLED_STATE_ENABLED) {
if (TestHelpers.isInLauncherProcess()) {
- getContext().getPackageManager().setComponentEnabledSetting(
- cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
+ pm.setComponentEnabledSetting(cn, COMPONENT_ENABLED_STATE_ENABLED, DONT_KILL_APP);
} else {
try {
- mDevice.executeShellCommand("pm enable " + cn.flattenToString());
+ final int userId = ContextUtils.getUserId(getContext());
+ mDevice.executeShellCommand(
+ "pm enable --user " + userId + " " + cn.flattenToString());
} catch (IOException e) {
fail(e.toString());
}
@@ -360,9 +365,11 @@
if (hasSystemUiObject("keyguard_status_view")) return "Phone is locked";
- if (!mDevice.hasObject(By.textStartsWith(""))) return "Screen is empty";
+ if (!mDevice.wait(Until.hasObject(By.textStartsWith("")), WAIT_TIME_MS)) {
+ return "Screen is empty";
+ }
- final String navigationModeError = getNavigationModeMismatchError();
+ final String navigationModeError = getNavigationModeMismatchError(true);
if (navigationModeError != null) return navigationModeError;
} catch (Throwable e) {
Log.w(TAG, "getSystemAnomalyMessage failed", e);
@@ -450,7 +457,7 @@
}
}
- dumpDiagnostics();
+ dumpDiagnostics(message);
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
@@ -458,10 +465,11 @@
return message;
}
- private void dumpDiagnostics() {
- Log.e("b/156287114", "Input:");
+ private void dumpDiagnostics(String message) {
+ log("Diagnostics for failure: " + message);
+ log("Input:");
logShellCommand("dumpsys input");
- Log.e("b/156287114", "TIS:");
+ log("TIS:");
logShellCommand("dumpsys activity service TouchInteractionService");
}
@@ -469,10 +477,10 @@
try {
for (String line : mDevice.executeShellCommand(command).split("\\n")) {
SystemClock.sleep(10);
- Log.d("b/156287114", line);
+ log(line);
}
} catch (IOException e) {
- Log.d("b/156287114", "Failed to execute " + command);
+ log("Failed to execute " + command);
}
}
@@ -529,17 +537,28 @@
mExpectedRotation = expectedRotation;
}
- public String getNavigationModeMismatchError() {
+ public String getNavigationModeMismatchError(boolean waitForCorrectState) {
+ final int waitTime = waitForCorrectState ? WAIT_TIME_MS : 0;
final NavigationModel navigationModel = getNavigationModel();
- final boolean hasRecentsButton = hasSystemUiObject("recent_apps");
- final boolean hasHomeButton = hasSystemUiObject("home");
- if ((navigationModel == NavigationModel.THREE_BUTTON) != hasRecentsButton) {
- return "Presence of recents button doesn't match the interaction mode, mode="
- + navigationModel.name() + ", hasRecents=" + hasRecentsButton;
+
+ if (navigationModel == NavigationModel.THREE_BUTTON) {
+ if (!mDevice.wait(Until.hasObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")), waitTime)) {
+ return "Recents button not present in 3-button mode";
+ }
+ } else {
+ if (!mDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "recent_apps")), waitTime)) {
+ return "Recents button is present in non-3-button mode";
+ }
}
- if ((navigationModel != NavigationModel.ZERO_BUTTON) != hasHomeButton) {
- return "Presence of home button doesn't match the interaction mode, mode="
- + navigationModel.name() + ", hasHome=" + hasHomeButton;
+
+ if (navigationModel == NavigationModel.ZERO_BUTTON) {
+ if (!mDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "home")), waitTime)) {
+ return "Home button is present in gestural mode";
+ }
+ } else {
+ if (!mDevice.wait(Until.hasObject(By.res(SYSTEMUI_PACKAGE, "home")), waitTime)) {
+ return "Home button not present in non-gestural mode";
+ }
}
return null;
}
@@ -550,13 +569,7 @@
assertEquals("Unexpected display rotation",
mExpectedRotation, mDevice.getDisplayRotation());
- // b/148422894
- String error = null;
- for (int i = 0; i != 600; ++i) {
- error = getNavigationModeMismatchError();
- if (error == null) break;
- sleep(100);
- }
+ final String error = getNavigationModeMismatchError(true);
assertTrue(error, error == null);
log("verifyContainerType: " + containerType);
@@ -595,11 +608,7 @@
return waitForLauncherObject(APPS_RES_ID);
}
case OVERVIEW: {
- if (hasAllAppsInOverview()) {
- waitForLauncherObject(APPS_RES_ID);
- } else {
- waitUntilLauncherObjectGone(APPS_RES_ID);
- }
+ waitUntilLauncherObjectGone(APPS_RES_ID);
waitUntilLauncherObjectGone(WORKSPACE_RES_ID);
waitUntilLauncherObjectGone(WIDGETS_RES_ID);
@@ -634,12 +643,20 @@
fail("Launcher didn't initialize");
}
+ Parcelable executeAndWaitForLauncherEvent(Runnable command,
+ UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
+ return executeAndWaitForEvent(
+ command,
+ e -> mLauncherPackage.equals(e.getPackageName()) && eventFilter.accept(e),
+ message);
+ }
+
Parcelable executeAndWaitForEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
try {
final AccessibilityEvent event =
mInstrumentation.getUiAutomation().executeAndWaitForEvent(
- command, eventFilter, WAIT_TIME_MS);
+ command, eventFilter, LONG_WAIT_TIME_MS);
assertNotNull("executeAndWaitForEvent returned null (this can't happen)", event);
final Parcelable parcelableData = event.getParcelableData();
event.recycle();
@@ -929,7 +946,7 @@
@NonNull
UiObject2 waitForAndroidObject(String resId) {
- final UiObject2 object = mDevice.wait(
+ final UiObject2 object = TestHelpers.wait(
Until.findObject(By.res(ANDROID_PACKAGE, resId)), WAIT_TIME_MS);
assertNotNull("Can't find a android object with id: " + resId, object);
return object;
@@ -986,7 +1003,7 @@
void runToState(Runnable command, int expectedState) {
final List<Integer> actualEvents = new ArrayList<>();
- executeAndWaitForEvent(
+ executeAndWaitForLauncherEvent(
command,
event -> isSwitchToStateEvent(event, expectedState, actualEvents),
() -> "Failed to receive an event for the state change: expected ["
@@ -1101,7 +1118,7 @@
return;
}
- executeAndWaitForEvent(
+ executeAndWaitForLauncherEvent(
() -> linearGesture(
startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
@@ -1289,30 +1306,13 @@
getTestInfo(TestProtocol.REQUEST_ENABLE_DEBUG_TRACING);
}
- public boolean hasAllAppsInOverview() {
- // Vertical bar layouts don't contain all apps
- if (!mDevice.isNaturalOrientation()) {
- return false;
- }
- // Portrait two button (quickstep) always has all apps.
- if (getNavigationModel() == NavigationModel.TWO_BUTTON) {
- return true;
- }
- // Overview actions hide all apps
- if (overviewActionsEnabled()) {
- return false;
- }
- // ...otherwise there should be all apps
- return true;
- }
-
- private boolean overviewActionsEnabled() {
- return getTestInfo(TestProtocol.REQUEST_OVERVIEW_ACTIONS_ENABLED).getBoolean(
+ boolean overviewShareEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
- boolean overviewShareEnabled() {
- return getTestInfo(TestProtocol.REQUEST_OVERVIEW_SHARE_ENABLED).getBoolean(
+ boolean overviewContentPushEnabled() {
+ return getTestInfo(TestProtocol.REQUEST_OVERVIEW_CONTENT_PUSH_ENABLED).getBoolean(
TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@@ -1381,7 +1381,7 @@
if (mCheckEventsForSuccessfulGestures) {
final String message = eventChecker.verify(WAIT_TIME_MS, true);
if (message != null) {
- dumpDiagnostics();
+ dumpDiagnostics(message);
checkForAnomaly();
Assert.fail(formatSystemHealthMessage(
"http://go/tapl : successful gesture produced " + message));
@@ -1394,7 +1394,7 @@
}
boolean isLauncher3() {
- return "com.android.launcher3".equals(getLauncherPackageName());
+ return mIsLauncher3;
}
void expectEvent(String sequence, Pattern expected) {
@@ -1413,4 +1413,4 @@
return null;
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
index 4440b82..ab6465c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
+++ b/tests/tapl/com/android/launcher3/tapl/LogEventChecker.java
@@ -57,6 +57,8 @@
while (true) {
rawEvents = mLauncher.getTestInfo(TestProtocol.REQUEST_GET_TEST_EVENTS)
.getStringArrayList(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ if (rawEvents == null) return null;
+
final int expectedCount = mExpectedEvents.entrySet()
.stream().mapToInt(e -> e.getValue().size()).sum();
if (rawEvents.size() >= expectedCount
@@ -83,6 +85,7 @@
String verify(long waitForExpectedCountMs, boolean successfulGesture) {
final ListMap<String> actualEvents = finishSync(waitForExpectedCountMs);
+ if (actualEvents == null) return "null event sequences because launcher likely died";
final StringBuilder sb = new StringBuilder();
boolean hasMismatches = false;
@@ -91,8 +94,7 @@
List<String> actual = new ArrayList<>(actualEvents.getNonNull(sequence));
final int mismatchPosition = getMismatchPosition(expectedEvents.getValue(), actual);
- hasMismatches = hasMismatches
- || mismatchPosition != -1 && !ignoreMistatch(successfulGesture, sequence);
+ hasMismatches = hasMismatches || mismatchPosition != -1;
formatSequenceWithMismatch(
sb,
sequence,
@@ -103,8 +105,7 @@
// Check for unexpected event sequences in the actual data.
for (String actualNamedSequence : actualEvents.keySet()) {
if (!mExpectedEvents.containsKey(actualNamedSequence)) {
- hasMismatches = hasMismatches
- || !ignoreMistatch(successfulGesture, actualNamedSequence);
+ hasMismatches = true;
formatSequenceWithMismatch(
sb,
actualNamedSequence,
@@ -117,13 +118,6 @@
return hasMismatches ? "mismatching events: " + sb.toString() : null;
}
- // Workaround for b/154157191
- private static boolean ignoreMistatch(boolean successfulGesture, String sequence) {
- // b/156287114
- return false;
-// return TestProtocol.SEQUENCE_TIS.equals(sequence) && successfulGesture;
- }
-
// If the list of actual events matches the list of expected events, returns -1, otherwise
// the position of the mismatch.
private static int getMismatchPosition(List<Pattern> expected, List<String> actual) {
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index a30a404..e3e0f42 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -34,6 +34,27 @@
}
/**
+ * Clicks content push button.
+ */
+ @NonNull
+ public Overview clickAndDismissContentPush() {
+ if (mLauncher.overviewContentPushEnabled()) {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "want to click content push button and exit screenshot ui")) {
+ UiObject2 exo = mLauncher.waitForObjectInContainer(mOverviewActions,
+ "action_content_push");
+ mLauncher.clickLauncherObject(exo);
+ try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+ "clicked content push button")) {
+ return new Overview(mLauncher);
+ }
+ }
+ }
+ return new Overview(mLauncher);
+ }
+
+ /**
* Clicks screenshot button and closes screenshot ui.
*/
@NonNull
diff --git a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
index b8791e8..7f6062f 100644
--- a/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
+++ b/tests/tapl/com/android/launcher3/tapl/TestHelpers.java
@@ -27,6 +27,11 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.os.DropBoxManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.uiautomator.SearchCondition;
+import androidx.test.uiautomator.UiDevice;
import org.junit.Assert;
@@ -35,6 +40,7 @@
public class TestHelpers {
+ private static final String TAG = "Tapl";
private static Boolean sIsInLauncherProcess;
public static boolean isInLauncherProcess() {
@@ -154,4 +160,12 @@
return null;
}
}
+
+ public static <R> R wait(SearchCondition<R> condition, long timeout) {
+ Log.d(TAG,
+ "TestHelpers.wait, condition=" + timeout + ", time=" + SystemClock.uptimeMillis());
+ final R result = UiDevice.getInstance(getInstrumentation()).wait(condition, timeout);
+ Log.d(TAG, "TestHelpers.wait, result=" + result + ", time=" + SystemClock.uptimeMillis());
+ return result;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 39ac645..22f4d31 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -27,9 +27,10 @@
import androidx.test.uiautomator.UiObject2;
import androidx.test.uiautomator.Until;
-import com.android.launcher3.tapl.LauncherInstrumentation.GestureScope;
+import com.android.launcher3.testing.TestProtocol;
import java.util.Collection;
+import java.util.List;
/**
* All widgets container.
@@ -90,54 +91,101 @@
return LauncherInstrumentation.ContainerType.WIDGETS;
}
+ private int getWidgetsScroll() {
+ return mLauncher.getTestInfo(
+ TestProtocol.REQUEST_WIDGETS_SCROLL_Y)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ }
+
public Widget getWidget(String labelText) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting widget " + labelText + " in widgets list")) {
- final UiObject2 widgetsContainer = verifyActiveContainer();
+ final UiObject2 fullWidgetsPicker = verifyActiveContainer();
mLauncher.assertTrue("Widgets container didn't become scrollable",
- widgetsContainer.wait(Until.scrollable(true), WAIT_TIME_MS));
+ fullWidgetsPicker.wait(Until.scrollable(true), WAIT_TIME_MS));
final Point displaySize = mLauncher.getRealDisplaySize();
- final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
+ final UiObject2 widgetsContainer = findTestAppWidgetsTableContainer();
+ mLauncher.assertTrue("Can't locate widgets list for the test app: "
+ + mLauncher.getLauncherPackageName(),
+ widgetsContainer != null);
+ final BySelector labelSelector = By.clazz("android.widget.TextView").text(labelText);
int i = 0;
for (; ; ) {
- final Collection<UiObject2> cells = mLauncher.getObjectsInContainer(
- widgetsContainer, "widgets_scroll_container");
- mLauncher.assertTrue("Widgets doesn't have 2 rows", cells.size() >= 2);
- for (UiObject2 cell : cells) {
- final UiObject2 label = cell.findObject(labelSelector);
- if (label == null) continue;
-
- final UiObject2 widget = label.getParent().getParent();
- mLauncher.assertEquals(
- "View is not WidgetCell",
- "com.android.launcher3.widget.WidgetCell",
- widget.getClassName());
-
- int maxWidth = 0;
- for (UiObject2 sibling : widget.getParent().getChildren()) {
- maxWidth = Math.max(mLauncher.getVisibleBounds(sibling).width(), maxWidth);
- }
-
- if (mLauncher.getVisibleBounds(widget).bottom
- <= displaySize.y - mLauncher.getBottomGestureSize()) {
- int visibleDelta = maxWidth - mLauncher.getVisibleBounds(widget).width();
- if (visibleDelta > 0) {
- Rect parentBounds = mLauncher.getVisibleBounds(cell);
- mLauncher.linearGesture(parentBounds.centerX() + visibleDelta
- + mLauncher.getTouchSlop(),
- parentBounds.centerY(), parentBounds.centerX(),
- parentBounds.centerY(), 10, true, GestureScope.INSIDE);
+ final Collection<UiObject2> tableRows = widgetsContainer.getChildren();
+ for (UiObject2 row : tableRows) {
+ final Collection<UiObject2> widgetCells = row.getChildren();
+ for (UiObject2 widget : widgetCells) {
+ final UiObject2 label = widget.findObject(labelSelector);
+ if (label == null) {
+ continue;
}
+ mLauncher.assertEquals(
+ "View is not WidgetCell",
+ "com.android.launcher3.widget.WidgetCell",
+ widget.getClassName());
return new Widget(mLauncher, widget);
}
}
mLauncher.assertTrue("Too many attempts", ++i <= 40);
- mLauncher.scrollToLastVisibleRow(widgetsContainer, cells, 0);
+ final int scroll = getWidgetsScroll();
+ mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, tableRows, 0);
+ final int newScroll = getWidgetsScroll();
+ mLauncher.assertTrue(
+ "Scrolled in a wrong direction in Widgets: from " + scroll + " to "
+ + newScroll, newScroll >= scroll);
+ mLauncher.assertTrue("Unable to scroll to the widget", newScroll != scroll);
}
}
}
+
+ /** Finds the widgets list of this test app from the collapsed full widgets picker. */
+ private UiObject2 findTestAppWidgetsTableContainer() {
+ final BySelector headerSelector = By.res(mLauncher.getLauncherPackageName(),
+ "widgets_list_header");
+ final BySelector targetAppSelector = By.clazz("android.widget.TextView").text(
+ mLauncher.getContext().getPackageName());
+ final BySelector widgetsContainerSelector = By.res(mLauncher.getLauncherPackageName(),
+ "widgets_table");
+
+ boolean hasHeaderExpanded = false;
+ for (int i = 0; i < 40; i++) {
+ UiObject2 fullWidgetsPicker = verifyActiveContainer();
+
+ UiObject2 header = fullWidgetsPicker.findObject(headerSelector);
+ mLauncher.assertTrue("Can't find a widget header", header != null);
+
+ // Look for a header that has the test app name.
+ UiObject2 headerTitle = fullWidgetsPicker.findObject(targetAppSelector);
+ if (headerTitle != null) {
+ // If we find the header and it has not been expanded, let's click it to see the
+ // widgets list.
+ if (!hasHeaderExpanded) {
+ hasHeaderExpanded = true;
+ mLauncher.clickLauncherObject(headerTitle);
+ // After clicking the header, the recyclerview has been updated. Let's refresh
+ // the container UIObject2.
+ fullWidgetsPicker = verifyActiveContainer();
+ // Refresh headerTitle because the first instance is stale after
+ // verifyActiveContainer call.
+ headerTitle = fullWidgetsPicker.findObject(targetAppSelector);
+ }
+
+ // Look for a widgets list.
+ UiObject2 widgetsContainer = fullWidgetsPicker.findObject(widgetsContainerSelector);
+ if (widgetsContainer != null) {
+ return widgetsContainer;
+ }
+ mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, List.of(headerTitle), 0);
+ } else {
+ mLauncher.scrollToLastVisibleRow(fullWidgetsPicker, fullWidgetsPicker.getChildren(),
+ 0);
+ }
+ }
+
+ return null;
+ }
}
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index f0e686f..d43e235 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -110,7 +110,8 @@
TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
LauncherInstrumentation.log(
- "switchToAllApps: swipeHeight = " + swipeHeight + ", slop = "
+ "switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY
+ + ", swipeHeight = " + swipeHeight + ", slop = "
+ mLauncher.getTouchSlop());
mLauncher.swipeToState(