Merge "Revert "Re-enabling Widgets in Launcher3Go"" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 0dc7ff1..dbafcbc 100644
--- a/Android.mk
+++ b/Android.mk
@@ -20,9 +20,13 @@
# Prebuilt Java Libraries
#
include $(CLEAR_VARS)
-LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
- libSharedSystemUI:quickstep/libs/sysui_shared.jar
-include $(BUILD_MULTI_PREBUILT)
+LOCAL_MODULE := libSharedSystemUI
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_SRC_FILES := quickstep/libs/sysui_shared.jar
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_SDK_VERSION := current
+include $(BUILD_PREBUILT)
#
# Build rule for Launcher3 app.
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index bb03f50..c24850d 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -87,9 +87,11 @@
android:process=":wallpaper_chooser"
android:permission="android.permission.BIND_JOB_SERVICE" />
- <service android:name="com.android.launcher3.notification.NotificationListener"
- android:enabled="@bool/notification_badging_enabled"
- android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <service
+ android:name="com.android.launcher3.notification.NotificationListener"
+ android:label="@string/icon_badging_service_title"
+ android:enabled="@bool/notification_badging_enabled"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 6ef7828..fde22eb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -34,7 +34,7 @@
<permission
android:name="com.android.launcher3.permission.READ_SETTINGS"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="normal"
+ android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_read_settings"
android:description="@string/permdesc_read_settings"/>
<permission
@@ -71,7 +71,7 @@
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:windowSoftInputMode="adjustPan"
- android:screenOrientation="nosensor"
+ android:screenOrientation="unspecified"
android:configChanges="keyboard|keyboardHidden|navigation"
android:resizeableActivity="true"
android:resumeWhilePausing="true"
diff --git a/proguard.flags b/proguard.flags
index b8cade5..086337d 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -39,7 +39,7 @@
public int getY();
}
--keep class com.android.launcher3.dragndrop.DragLayer$LayoutParams {
+-keep class com.android.launcher3.views.BaseDragLayer$LayoutParams {
public void setWidth(int);
public int getWidth();
public void setHeight(int);
@@ -102,6 +102,11 @@
public <init>(...);
}
+# InstantAppResolver
+-keep class com.android.quickstep.InstantAppResolverImpl {
+ public <init>(...);
+}
+
-keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
*;
}
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index de74fce..4013429 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -68,6 +68,8 @@
SEARCHBOX = 6;
EDITTEXT = 7;
NOTIFICATION = 8;
+ TASK = 9; // Each page of Recents UI (QuickStep)
+ WEB_APP = 10;
}
// Used to define what type of container a Target would represent.
@@ -78,11 +80,14 @@
FOLDER = 3;
ALLAPPS = 4;
WIDGETS = 5;
- OVERVIEW = 6;
+ 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)
}
// Used to define what type of control a Target would represent.
@@ -100,6 +105,7 @@
HOME_INTENT = 10; // Deprecated, use enum Command instead
BACK_BUTTON = 11; // Deprecated, use enum Command instead
// GO_TO_PLAYSTORE
+ QUICK_SCRUB_BUTTON = 12;
}
// Used to define the action component of the LauncherEvent.
@@ -141,6 +147,7 @@
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;
}
//
@@ -150,7 +157,6 @@
//
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;
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 02b4379..f62d1d6 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -36,13 +36,41 @@
android:restoreAnyVersion="true"
android:supportsRtl="true" >
- <service android:name="com.android.quickstep.TouchInteractionService"
- android:exported="true" />
+ <service
+ android:name="com.android.quickstep.TouchInteractionService"
+ android:permission="android.permission.STATUS_BAR_SERVICE" >
+ <intent-filter>
+ <action android:name="android.intent.action.QUICKSTEP_SERVICE" />
+ </intent-filter>
+ </service>
<!-- STOPSHIP: Change exported to false once all the integration is complete.
It is set to true so that the activity can be started from command line -->
<activity android:name="com.android.quickstep.RecentsActivity"
- android:exported="true" />
+ android:exported="true"
+ android:excludeFromRecents="true"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:theme="@style/LauncherTheme"
+ android:screenOrientation="unspecified"
+ android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:resizeableActivity="true"
+ android:resumeWhilePausing="true"
+ android:taskAffinity="" />
+
+ <!-- Content provider to settings search -->
+ <provider
+ android:name="com.android.quickstep.LauncherSearchIndexablesProvider"
+ android:authorities="com.android.launcher3"
+ 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" />
+ </intent-filter>
+ </provider>
</application>
</manifest>
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 0e9034a..b75a193 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index d4f995d..0000000
--- a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 9013c1a..0000000
--- a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 6e924ec..0000000
--- a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 33d0edd..0000000
--- a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 20c85e6..0000000
--- a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 58a1ca0..0000000
--- a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 9d3dc31..0000000
--- a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index 7fb248b..0000000
--- a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png
deleted file mode 100644
index 49ec7aa..0000000
--- a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png
deleted file mode 100644
index a8922e2..0000000
--- a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png
+++ /dev/null
Binary files differ
diff --git a/quickstep/res/drawable/ic_empty_recents.xml b/quickstep/res/drawable/ic_empty_recents.xml
new file mode 100644
index 0000000..5183733
--- /dev/null
+++ b/quickstep/res/drawable/ic_empty_recents.xml
@@ -0,0 +1,30 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="92dp"
+ android:height="80dp"
+ android:viewportWidth="92.0"
+ android:viewportHeight="80.0"
+ android:tint="?android:attr/textColorPrimary">
+ <path
+ android:fillColor="#AAFFFFFF"
+ android:pathData="M18,6H2C0.9,6,0,6.9,0,8v64c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C20,6.9,19.1,6,18,6z M16,69.25
+ c0,0.41-0.34,0.75-0.75,0.75H4.75C4.34,70,4,69.66,4,69.25v-58.5C4,10.34,4.34,10,4.75,10h10.5c0.41,0,0.75,0.34,0.75,0.75V69.25
+ z M90,6H74c-1.1,0-2,0.9-2,2v64c0,1.1,0.9,2,2,2h16c1.1,0,2-0.9,2-2V8C92,6.9,91.1,6,90,6z M88,69.25c0,0.41-0.34,0.75-0.75,0.75
+ h-10.5C76.34,70,76,69.66,76,69.25v-58.5c0-0.41,0.34-0.75,0.75-0.75h10.5c0.41,0,0.75,0.34,0.75,0.75V69.25z M64,0H28
+ c-2.21,0-4,1.79-4,4v72c0,2.21,1.79,4,4,4h36c2.21,0,4-1.79,4-4V4C68,1.79,66.21,0,64,0z M64,75c0,0.55-0.45,1-1,1H29
+ c-0.55,0-1-0.45-1-1V5c0-0.55,0.45-1,1-1h34c0.55,0,1,0.45,1,1V75z"/>
+</vector>
diff --git a/res/layout/widgets_bottom_sheet_scrim.xml b/quickstep/res/layout/drag_handle_indicator.xml
similarity index 63%
copy from res/layout/widgets_bottom_sheet_scrim.xml
copy to quickstep/res/layout/drag_handle_indicator.xml
index 6c6626c..9ee05d5 100644
--- a/res/layout/widgets_bottom_sheet_scrim.xml
+++ b/quickstep/res/layout/drag_handle_indicator.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,11 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<com.android.launcher3.graphics.GradientView
+<com.android.quickstep.views.QuickstepDragIndicator
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/gradient_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:layout_ignoreInsets="true" />
\ No newline at end of file
+ android:id="@+id/drag_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/accessibility_desc_recent_apps"
+ android:scaleType="centerInside"
+ android:tint="?attr/workspaceTextColor" />
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
new file mode 100644
index 0000000..22f8b55
--- /dev/null
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ 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.
+-->
+<com.android.quickstep.fallback.RecentsRootView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/drag_layer"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true">
+
+ <com.android.quickstep.fallback.FallbackRecentsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/overview_panel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:theme="@style/HomeScreenElementTheme" />
+
+</com.android.quickstep.fallback.RecentsRootView>
\ No newline at end of file
diff --git a/quickstep/res/layout/longpress_options_menu.xml b/quickstep/res/layout/longpress_options_menu.xml
deleted file mode 100644
index 9cf0fcf..0000000
--- a/quickstep/res/layout/longpress_options_menu.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<com.android.launcher3.uioverrides.OptionsPopupView
- 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="96dp"
- android:background="?attr/popupColorPrimary"
- android:elevation="@dimen/deep_shortcuts_elevation"
- android:orientation="horizontal"
- launcher:layout_ignoreInsets="true">
-
- <FrameLayout
- android:id="@+id/wallpaper_button"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="?android:attr/selectableItemBackground">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:drawablePadding="4dp"
- android:drawableTint="?android:attr/textColorPrimary"
- android:drawableTop="@drawable/ic_wallpaper"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:text="@string/wallpaper_button_text"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"/>
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/widget_button"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="?android:attr/selectableItemBackground">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:drawablePadding="4dp"
- android:drawableTint="?android:attr/textColorPrimary"
- android:drawableTop="@drawable/ic_widget"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:text="@string/widget_button_text"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"/>
-
- </FrameLayout>
-
- <FrameLayout
- android:id="@+id/settings_button"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:background="?android:attr/selectableItemBackground">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:drawablePadding="4dp"
- android:drawableTint="?android:attr/textColorPrimary"
- android:drawableTop="@drawable/ic_setting"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:text="@string/settings_button_text"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="12sp"/>
-
- </FrameLayout>
-
-</com.android.launcher3.uioverrides.OptionsPopupView>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 9f4f8a1..89e0571 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -14,18 +14,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.quickstep.RecentsView
+<com.android.quickstep.views.LauncherRecentsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
- android:alpha="0.0"
- android:visibility="invisible" >
+ android:visibility="invisible"
+ android:focusableInTouchMode="true" >
- <com.android.launcher3.uioverrides.WorkspaceCard
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
-</com.android.quickstep.RecentsView>
\ No newline at end of file
+</com.android.quickstep.views.LauncherRecentsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 91b6aa3..b8b360a 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -13,12 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.quickstep.TaskView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.quickstep.views.TaskView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="4dp">
- <com.android.quickstep.TaskThumbnailView
+ <com.android.quickstep.views.TaskThumbnailView
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -28,5 +28,6 @@
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
+ android:importantForAccessibility="no"
android:layout_gravity="top|center_horizontal" />
-</com.android.quickstep.TaskView>
\ No newline at end of file
+</com.android.quickstep.views.TaskView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
index 6e3fb4f..b846665 100644
--- a/quickstep/res/layout/task_menu.xml
+++ b/quickstep/res/layout/task_menu.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.quickstep.TaskMenuView
+<com.android.quickstep.views.TaskMenuView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="wrap_content"
@@ -33,4 +33,4 @@
android:paddingTop="18dp"
android:drawablePadding="8dp"
android:gravity="center_horizontal"/>
-</com.android.quickstep.TaskMenuView>
\ No newline at end of file
+</com.android.quickstep.views.TaskMenuView>
\ No newline at end of file
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
new file mode 100644
index 0000000..fa8f9dc
--- /dev/null
+++ b/quickstep/res/values-af/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Verdeelde skerm"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Speld vas"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swiep van onder af op om programme te wissel"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oorsig"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Geen onlangse items nie"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
new file mode 100644
index 0000000..d14e06c
--- /dev/null
+++ b/quickstep/res/values-am/strings.xml
@@ -0,0 +1,29 @@
+<?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">"የተከፈለ ማያ ገጽ"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ሰካ"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"መተግበሪያዎችን ለመቀያየር ከግርጌ ወደ ላይ በጣት ጠረግ ያድርጉ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ማጠቃለያ"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ምንም የቅርብ ጊዜ ንጥሎች የሉም"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
new file mode 100644
index 0000000..8efffd2
--- /dev/null
+++ b/quickstep/res/values-ar/strings.xml
@@ -0,0 +1,29 @@
+<?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">"تقسيم الشاشة"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"تثبيت"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"التمرير سريعًا لأعلى من أسفل للتبديل بين التطبيقات"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نظرة عامة"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ليست هناك عناصر تم استخدامها مؤخرًا"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
new file mode 100644
index 0000000..a832f9a
--- /dev/null
+++ b/quickstep/res/values-az/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Bölünmüş ekran"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Sancın"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Tətbiqləri dəyişmək üçün aşağıdan yuxarı doğru sürüşdürün"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"İcmal"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Son elementlər yoxdur"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..ba44830
--- /dev/null
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Podeljeni ekran"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Prevucite nagore da biste prešli na drugu aplikaciju"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
new file mode 100644
index 0000000..df55803
--- /dev/null
+++ b/quickstep/res/values-be/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Падзяліць экран"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Замацаваць"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Для пераключэння праграм правядзіце па экране пальцам знізу ўверх"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Агляд"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Няма новых элементаў"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
new file mode 100644
index 0000000..c46245c
--- /dev/null
+++ b/quickstep/res/values-bg/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Разделен екран"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Фиксиране"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Прекарайте пръст нагоре от долната част, за да превключите между приложенията"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Общ преглед"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Няма скорошни елементи"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
new file mode 100644
index 0000000..9080072
--- /dev/null
+++ b/quickstep/res/values-bn/strings.xml
@@ -0,0 +1,31 @@
+<?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">"স্ক্রিন স্প্লিট করুন"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"পিন করুন"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"অ্যাপগুলির মধ্যে সুইচ করতে উপর থেকে নিচের দিকে সোয়াইপ করুন"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
new file mode 100644
index 0000000..7e61277
--- /dev/null
+++ b/quickstep/res/values-bs/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Način rada podijeljenog ekrana"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Zakači"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Prevucite od dolje prema gore za promjenu aplikacije"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
new file mode 100644
index 0000000..ac77992
--- /dev/null
+++ b/quickstep/res/values-ca/strings.xml
@@ -0,0 +1,31 @@
+<?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">"Divideix la pantalla"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixa"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Llisca cap amunt des de la part inferior per canviar d\'aplicació"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
new file mode 100644
index 0000000..e8c0cb0
--- /dev/null
+++ b/quickstep/res/values-cs/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Rozdělená obrazovka"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"PIN"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Aplikace můžete přepínat přejetím zdola nahoru"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
new file mode 100644
index 0000000..6ddb31b
--- /dev/null
+++ b/quickstep/res/values-da/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Delt skærm"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fastgør"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Stryg opad fra bunden for at skifte apps"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversigt"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nye elementer"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
new file mode 100644
index 0000000..01c785e
--- /dev/null
+++ b/quickstep/res/values-de/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Bildschirm teilen"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixieren"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Zum Wechseln zwischen Apps vom unteren Bildschirmrand nach oben wischen"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Übersicht"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Keine kürzlich verwendeten Elemente"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
new file mode 100644
index 0000000..6b2a25f
--- /dev/null
+++ b/quickstep/res/values-el/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Διαχωρισμός οθόνης"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Καρφίτσωμα"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Σύρετε από κάτω προς τα επάνω για εναλλαγή εφαρμογών"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Επισκόπηση"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Δεν υπάρχουν πρόσφατα στοιχεία"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..402499e
--- /dev/null
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -0,0 +1,29 @@
+<?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="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swipe up from the bottom to switch apps"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..402499e
--- /dev/null
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -0,0 +1,29 @@
+<?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="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swipe up from the bottom to switch apps"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..402499e
--- /dev/null
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -0,0 +1,29 @@
+<?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="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swipe up from the bottom to switch apps"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"No recent items"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..1b9f926
--- /dev/null
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Pantalla dividida"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Desliza el dedo hacia arriba para cambiar de app"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recientes"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
new file mode 100644
index 0000000..c63f1d3
--- /dev/null
+++ b/quickstep/res/values-es/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Dividir pantalla"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fijar"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Desliza el dedo hacia arriba desde la parte inferior para cambiar de aplicación"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Aplicaciones recientes"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"No hay elementos recientes"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
new file mode 100644
index 0000000..30199b9
--- /dev/null
+++ b/quickstep/res/values-et/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Jagatud ekraan"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Kinnita"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Rakenduste vahetamiseks pühkige alaosast üles"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ülevaade"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Hiljutisi üksusi pole"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
new file mode 100644
index 0000000..b6386cd
--- /dev/null
+++ b/quickstep/res/values-eu/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Zatitu pantaila"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Ainguratu"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Aplikazioak aldatzeko, pasatu hatza pantailako behealdetik gora"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
new file mode 100644
index 0000000..52beadd
--- /dev/null
+++ b/quickstep/res/values-fa/strings.xml
@@ -0,0 +1,29 @@
+<?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">"تقسیم صفحه"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"پین"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"برای تغییر برنامهها، از پایین تند به بالا بکشید"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"نمای کلی"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"بدون موارد اخیر"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
new file mode 100644
index 0000000..a27a9cb
--- /dev/null
+++ b/quickstep/res/values-fi/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Jaettu näyttö"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Kiinnitä"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Vaihda sovellusta pyyhkäisemällä alareunasta ylös."</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Viimeisimmät"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Ei viimeaikaisia kohteita"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..8a603e9
--- /dev/null
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Écran divisé"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Balayez l\'écran du bas vers le haut pour changer d\'application"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
new file mode 100644
index 0000000..9192287
--- /dev/null
+++ b/quickstep/res/values-fr/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Écran partagé"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Épingler"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Balayer l\'écran de bas en haut pour changer d\'application"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
new file mode 100644
index 0000000..25d3796
--- /dev/null
+++ b/quickstep/res/values-gl/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Pantalla dividida"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Pasa o dedo cara arriba desde a parte inferior para cambiar de aplicacións"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
new file mode 100644
index 0000000..f463e13
--- /dev/null
+++ b/quickstep/res/values-gu/strings.xml
@@ -0,0 +1,31 @@
+<?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">"સ્ક્રીનને વિભાજિત કરો"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"પિન કરો"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ઍપને સ્વિચ કરવા માટે નીચેથી ઉપર સ્વાઇપ કરો"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
new file mode 100644
index 0000000..ee933d1
--- /dev/null
+++ b/quickstep/res/values-hi/strings.xml
@@ -0,0 +1,31 @@
+<?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">"स्क्रीन को दो हिस्सों में बाँटना (स्प्लिट स्क्रीन)"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करना"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ऐप्लिकेशन स्विच करने के लिए सबसे नीचे से ऊपर की ओर स्वाइप करें"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
new file mode 100644
index 0000000..a0b734f
--- /dev/null
+++ b/quickstep/res/values-hr/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Podijeljeni zaslon"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Prikvači"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Prijeđite prstom od dna prema gore da biste promijenili aplikaciju"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nema nedavnih stavki"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
new file mode 100644
index 0000000..8a465e2
--- /dev/null
+++ b/quickstep/res/values-hu/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Osztott képernyő"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Rögzítés"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Ha váltani szeretne az alkalmazások között, csúsztassa gyorsan az ujját a képernyő aljától felfelé"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
new file mode 100644
index 0000000..fdfe818
--- /dev/null
+++ b/quickstep/res/values-hy/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Տրոհել էկրանը"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Ամրացնել"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Սահեցրեք ներքևից վերև՝ մյուս հավելվածին անցնելու համար"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ընդհանուր տեղեկություններ"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Վերջին տարրեր չկան"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
new file mode 100644
index 0000000..786a10c
--- /dev/null
+++ b/quickstep/res/values-in/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Layar terpisah"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Pasang pin"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Geser dari bawah ke atas untuk beralih aplikasi"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ringkasan"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Tidak ada item baru-baru ini"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
new file mode 100644
index 0000000..b01a749
--- /dev/null
+++ b/quickstep/res/values-is/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Skipta skjá"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Festa"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Strjúktu upp til að skipta um forrit"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
new file mode 100644
index 0000000..0da2251
--- /dev/null
+++ b/quickstep/res/values-it/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Schermo diviso"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Blocca"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Scorri verso l\'alto dalla parte inferiore per cambiare app"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Panoramica"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nessun elemento recente"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
new file mode 100644
index 0000000..f7e8338
--- /dev/null
+++ b/quickstep/res/values-iw/strings.xml
@@ -0,0 +1,29 @@
+<?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">"מסך מפוצל"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"הצמדה"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"יש להחליק כלפי מעלה מהחלק התחתון כדי לעבור בין אפליקציות"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"מסכים אחרונים"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"אין פריטים אחרונים"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
new file mode 100644
index 0000000..7e14d2c
--- /dev/null
+++ b/quickstep/res/values-ja/strings.xml
@@ -0,0 +1,29 @@
+<?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">"分割画面"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"アプリを切り替えるには、下から上にスワイプします"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概要"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"最近のアイテムはありません"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
new file mode 100644
index 0000000..cf4c661
--- /dev/null
+++ b/quickstep/res/values-ka/strings.xml
@@ -0,0 +1,29 @@
+<?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">"ეკრანის გაყოფა"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ჩამაგრება"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"აპების გადასართავად გადაფურცლეთ ქვედა კიდედან ზემოთ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"მიმოხილვა"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ბოლოს გამოყენებული ერთეულები არ არის"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
new file mode 100644
index 0000000..f865a04
--- /dev/null
+++ b/quickstep/res/values-kk/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Экранды бөлу"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Бекіту"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Қолданбалар арасында ауысу үшін төменнен жоғары қарай саусақпен сырғытыңыз"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Шолу"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Соңғы элементтер жоқ"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
new file mode 100644
index 0000000..a35ab26
--- /dev/null
+++ b/quickstep/res/values-km/strings.xml
@@ -0,0 +1,29 @@
+<?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">"មុខងារបំបែកអេក្រង់"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ដៅ"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"អូសពីក្រោមឡើងលើ ដើម្បីប្ដូរកម្មវិធី"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ទិដ្ឋភាពរួម"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"មិនមានធាតុថ្មីៗទេ"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
new file mode 100644
index 0000000..dc57df1
--- /dev/null
+++ b/quickstep/res/values-kn/strings.xml
@@ -0,0 +1,29 @@
+<?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">"ಪರದೆಯನ್ನು ಬೇರ್ಪಡಿಸಿ"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ಪಿನ್ ಮಾಡಿ"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಬದಲಿಸಲು ಕೆಳಗಿನಿಂದ ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ಅವಲೋಕನ"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ಯಾವುದೇ ಇತ್ತೀಚಿನ ಐಟಂಗಳಿಲ್ಲ"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
new file mode 100644
index 0000000..36fd122
--- /dev/null
+++ b/quickstep/res/values-ko/strings.xml
@@ -0,0 +1,29 @@
+<?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">"화면 분할"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"고정"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"아래에서 위로 스와이프하여 앱을 전환합니다."</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"최근 사용"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"최근 항목이 없습니다."</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
new file mode 100644
index 0000000..060a8dd
--- /dev/null
+++ b/quickstep/res/values-ky/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Экранды бөлүү"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Кадап коюу"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Колдонмолорду которуштуруу үчүн экранды төмөндөн жогору карай сүрүңүз"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Сереп салуу"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Акыркы колдонмолор жок"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
new file mode 100644
index 0000000..1abb856
--- /dev/null
+++ b/quickstep/res/values-lo/strings.xml
@@ -0,0 +1,29 @@
+<?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">"ແບ່ງໜ້າຈໍ"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ປັກໝຸດ"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ປັດຂຶ້ນຈາກລຸ່ມສຸດເພື່ອສະຫຼັບແອັບ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ພາບຮວມ"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ບໍ່ມີລາຍການຫຼ້າສຸດ"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
new file mode 100644
index 0000000..0c09a94
--- /dev/null
+++ b/quickstep/res/values-lt/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Skaidyti ekraną"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Prisegti"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Perbraukite aukštyn iš apačios, kad perjungtumėte programas"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
new file mode 100644
index 0000000..72d1cb5
--- /dev/null
+++ b/quickstep/res/values-lv/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Sadalīt ekrānu"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Piespraust"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Lai pārslēgtu lietotnes, velciet augšup no apakšdaļas."</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pārskats"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nav nesenu vienumu."</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
new file mode 100644
index 0000000..06bf5d0
--- /dev/null
+++ b/quickstep/res/values-mk/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Поделен екран"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Прикачување"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Повлечете нагоре од дното за да ги смените апликациите"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Нема неодамнешни ставки"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
new file mode 100644
index 0000000..b936906
--- /dev/null
+++ b/quickstep/res/values-ml/strings.xml
@@ -0,0 +1,29 @@
+<?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">"സ്ക്രീൻ വിഭജിക്കുക"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"പിൻ ചെയ്യുക"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ആപ്പുകൾ മാറാൻ താഴെ നിന്ന് മുകളിലേക്ക് സ്വൈപ്പ് ചെയ്യുക"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"അവലോകനം"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"സമീപകാല ഇനങ്ങൾ ഒന്നുമില്ല"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
new file mode 100644
index 0000000..8b92214
--- /dev/null
+++ b/quickstep/res/values-mn/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Дэлгэцийг хуваах"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Тогтоох"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Аппыг сэлгэхийн тулд доороос дээш шударна уу"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Тойм"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Сүүлийн үеийн зүйл алга"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
new file mode 100644
index 0000000..596792d
--- /dev/null
+++ b/quickstep/res/values-mr/strings.xml
@@ -0,0 +1,29 @@
+<?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">"विभाजित स्क्रीन"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन करा"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"अॅप्स स्विच करण्यासाठी तळापासून वर स्वाइप करा"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"अवलोकन"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"कोणतेही अलीकडील आयटम नाहीत"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
new file mode 100644
index 0000000..336aaf6
--- /dev/null
+++ b/quickstep/res/values-ms/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Skrin pisah"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Semat"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Leret ke atas dari bawah untuk menukar apl"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Ikhtisar"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Tiada item terbaharu"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
new file mode 100644
index 0000000..d71e5fc
--- /dev/null
+++ b/quickstep/res/values-my/strings.xml
@@ -0,0 +1,29 @@
+<?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">"မျက်နှာပြင် ခွဲ၍ပြသခြင်း"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ပင်ထိုးခြင်း"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"အက်ပ်များပြောင်းရန် အောက်ခြေမှ အပေါ်သို့ပွတ်ဆွဲပါ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"အနှစ်ချုပ်"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"မကြာမီကဖွင့်ထားသည်များ မရှိပါ"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
new file mode 100644
index 0000000..504f43a
--- /dev/null
+++ b/quickstep/res/values-nb/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Delt skjerm"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fest"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Sveip opp fra bunnen for å bytte app"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Oversikt"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Ingen nylige elementer"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
new file mode 100644
index 0000000..7500213
--- /dev/null
+++ b/quickstep/res/values-ne/strings.xml
@@ -0,0 +1,29 @@
+<?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">"स्क्रिन विभाजन गर्नुहोस्"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"पिन गर्नुहोस्"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"अनुप्रयोगहरू बदल्न तलबाट माथितिर स्वाइप गर्नुहोस्"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"परिदृश्य"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"हालसालैको कुनै पनि वस्तु छैन"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
new file mode 100644
index 0000000..2ba24a6
--- /dev/null
+++ b/quickstep/res/values-nl/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Gesplitst scherm"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Vastzetten"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Veeg omhoog vanaf de onderkant om tussen apps te wisselen"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overzicht"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Geen recente items"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
new file mode 100644
index 0000000..fbcb60c
--- /dev/null
+++ b/quickstep/res/values-pa/strings.xml
@@ -0,0 +1,29 @@
+<?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">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ਪਿੰਨ ਕਰੋ"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ਐਪਾਂ ਵਿੱਚ ਅਦਲਾ-ਬਦਲੀ ਕਰਨ ਲਈ ਹੇਠਾਂ ਤੋਂ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ਰੂਪ-ਰੇਖਾ"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ਕੋਈ ਹਾਲੀਆ ਆਈਟਮਾਂ ਨਹੀਂ"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
new file mode 100644
index 0000000..1ad7070
--- /dev/null
+++ b/quickstep/res/values-pl/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Podziel ekran"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Przypnij"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Przesuń palcem z dołu ekranu, by przełączać aplikacje"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Przegląd"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Brak ostatnich elementów"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..a63d329
--- /dev/null
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Ecrã dividido"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Deslize rapidamente para cima a partir da parte inferior para alternar entre aplicações."</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Vista geral"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
new file mode 100644
index 0000000..05d20e0
--- /dev/null
+++ b/quickstep/res/values-pt/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Tela dividida"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixar"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Deslize de baixo para cima para alternar entre apps"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Visão geral"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Nenhum item recente"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
new file mode 100644
index 0000000..4264370
--- /dev/null
+++ b/quickstep/res/values-ro/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Ecran divizat"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fixați"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Glisați de jos în sus pentru a schimba aplicațiile"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Recente"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Niciun element recent"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
new file mode 100644
index 0000000..47ddff5
--- /dev/null
+++ b/quickstep/res/values-ru/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Разделить экран"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Блокировать"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Чтобы переключить приложение, проведите по экрану снизу вверх"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Обзор"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Недавних приложений нет."</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
new file mode 100644
index 0000000..a9b1493
--- /dev/null
+++ b/quickstep/res/values-si/strings.xml
@@ -0,0 +1,29 @@
+<?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">"බෙදුම් තිරය"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"අමුණන්න"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"යෙදුම් මාරු කිරීම සඳහා පහළ සිට ස්වයිප් කරන්න"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"දළ විශ්ලේෂණය"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"මෑත අයිතම නැත"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
new file mode 100644
index 0000000..fe02855
--- /dev/null
+++ b/quickstep/res/values-sk/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Rozdeliť obrazovku"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Pripnúť"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Aplikácie môžete prepínať potiahnutím prstom zdola nahor"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
new file mode 100644
index 0000000..72d52a5
--- /dev/null
+++ b/quickstep/res/values-sl/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Razdeljen zaslon"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Pripni"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Če želite preklopiti med aplikacijami, z dna zaslona s prstom povlecite navzgor"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Pregled"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Ni nedavnih elementov"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
new file mode 100644
index 0000000..954342c
--- /dev/null
+++ b/quickstep/res/values-sq/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Ekrani i ndarë"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Gozhdo"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Rrëshqit larg nga poshtë për të ndryshuar aplikacionet"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
new file mode 100644
index 0000000..51a9586
--- /dev/null
+++ b/quickstep/res/values-sr/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Подељени екран"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Закачи"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Превуците нагоре да бисте прешли на другу апликацију"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Преглед"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Нема недавних ставки"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
new file mode 100644
index 0000000..266cf60
--- /dev/null
+++ b/quickstep/res/values-sv/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Delad skärm"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Fäst"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Växla mellan appar genom att svepa uppåt från nederkanten"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
new file mode 100644
index 0000000..e85fa45
--- /dev/null
+++ b/quickstep/res/values-sw/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Gawa skrini"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Bandika"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Telezesha kidole juu kuanzia chini ili ubadilishe programu"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Muhtasari"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Hakuna vipengee vya hivi karibuni"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
new file mode 100644
index 0000000..de03ae6
--- /dev/null
+++ b/quickstep/res/values-ta/strings.xml
@@ -0,0 +1,29 @@
+<?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">"திரைப் பிரிப்பு"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"பின் செய்தல்"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ஆப்ஸிற்கு இடையே மாற்றுவதற்கு, கீழிருந்து மேல்நோக்கி ஸ்வைப் செய்க"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
new file mode 100644
index 0000000..108f350
--- /dev/null
+++ b/quickstep/res/values-te/strings.xml
@@ -0,0 +1,29 @@
+<?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">"స్క్రీన్ని విభజించు"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"పిన్ చేయి"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"యాప్లను మార్చడానికి దిగువ నుండి పైకి స్వైప్ చేయండి"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"అవలోకనం"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ఇటీవలి అంశాలు ఏవీ లేవు"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
new file mode 100644
index 0000000..80f91b0
--- /dev/null
+++ b/quickstep/res/values-th/strings.xml
@@ -0,0 +1,29 @@
+<?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">"แยกหน้าจอ"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"ตรึง"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"เลื่อนขึ้นจากด้านล่างเพื่อสลับแอป"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"ภาพรวม"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"ไม่มีรายการล่าสุด"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
new file mode 100644
index 0000000..b28e04e
--- /dev/null
+++ b/quickstep/res/values-tl/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Hatiin ang screen"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"I-pin"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Mag-swipe pataas mula sa ibaba para lumipat ng app"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Overview"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Walang kamakailang item"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
new file mode 100644
index 0000000..1399353
--- /dev/null
+++ b/quickstep/res/values-tr/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Bölünmüş ekran"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Sabitle"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Uygulamaları değiştirmek için alttan yukarı kaydırın"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Genel bakış"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Yeni öğe yok"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
new file mode 100644
index 0000000..929bbe7
--- /dev/null
+++ b/quickstep/res/values-uk/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Розділити екран"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Закріпити"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Щоб переходити між додатками, проводьте пальцем знизу вгору"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Огляд"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Немає нещодавніх додатків"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
new file mode 100644
index 0000000..0271fe4
--- /dev/null
+++ b/quickstep/res/values-ur/strings.xml
@@ -0,0 +1,31 @@
+<?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">"اسپلٹ اسکرین وضع"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"پن کریں"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"ایپس کو سوئچ کرنے کیلئے نیچے سے اوپر سوائپ کریں"</string>
+ <!-- no translation found for accessibility_desc_recent_apps (1444379410873162882) -->
+ <skip />
+ <!-- no translation found for recents_empty_message (7040467240571714191) -->
+ <skip />
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
new file mode 100644
index 0000000..91e11d3
--- /dev/null
+++ b/quickstep/res/values-uz/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Ekranni ikkiga ajratish"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Mahkamlash"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Ilovalarni almashtirish uchun pastdan yuqoriga suring"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Nazar"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Yaqinda ishlatilgan ilovalar yo‘q"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
new file mode 100644
index 0000000..809517a
--- /dev/null
+++ b/quickstep/res/values-vi/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Chia đôi màn hình"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Ghim"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Vuốt từ dưới lên để chuyển đổi ứng dụng"</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>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..a44dd2d
--- /dev/null
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -0,0 +1,29 @@
+<?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">"分屏"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"从屏幕底部向上滑动即可切换应用"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概览"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"近期没有任何内容"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..3879bc5
--- /dev/null
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -0,0 +1,29 @@
+<?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">"分割畫面"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"從螢幕底部向上快速滑動,即可切換應用程式"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"概覽"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..f275168
--- /dev/null
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -0,0 +1,29 @@
+<?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">"分割畫面"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"固定"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"從畫面底部向上滑動以切換應用程式"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"總覽"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"最近沒有任何項目"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
new file mode 100644
index 0000000..206718e
--- /dev/null
+++ b/quickstep/res/values-zu/strings.xml
@@ -0,0 +1,29 @@
+<?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">"Hlukanisa isikrini"</string>
+ <string name="recent_task_option_pin" msgid="7929860679018978258">"Phina"</string>
+ <string name="recents_swipe_up_onboarding" msgid="1025535041275136564">"Swayiphela phezulu kusukela phansi ukuze ushintshe izinhlelo zokusebenza"</string>
+ <string name="accessibility_desc_recent_apps" msgid="1444379410873162882">"Buka konke"</string>
+ <string name="recents_empty_message" msgid="7040467240571714191">"Azikho izinto zakamuva"</string>
+ <!-- no translation found for accessibility_close_task (5354563209433803643) -->
+ <skip />
+</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d8504f1..ab6d8af 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -16,8 +16,6 @@
<resources>
- <dimen name="options_menu_icon_size">24dp</dimen>
-
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<dimen name="task_menu_background_radius">12dp</dimen>
@@ -25,16 +23,18 @@
<dimen name="task_fade_length">20dp</dimen>
<dimen name="recents_page_spacing">10dp</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>
<dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
<dimen name="quickstep_fling_min_velocity">250dp</dimen>
- <dimen name="workspace_overview_offset_x">-24dp</dimen>
-
- <!-- TODO: This can be calculated using other resource values -->
- <dimen name="all_apps_search_box_full_height">90dp</dimen>
-
<!-- Launcher app transition -->
<dimen name="content_trans_y">25dp</dimen>
<dimen name="workspace_trans_y">80dp</dimen>
+
+ <dimen name="recents_empty_message_text_size">16sp</dimen>
+ <dimen name="recents_empty_message_text_padding">16dp</dimen>
+
</resources>
diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml
index ba99d81..2bd9f8f 100644
--- a/quickstep/res/values/override.xml
+++ b/quickstep/res/values/override.xml
@@ -16,5 +16,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
+
+ <string name="instant_app_resolver_class" translatable="false">com.android.quickstep.InstantAppResolverImpl</string>
</resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index c2d6604..7ba91b3 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -26,4 +26,16 @@
<string name="recent_task_option_split_screen">Split screen</string>
<!-- Title for an option to keep an app pinned to the screen until it is unpinned -->
<string name="recent_task_option_pin">Pin</string>
+
+ <!-- Text that shows above the navigation bar after launching a few apps -->
+ <string name="recents_swipe_up_onboarding">Swipe up from the bottom to switch apps</string>
+
+ <!-- Content description for the recent apps panel (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_desc_recent_apps">Overview</string>
+
+ <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
+ <string name="recents_empty_message">No recent items</string>
+
+ <!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_close_task">Close</string>
</resources>
\ No newline at end of file
diff --git a/quickstep/res/xml/indexable_launcher_prefs.xml b/quickstep/res/xml/indexable_launcher_prefs.xml
new file mode 100644
index 0000000..2655402
--- /dev/null
+++ b/quickstep/res/xml/indexable_launcher_prefs.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <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"
+ />
+
+ <ListPreference
+ android:key="pref_override_icon_shape"
+ android:title="@string/icon_shape_override_label"
+ android:summary="@string/icon_shape_override_label_location"
+ android:entries="@array/icon_shape_override_paths_names"
+ android:entryValues="@array/icon_shape_override_paths_values"
+ android:defaultValue=""
+ android:persistent="false" />
+
+</PreferenceScreen>
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 489e55b..f919339 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -15,28 +15,92 @@
*/
package com.android.launcher3;
+import static com.android.systemui.shared.recents.utilities.Utilities
+ .postAtFrontOfQueueAsynchronously;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.annotation.TargetApi;
+import android.os.Build;
import android.os.Handler;
+import android.support.annotation.BinderThread;
+import android.support.annotation.UiThread;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+@TargetApi(Build.VERSION_CODES.P)
+public abstract class LauncherAnimationRunner extends AnimatorListenerAdapter
+ implements RemoteAnimationRunnerCompat {
-public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+ private static final int REFRESH_RATE_MS = 16;
- AnimatorSet mAnimator;
- private Launcher mLauncher;
+ private final Handler mHandler;
- LauncherAnimationRunner(Launcher launcher) {
- mLauncher = launcher;
+ private Runnable mSysFinishRunnable;
+
+ private AnimatorSet mAnimator;
+
+ public LauncherAnimationRunner(Handler handler) {
+ mHandler = handler;
}
+ @BinderThread
+ @Override
+ public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
+ postAtFrontOfQueueAsynchronously(mHandler, () -> {
+ // Finish any previous animation
+ finishSystemAnimation();
+
+ mSysFinishRunnable = runnable;
+ mAnimator = getAnimator(targetCompats);
+ if (mAnimator == null) {
+ finishSystemAnimation();
+ return;
+ }
+ mAnimator.addListener(this);
+ mAnimator.start();
+ // Because t=0 has the app icon in its original spot, we can skip the
+ // first frame and have the same movement one frame earlier.
+ mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
+
+ });
+ }
+
+
+ @UiThread
+ public abstract AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats);
+
+ @UiThread
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (animation == mAnimator) {
+ mAnimator = null;
+ finishSystemAnimation();
+ }
+ }
+
+ /**
+ * Called by the system
+ */
+ @BinderThread
@Override
public void onAnimationCancelled() {
- postAtFrontOfQueueAsynchronously(mLauncher.getWindow().getDecorView().getHandler(), () -> {
+ postAtFrontOfQueueAsynchronously(mHandler, () -> {
if (mAnimator != null) {
- mAnimator.cancel();
+ mAnimator.removeListener(this);
+ mAnimator.end();
+ mAnimator = null;
}
});
}
+
+ @UiThread
+ private void finishSystemAnimation() {
+ if (mSysFinishRunnable != null) {
+ mSysFinishRunnable.run();
+ mSysFinishRunnable = null;
+ }
+ }
}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 62e76d8..af81a59 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -16,10 +16,13 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.systemui.shared.recents.utilities.Utilities.getNextFrameNumber;
import static com.android.systemui.shared.recents.utilities.Utilities.getSurface;
-import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+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;
@@ -27,28 +30,36 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Rect;
-import android.media.midi.MidiManager.OnDeviceOpenedListener;
+import android.graphics.drawable.Drawable;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
-import android.widget.ImageView;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.quickstep.RecentsAnimationInterpolator;
+import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
@@ -67,36 +78,50 @@
implements OnDeviceProfileChangeListener {
private static final String TAG = "LauncherTransition";
- private static final int REFRESH_RATE_MS = 16;
+ private static final int STATUS_BAR_TRANSITION_DURATION = 120;
private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
"android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
- private static final int LAUNCHER_RESUME_START_DELAY = 150;
+ private static final int APP_LAUNCH_DURATION = 500;
+ // Use a shorter duration for x or y translation to create a curve effect
+ private static final int APP_LAUNCH_CURVED_DURATION = 233;
+ private static final int RECENTS_LAUNCH_DURATION = 336;
+ private static final int LAUNCHER_RESUME_START_DELAY = 100;
private static final int CLOSING_TRANSITION_DURATION_MS = 350;
// Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
- private static final float ALL_APPS_PROGRESS_START = 1.3059858f;
- private static final float ALL_APPS_PROGRESS_SLIDE_END = 0.99581414f;
+ public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
+ public static final float ALL_APPS_PROGRESS_OVERSHOOT = 0.99581414f;
private final DragLayer mDragLayer;
private final Launcher mLauncher;
- private DeviceProfile mDeviceProfile;
+
+ private final Handler mHandler;
+ private final boolean mIsRtl;
private final float mContentTransY;
private final float mWorkspaceTransY;
- private ImageView mFloatingView;
- private boolean mIsRtl;
+ private DeviceProfile mDeviceProfile;
+ private View mFloatingView;
- private Animator mCurrentAnimator;
+ private RemoteAnimationRunnerCompat mRemoteAnimationOverride;
+
+ private final AnimatorListenerAdapter mReapplyStateListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getStateManager().reapplyState();
+ }
+ };
public LauncherAppTransitionManagerImpl(Context context) {
mLauncher = Launcher.getLauncher(context);
mDragLayer = mLauncher.getDragLayer();
+ mHandler = new Handler(Looper.getMainLooper());
+ mIsRtl = Utilities.isRtl(mLauncher.getResources());
mDeviceProfile = mLauncher.getDeviceProfile();
- mIsRtl = Utilities.isRtl(mLauncher.getResources());
Resources res = mLauncher.getResources();
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
@@ -111,59 +136,41 @@
mDeviceProfile = dp;
}
- private void setCurrentAnimator(Animator animator) {
- if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
- mCurrentAnimator.cancel();
- }
- mCurrentAnimator = animator;
- }
-
/**
- * @return A Bundle with remote animations that controls how the window of the opening
+ * @return ActivityOptions with remote animations that controls how the window of the opening
* targets are displayed.
*/
@Override
- public Bundle getActivityLaunchOptions(Launcher launcher, View v) {
+ public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
if (hasControlRemoteAppTransitionPermission()) {
try {
- RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) {
+ RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler) {
+
@Override
- public void onAnimationStart(RemoteAnimationTargetCompat[] targets,
- Runnable finishedCallback) {
- // Post at front of queue ignoring sync barriers to make sure it gets
- // processed before the next frame.
- postAtFrontOfQueueAsynchronously(v.getHandler(), () -> {
- mAnimator = new AnimatorSet();
- setCurrentAnimator(mAnimator);
- mAnimator.play(getLauncherAnimators(v));
- mAnimator.play(getWindowAnimators(v, targets));
- mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- // Reset launcher to normal state
- v.setVisibility(View.VISIBLE);
- ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView);
+ public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+ AnimatorSet anim = new AnimatorSet();
- mDragLayer.setAlpha(1f);
- mDragLayer.setTranslationY(0f);
- View appsView = mLauncher.getAppsView();
- appsView.setAlpha(1f);
- appsView.setTranslationY(0f);
+ if (!composeRecentsLaunchAnimator(v, targetCompats, anim)) {
+ // Set the state animation first so that any state listeners are called
+ // before our internal listeners.
+ mLauncher.getStateManager().setCurrentAnimation(anim);
- finishedCallback.run();
- }
- });
- mAnimator.start();
- // Because t=0 has the app icon in its original spot, we can skip the
- // first frame and have the same movement one frame earlier.
- mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
- });
+ anim.play(getIconAnimator(v));
+ if (launcherIsATargetWithMode(targetCompats, MODE_CLOSING)) {
+ anim.play(getLauncherContentAnimator(false /* show */));
+ }
+ anim.play(getWindowAnimators(v, targetCompats));
+ }
+ return anim;
}
};
- return ActivityOptionsCompat.makeRemoteAnimation(
- new RemoteAnimationAdapterCompat(runner, 500, 380)).toBundle();
+ int duration = findTaskViewToLaunch(launcher, v, null) != null
+ ? RECENTS_LAUNCH_DURATION : APP_LAUNCH_DURATION;
+ int statusBarTransitionDelay = duration - STATUS_BAR_TRANSITION_DURATION;
+ return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
+ runner, duration, statusBarTransitionDelay));
} catch (NoClassDefFoundError e) {
// Gracefully fall back to default launch options if the user's platform doesn't
// have the latest changes.
@@ -172,14 +179,196 @@
return getDefaultActivityLaunchOptions(launcher, v);
}
+ public void setRemoteAnimationOverride(RemoteAnimationRunnerCompat remoteAnimationOverride) {
+ mRemoteAnimationOverride = remoteAnimationOverride;
+ }
+
/**
- * @return Animators that control the movements of the Launcher and icon of the opening target.
+ * 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.
*/
- private AnimatorSet getLauncherAnimators(View v) {
- AnimatorSet launcherAnimators = new AnimatorSet();
- launcherAnimators.play(getLauncherContentAnimator(false /* show */));
- launcherAnimators.play(getIconAnimator(v));
- return launcherAnimators;
+ private TaskView findTaskViewToLaunch(
+ BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
+ if (v instanceof TaskView) {
+ return (TaskView) v;
+ }
+ RecentsView recentsView = activity.getOverviewPanel();
+
+ // 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();
+ if (componentName != null) {
+ for (int i = 0; i < recentsView.getChildCount(); i++) {
+ TaskView taskView = (TaskView) recentsView.getPageAt(i);
+ if (recentsView.isTaskViewVisible(taskView)) {
+ Task task = taskView.getTask();
+ if (componentName.equals(task.key.getComponent())) {
+ 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;
+ }
+
+ /**
+ * Composes the animations for a launch from the recents list if possible.
+ */
+ private boolean composeRecentsLaunchAnimator(View v,
+ RemoteAnimationTargetCompat[] targets, AnimatorSet target) {
+ // Ensure recents is actually visible
+ if (!mLauncher.getStateManager().getState().overviewUi) {
+ return false;
+ }
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ boolean launcherClosing = launcherIsATargetWithMode(targets, MODE_CLOSING);
+ boolean skipLauncherChanges = !launcherClosing;
+
+ TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
+ if (taskView == null) {
+ return false;
+ }
+
+ // 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 = mReapplyStateListener;
+ } else {
+ AnimatorPlaybackController controller =
+ mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(NORMAL, RECENTS_LAUNCH_DURATION);
+ controller.dispatchOnStart();
+ launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+ windowAnimEndListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mLauncher.getStateManager().goToState(NORMAL, false);
+ }
+ };
+ }
+
+ target.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets));
+ target.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(target);
+ target.addListener(windowAnimEndListener);
+ return true;
+ }
+
+ /**
+ * @return Animator that controls the window of the opening targets for the recents launch
+ * animation.
+ */
+ private ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipLauncherChanges,
+ RemoteAnimationTargetCompat[] targets) {
+ final RecentsAnimationInterpolator recentsInterpolator = v.getRecentsInterpolator();
+
+ Rect crop = new Rect();
+ Matrix matrix = new Matrix();
+
+ ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+ appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+ appAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ boolean isFirstFrame = true;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final Surface surface = getSurface(v);
+ final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
+ if (frameNumber == -1) {
+ // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
+ Log.w(TAG, "Failed to animate, surface got destroyed.");
+ return;
+ }
+ final float percent = animation.getAnimatedFraction();
+ TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
+
+ float alphaDuration = 75;
+ if (!skipLauncherChanges) {
+ v.setScaleX(tw.taskScale);
+ v.setScaleY(tw.taskScale);
+ v.setTranslationX(tw.taskX);
+ v.setTranslationY(tw.taskY);
+ // Defer fading out the view until after the app window gets faded in
+ v.setAlpha(getValue(1f, 0f, alphaDuration, alphaDuration,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR));
+ }
+
+ matrix.setScale(tw.winScale, tw.winScale);
+ matrix.postTranslate(tw.winX, tw.winY);
+ crop.set(tw.winCrop);
+
+ // Fade in the app window.
+ float alpha = getValue(0f, 1f, 0, alphaDuration,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR);
+
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+ t.setAlpha(target.leash, alpha);
+
+ // TODO: This isn't correct at the beginning of the animation, but better
+ // than nothing.
+ matrix.postTranslate(target.position.x, target.position.y);
+ t.setMatrix(target.leash, matrix);
+ t.setWindowCrop(target.leash, crop);
+
+ if (!skipLauncherChanges) {
+ t.deferTransactionUntil(target.leash, surface, frameNumber);
+ }
+ }
+ if (isFirstFrame) {
+ t.show(target.leash);
+ }
+ }
+ t.apply();
+
+ matrix.reset();
+ isFirstFrame = false;
+ }
+ });
+ return appAnimator;
}
/**
@@ -200,7 +389,9 @@
if (mLauncher.isInState(LauncherState.ALL_APPS) && !mDeviceProfile.isVerticalBarLayout()) {
// All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
- View appsView = mLauncher.getAppsView();
+ final View appsView = mLauncher.getAppsView();
+ final float startAlpha = appsView.getAlpha();
+ final float startY = appsView.getTranslationY();
appsView.setAlpha(alphas[0]);
appsView.setTranslationY(trans[0]);
@@ -213,6 +404,14 @@
launcherAnimator.play(alpha);
launcherAnimator.play(transY);
+
+ launcherAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ appsView.setAlpha(startAlpha);
+ appsView.setTranslationY(startY);
+ }
+ });
} else {
mDragLayer.setAlpha(alphas[0]);
mDragLayer.setTranslationY(trans[0]);
@@ -227,6 +426,13 @@
launcherAnimator.play(dragLayerAlpha);
launcherAnimator.play(dragLayerTransY);
+ launcherAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mDragLayer.setAlpha(1);
+ mDragLayer.setTranslationY(0);
+ }
+ });
}
return launcherAnimator;
}
@@ -235,31 +441,56 @@
* @return Animator that controls the icon used to launch the target.
*/
private AnimatorSet getIconAnimator(View v) {
- boolean isBubbleTextView = v instanceof BubbleTextView;
- mFloatingView = new ImageView(mLauncher);
- if (isBubbleTextView) {
+ final boolean isBubbleTextView = v instanceof BubbleTextView;
+ mFloatingView = new View(mLauncher);
+ if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
// Create a copy of the app icon
- Bitmap iconBitmap = ((FastBitmapDrawable) ((BubbleTextView) v).getIcon()).getBitmap();
- mFloatingView.setImageDrawable(new FastBitmapDrawable(iconBitmap));
+ mFloatingView.setBackground(
+ DrawableFactory.get(mLauncher).newIcon((ItemInfoWithIcon) v.getTag()));
}
// Position the floating view exactly on top of the original
Rect rect = new Rect();
- mDragLayer.getDescendantRectRelativeToSelf(v, rect);
- int viewLocationStart = mIsRtl
- ? mDeviceProfile.widthPx - rect.right
- : rect.left;
+ final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
+ if (fromDeepShortcutView) {
+ // Deep shortcut views have their icon drawn in a separate view.
+ DeepShortcutView view = (DeepShortcutView) v.getParent();
+ mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
+ } else {
+ mDragLayer.getDescendantRectRelativeToSelf(v, rect);
+ }
+ int viewLocationLeft = rect.left;
int viewLocationTop = rect.top;
- if (isBubbleTextView) {
- ((BubbleTextView) v).getIconBounds(rect);
+ float startScale = 1f;
+ if (isBubbleTextView && !fromDeepShortcutView) {
+ BubbleTextView btv = (BubbleTextView) v;
+ btv.getIconBounds(rect);
+ Drawable dr = btv.getIcon();
+ if (dr instanceof FastBitmapDrawable) {
+ startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+ }
+ } else {
+ rect.set(0, 0, rect.width(), rect.height());
}
+ viewLocationLeft += rect.left;
+ viewLocationTop += rect.top;
+ int viewLocationStart = mIsRtl
+ ? mDeviceProfile.widthPx - rect.right
+ : viewLocationLeft;
LayoutParams lp = new LayoutParams(rect.width(), rect.height());
lp.ignoreInsets = true;
- lp.setMarginStart(viewLocationStart + rect.left);
- lp.topMargin = viewLocationTop + rect.top;
+ lp.setMarginStart(viewLocationStart);
+ lp.topMargin = viewLocationTop;
mFloatingView.setLayoutParams(lp);
+ // Set the properties here already to make sure they'are available when running the first
+ // animation frame.
+ mFloatingView.setLeft(viewLocationLeft);
+ mFloatingView.setTop(viewLocationTop);
+ mFloatingView.setRight(viewLocationLeft + rect.width());
+ mFloatingView.setBottom(viewLocationTop + rect.height());
+
// Swap the two views in place.
((ViewGroup) mDragLayer.getParent()).addView(mFloatingView);
v.setVisibility(View.INVISIBLE);
@@ -280,8 +511,10 @@
// Adjust the duration to change the "curve" of the app icon to the center.
boolean isBelowCenterY = lp.topMargin < centerY;
- x.setDuration(isBelowCenterY ? 500 : 233);
- y.setDuration(isBelowCenterY ? 233 : 500);
+ x.setDuration(isBelowCenterY ? APP_LAUNCH_DURATION : APP_LAUNCH_CURVED_DURATION);
+ y.setDuration(isBelowCenterY ? APP_LAUNCH_CURVED_DURATION : APP_LAUNCH_DURATION);
+ x.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ y.setInterpolator(Interpolators.AGGRESSIVE_EASE);
appIconAnimatorSet.play(x);
appIconAnimatorSet.play(y);
@@ -290,22 +523,26 @@
float maxScaleX = mDeviceProfile.widthPx / (float) rect.width();
float maxScaleY = mDeviceProfile.heightPx / (float) rect.height();
float scale = Math.max(maxScaleX, maxScaleY);
- ObjectAnimator sX = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_X, 1f, scale);
- ObjectAnimator sY = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_Y, 1f, scale);
- sX.setDuration(500);
- sY.setDuration(500);
- appIconAnimatorSet.play(sX);
- appIconAnimatorSet.play(sY);
+ ObjectAnimator scaleAnim = ObjectAnimator
+ .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
+ scaleAnim.setDuration(APP_LAUNCH_DURATION).setInterpolator(Interpolators.EXAGGERATED_EASE);
+ appIconAnimatorSet.play(scaleAnim);
// Fade out the app icon.
ObjectAnimator alpha = ObjectAnimator.ofFloat(mFloatingView, View.ALPHA, 1f, 0f);
- alpha.setStartDelay(17);
- alpha.setDuration(33);
+ alpha.setStartDelay(32);
+ alpha.setDuration(50);
+ alpha.setInterpolator(Interpolators.LINEAR);
appIconAnimatorSet.play(alpha);
- for (Animator a : appIconAnimatorSet.getChildAnimations()) {
- a.setInterpolator(Interpolators.AGGRESSIVE_EASE);
- }
+ appIconAnimatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Reset launcher to normal state
+ v.setVisibility(View.VISIBLE);
+ ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView);
+ }
+ });
return appIconAnimatorSet;
}
@@ -314,7 +551,11 @@
*/
private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
Rect bounds = new Rect();
- if (v instanceof BubbleTextView) {
+ if (v.getParent() instanceof DeepShortcutView) {
+ // Deep shortcut views have their icon drawn in a separate view.
+ DeepShortcutView view = (DeepShortcutView) v.getParent();
+ mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds);
+ } else if (v instanceof BubbleTextView) {
((BubbleTextView) v).getIconBounds(bounds);
} else {
mDragLayer.getDescendantRectRelativeToSelf(v, bounds);
@@ -325,7 +566,7 @@
Matrix matrix = new Matrix();
ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
- appAnimator.setDuration(500);
+ appAnimator.setDuration(APP_LAUNCH_DURATION);
appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
boolean isFirstFrame = true;
@@ -365,10 +606,9 @@
matrix.postTranslate(transX0, transY0);
// Fade in the app window.
- float alphaDelay = 0;
- float alphaDuration = 50;
- float alpha = getValue(0f, 1f, alphaDelay, alphaDuration,
- appAnimator.getDuration() * percent, Interpolators.AGGRESSIVE_EASE);
+ float alphaDuration = 60;
+ float alpha = getValue(0f, 1f, 0, alphaDuration,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR);
// Animate the window crop so that it starts off as a square, and then reveals
// horizontally.
@@ -381,7 +621,7 @@
TransactionCompat t = new TransactionCompat();
for (RemoteAnimationTargetCompat target : targets) {
- if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+ if (target.mode == MODE_OPENING) {
t.setAlpha(target.leash, alpha);
// TODO: This isn't correct at the beginning of the animation, but better
@@ -408,10 +648,12 @@
* Registers remote animations used when closing apps to home screen.
*/
private void registerRemoteAnimations() {
+ // Unregister this
if (hasControlRemoteAppTransitionPermission()) {
try {
RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
+ WindowManagerWrapper.ACTIVITY_TYPE_STANDARD,
new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
@@ -424,41 +666,71 @@
}
}
+ private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+ int launcherTaskId = mLauncher.getTaskId();
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == mode && target.taskId == launcherTaskId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* @return Runner that plays when user goes to Launcher
* ie. pressing home, swiping up from nav bar.
*/
private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
- return new LauncherAnimationRunner(mLauncher) {
+ return new LauncherAnimationRunner(mHandler) {
@Override
- public void onAnimationStart(RemoteAnimationTargetCompat[] targets,
- Runnable finishedCallback) {
- Handler handler = mLauncher.getWindow().getDecorView().getHandler();
- postAtFrontOfQueueAsynchronously(handler, () -> {
- if (Utilities.getPrefs(mLauncher).getBoolean("pref_use_screenshot_animation",
- true) && mLauncher.isInState(LauncherState.OVERVIEW)) {
- // We use a separate transition for Overview mode.
- setCurrentAnimator(null);
- finishedCallback.run();
- return;
- }
+ public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
+ Runnable runnable) {
+ if (mLauncher.getStateManager().getState().overviewUi
+ && mRemoteAnimationOverride != null) {
+ // This transition is only used for the fallback activity and should not be
+ // managed here (but necessary to implement here since the defined remote
+ // animation currently takes precendence over the one defined in the activity
+ // options).
+ mRemoteAnimationOverride.onAnimationStart(targetCompats, runnable);
+ return;
+ }
+ super.onAnimationStart(targetCompats, runnable);
+ }
- mAnimator = new AnimatorSet();
- setCurrentAnimator(mAnimator);
- mAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- finishedCallback.run();
- }
- });
- mAnimator.play(getClosingWindowAnimators(targets));
- mAnimator.play(getLauncherResumeAnimation());
- mAnimator.start();
+ @Override
+ public void onAnimationCancelled() {
+ if (mLauncher.getStateManager().getState().overviewUi
+ && mRemoteAnimationOverride != null) {
+ // This transition is only used for the fallback activity and should not be
+ // managed here (but necessary to implement here since the defined remote
+ // animation currently takes precendence over the one defined in the activity
+ // options).
+ mRemoteAnimationOverride.onAnimationCancelled();
+ return;
+ }
+ super.onAnimationCancelled();
+ }
- // Because t=0 has the app icon in its original spot, we can skip the
- // first frame and have the same movement one frame earlier.
- mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
- });
+ @Override
+ public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+ AnimatorSet anim = new AnimatorSet();
+ anim.play(getClosingWindowAnimators(targetCompats));
+
+ // Normally, we run the launcher content animation when we are transitioning home,
+ // but if home is already visible, then we don't want to animate the contents of
+ // launcher unless we know that we are animating home as a result of the home button
+ // press with quickstep, which will result in launcher being started on touch down,
+ // prior to the animation home (and won't be in the targets list because it is
+ // already visible). In that case, we force invisibility on touch down, and only
+ // reset it after the animation to home is initialized.
+ if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
+ || mLauncher.isForceInvisible()) {
+ // Only register the content animation for cancellation when state changes
+ mLauncher.getStateManager().setCurrentAnimation(anim);
+ createLauncherResumeAnimation(anim);
+ }
+ mLauncher.setForceInvisible(false);
+ return anim;
}
};
}
@@ -470,7 +742,7 @@
Matrix matrix = new Matrix();
float height = mLauncher.getDeviceProfile().heightPx;
float width = mLauncher.getDeviceProfile().widthPx;
- float endX = (Utilities.isRtl(mLauncher.getResources()) ? -width : width) * 1.16f;
+ float endX = (mLauncher.<RecentsView>getOverviewPanel().isRtl() ? -width : width) * 1.16f;
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
@@ -519,22 +791,27 @@
}
/**
- * @return Animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
+ * Creates an animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
*/
- private AnimatorSet getLauncherResumeAnimation() {
+ private void createLauncherResumeAnimation(AnimatorSet anim) {
if (mLauncher.isInState(LauncherState.ALL_APPS)
|| mLauncher.getDeviceProfile().isVerticalBarLayout()) {
AnimatorSet contentAnimator = getLauncherContentAnimator(true /* show */);
contentAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
- return contentAnimator;
+ anim.play(contentAnimator);
} else {
AnimatorSet workspaceAnimator = new AnimatorSet();
+
mLauncher.getWorkspace().setTranslationY(mWorkspaceTransY);
- mLauncher.getWorkspace().setAlpha(0f);
workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(),
View.TRANSLATION_Y, mWorkspaceTransY, 0));
- workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(), View.ALPHA,
- 0, 1f));
+
+ View currentPage = ((CellLayout) mLauncher.getWorkspace()
+ .getChildAt(mLauncher.getWorkspace().getCurrentPage()))
+ .getShortcutsAndWidgets();
+ currentPage.setAlpha(0f);
+ workspaceAnimator.play(ObjectAnimator.ofFloat(currentPage, View.ALPHA, 0, 1f));
+
workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
workspaceAnimator.setDuration(333);
workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
@@ -542,9 +819,9 @@
// Animate the shelf in two parts: slide in, and overeshoot.
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
// The shelf will start offscreen
- final float startY = ALL_APPS_PROGRESS_START;
+ final float startY = ALL_APPS_PROGRESS_OFF_SCREEN;
// And will end slightly pulled up, so that there is something to overshoot back to 1f.
- final float slideEnd = ALL_APPS_PROGRESS_SLIDE_END;
+ final float slideEnd = ALL_APPS_PROGRESS_OVERSHOOT;
allAppsController.setProgress(startY);
@@ -559,10 +836,10 @@
allAppsOvershoot.setDuration(153);
allAppsOvershoot.setInterpolator(Interpolators.OVERSHOOT_0);
- AnimatorSet resumeLauncherAnimation = new AnimatorSet();
- resumeLauncherAnimation.play(workspaceAnimator);
- resumeLauncherAnimation.playSequentially(allAppsSlideIn, allAppsOvershoot);
- return resumeLauncherAnimation;
+
+ anim.play(workspaceAnimator);
+ anim.playSequentially(allAppsSlideIn, allAppsOvershoot);
+ anim.addListener(mReapplyStateListener);
}
}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
new file mode 100644
index 0000000..0d1038a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -0,0 +1,54 @@
+/*
+ * 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.states.RotationHelper.REQUEST_LOCK;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import com.android.launcher3.states.InternalStateHandler;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+
+import java.util.function.BiPredicate;
+
+@TargetApi(Build.VERSION_CODES.P)
+public class LauncherInitListener extends InternalStateHandler implements ActivityInitListener {
+
+ private final BiPredicate<Launcher, Boolean> mOnInitListener;
+
+ public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
+ mOnInitListener = onInitListener;
+ }
+
+ @Override
+ protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ // For the duration of the gesture, lock the screen orientation to ensure that we do not
+ // rotate mid-quickscrub
+ launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
+ return mOnInitListener.test(launcher, alreadyOnHome);
+ }
+
+ @Override
+ public void register() {
+ initWhenReady();
+ }
+
+ @Override
+ public void unregister() {
+ clearReference();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 426fe35..0e3d2a4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -24,7 +24,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -32,7 +32,7 @@
*/
public class AllAppsState extends LauncherState {
- private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+ private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_ALL_APPS_SCRIM;
private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
@Override
@@ -57,7 +57,8 @@
@Override
public String getDescription(Launcher launcher) {
- return launcher.getString(R.string.all_apps_button_label);
+ AllAppsContainerView appsView = launcher.getAppsView();
+ return appsView.getDescription();
}
@Override
@@ -82,8 +83,15 @@
}
@Override
- public float getHoseatAlpha(Launcher launcher) {
- return 0;
+ public int getVisibleElements(Launcher launcher) {
+ return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+ }
+
+ @Override
+ public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+ // Keep the same transition properties as overview, so that we don't move around when
+ // transitioning to All Apps.
+ return LauncherState.OVERVIEW.getOverviewScaleAndTranslationYFactor(launcher);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java b/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java
deleted file mode 100644
index 6df1aba..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java
+++ /dev/null
@@ -1,92 +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;
-
-import com.android.launcher3.Alarm;
-import com.android.launcher3.OnAlarmListener;
-
-/**
- * Utility class to detect a pause during a drag.
- */
-public class DragPauseDetector implements OnAlarmListener {
-
- private static final float MAX_VELOCITY_TO_PAUSE = 0.2f;
- private static final long PAUSE_DURATION = 100;
-
- private final Alarm mAlarm;
- private final Runnable mOnPauseCallback;
-
- private boolean mTriggered = false;
- private int mDisabledFlags = 0;
-
- public DragPauseDetector(Runnable onPauseCallback) {
- mOnPauseCallback = onPauseCallback;
-
- mAlarm = new Alarm();
- mAlarm.setOnAlarmListener(this);
- mAlarm.setAlarm(PAUSE_DURATION);
- }
-
- public void onDrag(float velocity) {
- if (mTriggered || !isEnabled()) {
- return;
- }
-
- if (Math.abs(velocity) > MAX_VELOCITY_TO_PAUSE) {
- // Cancel any previous alarm and set a new alarm
- mAlarm.setAlarm(PAUSE_DURATION);
- }
- }
-
- @Override
- public void onAlarm(Alarm alarm) {
- if (!mTriggered && isEnabled()) {
- mTriggered = true;
- mOnPauseCallback.run();
- }
- }
-
- public boolean isTriggered () {
- return mTriggered;
- }
-
- public boolean isEnabled() {
- return mDisabledFlags == 0;
- }
-
- public void addDisabledFlags(int flags) {
- boolean wasEnabled = isEnabled();
- mDisabledFlags |= flags;
- resetAlarm(wasEnabled);
- }
-
- public void clearDisabledFlags(int flags) {
- boolean wasEnabled = isEnabled();
- mDisabledFlags &= ~flags;
- resetAlarm(wasEnabled);
- }
-
- private void resetAlarm(boolean wasEnabled) {
- boolean isEnabled = isEnabled();
- if (wasEnabled == isEnabled) {
- // Nothing has changed
- } if (isEnabled && !mTriggered) {
- mAlarm.setAlarm(PAUSE_DURATION);
- } else if (!isEnabled) {
- mAlarm.cancelAlarm();
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
deleted file mode 100644
index e39430d..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
+++ /dev/null
@@ -1,102 +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.uioverrides;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.touch.SwipeDetector.DIRECTION_NEGATIVE;
-import static com.android.launcher3.touch.SwipeDetector.DIRECTION_POSITIVE;
-import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
-import static com.android.launcher3.touch.SwipeDetector.VERTICAL;
-import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
-
-import android.graphics.Rect;
-import android.view.MotionEvent;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.util.VerticalSwipeController;
-import com.android.quickstep.RecentsView;
-
-/**
- * Extension of {@link VerticalSwipeController} to go from NORMAL to OVERVIEW.
- */
-public class EdgeSwipeController extends VerticalSwipeController implements
- OnDeviceProfileChangeListener {
-
- private static final Rect sTempRect = new Rect();
-
- public EdgeSwipeController(Launcher l) {
- super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout()
- ? HORIZONTAL : VERTICAL);
- l.addOnDeviceProfileChangeListener(this);
- }
-
- @Override
- public void onDeviceProfileChanged(DeviceProfile dp) {
- mDetector.updateDirection(dp.isVerticalBarLayout() ? HORIZONTAL : VERTICAL);
- }
-
- @Override
- protected boolean shouldInterceptTouch(MotionEvent ev) {
- return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
- }
-
- @Override
- protected int getSwipeDirection(MotionEvent ev) {
- return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE;
- }
-
- @Override
- protected boolean isTransitionFlipped() {
- return mLauncher.getDeviceProfile().isSeascape();
- }
-
- @Override
- protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
- // TODO: Log something
- }
-
- @Override
- protected void initSprings() {
- mSpringHandlers = new SpringAnimationHandler[0];
- }
-
- @Override
- protected float getShiftRange() {
- return getShiftRange(mLauncher);
- }
-
- public static float getShiftRange(Launcher launcher) {
- RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, sTempRect);
- DragLayer dl = launcher.getDragLayer();
- Rect insets = dl.getInsets();
- DeviceProfile dp = launcher.getDeviceProfile();
-
- if (dp.isVerticalBarLayout()) {
- if (dp.isSeascape()) {
- return insets.left + sTempRect.left;
- } else {
- return dl.getWidth() - sTempRect.right + insets.right;
- }
- } else {
- return dl.getHeight() - sTempRect.bottom + insets.bottom;
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
new file mode 100644
index 0000000..f98f7a5
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -0,0 +1,56 @@
+/*
+ * 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.uioverrides;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.quickstep.QuickScrubController;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Extension of overview state used for QuickScrub
+ */
+public class FastOverviewState extends OverviewState {
+
+ private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_DISABLE_RESTORE
+ | FLAG_DISABLE_INTERACTION | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON;
+
+ public FastOverviewState(int id) {
+ super(id, QuickScrubController.QUICK_SCRUB_START_DURATION, STATE_FLAGS);
+ }
+
+ @Override
+ public void onStateTransitionEnd(Launcher launcher) {
+ super.onStateTransitionEnd(launcher);
+ RecentsView recentsView = launcher.getOverviewPanel();
+ recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub();
+ }
+
+ public void onStateEnabled(Launcher launcher) {
+ super.onStateEnabled(launcher);
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ }
+
+ @Override
+ public int getVisibleElements(Launcher launcher) {
+ return NONE;
+ }
+
+ @Override
+ public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+ return new float[] {1f, 0.5f};
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/IgnoreTouchesInQuickScrub.java b/quickstep/src/com/android/launcher3/uioverrides/IgnoreTouchesInQuickScrub.java
deleted file mode 100644
index 2d5eb5a..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/IgnoreTouchesInQuickScrub.java
+++ /dev/null
@@ -1,41 +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.uioverrides;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.util.TouchController;
-import com.android.quickstep.TouchInteractionService;
-
-/**
- * Consumes touches when quick scrub is enabled.
- */
-public class IgnoreTouchesInQuickScrub implements TouchController {
-
- public IgnoreTouchesInQuickScrub() {
- }
-
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- return true;
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return TouchInteractionService.isQuickScrubEnabled();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
new file mode 100644
index 0000000..3622fc4
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
@@ -0,0 +1,75 @@
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.TouchInteractionService.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.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.quickstep.util.SysuiEventLogger;
+
+/**
+ * Touch controller for handling edge swipes in landscape/seascape UI
+ */
+public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchController {
+
+ public LandscapeEdgeSwipeController(Launcher l) {
+ super(l, SwipeDetector.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 int getSwipeDirection(MotionEvent ev) {
+ return SwipeDetector.DIRECTION_BOTH;
+ }
+
+ @Override
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() != isDragTowardPositive;
+ return draggingFromNav ? OVERVIEW : NORMAL;
+ }
+
+ @Override
+ protected float getShiftRange() {
+ return mLauncher.getDragLayer().getWidth();
+ }
+
+ @Override
+ protected float initCurrentAnimation() {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, maxAccuracy);
+ 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 (mFromState == NORMAL && targetState == OVERVIEW) {
+ SysuiEventLogger.writeDummyRecentsTransition(0);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
new file mode 100644
index 0000000..355b88d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+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.quickstep.TouchInteractionService;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Touch controller from going from OVERVIEW to ALL_APPS
+ */
+public class LandscapeStatesTouchController extends PortraitStatesTouchController {
+
+ public LandscapeStatesTouchController(Launcher l) {
+ super(l);
+ }
+
+ @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 true;
+ } 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) {
+ // Should swipe down go to OVERVIEW instead?
+ return TouchInteractionService.isConnected() ?
+ mLauncher.getStateManager().getLastState() : NORMAL;
+ } else {
+ return ALL_APPS;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OptionsPopupView.java b/quickstep/src/com/android/launcher3/uioverrides/OptionsPopupView.java
deleted file mode 100644
index c089d06..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/OptionsPopupView.java
+++ /dev/null
@@ -1,276 +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.uioverrides;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Outline;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.widget.Toast;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.RevealOutlineAnimation;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.GradientView;
-import com.android.launcher3.widget.WidgetsFullSheet;
-
-/**
- * Popup shown on long pressing an empty space in launcher
- */
-public class OptionsPopupView extends AbstractFloatingView implements OnClickListener {
-
- private final float mOutlineRadius;
- private final Launcher mLauncher;
- private final PointF mTouchPoint = new PointF();
-
- private final GradientView mGradientView;
-
- protected Animator mOpenCloseAnimator;
-
- public OptionsPopupView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
- setClipToOutline(true);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
- }
- });
-
- mLauncher = Launcher.getLauncher(context);
-
- mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet_scrim, mLauncher.getDragLayer(), false);
- mGradientView.setProgress(1, false);
- mGradientView.setAlpha(0);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- findViewById(R.id.wallpaper_button).setOnClickListener(this);
- findViewById(R.id.widget_button).setOnClickListener(this);
- findViewById(R.id.settings_button).setOnClickListener(this);
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.wallpaper_button) {
- mLauncher.onClickWallpaperPicker(null);
- close(true);
- } else if (view.getId() == R.id.widget_button) {
- if (mLauncher.getPackageManager().isSafeMode()) {
- Toast.makeText(mLauncher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- } else {
- WidgetsFullSheet.show(mLauncher, true /* animated */);
- close(true);
- }
- } else if (view.getId() == R.id.settings_button) {
- mLauncher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
- .setPackage(mLauncher.getPackageName())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- close(true);
- }
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() != MotionEvent.ACTION_DOWN) {
- return false;
- }
- if (mLauncher.getDragLayer().isEventOverView(this, ev)) {
- return false;
- }
- close(true);
- return true;
- }
-
- @Override
- protected void handleClose(boolean animate) {
- if (animate) {
- animateClose();
- } else {
- closeComplete();
- }
- }
-
- protected void animateClose() {
- if (!mIsOpen) {
- return;
- }
- mIsOpen = false;
-
- final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
- closeAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
-
- // Rectangular reveal (reversed).
- final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
- .createRevealAnimator(this, true);
- closeAnim.play(revealAnim);
-
- Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
- fadeOut.setInterpolator(Interpolators.DEACCEL);
- closeAnim.play(fadeOut);
-
- Animator gradientAlpha = ObjectAnimator.ofFloat(mGradientView, ALPHA, 0);
- gradientAlpha.setInterpolator(Interpolators.DEACCEL);
- closeAnim.play(gradientAlpha);
-
- closeAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- closeComplete();
- }
- });
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel();
- }
- mOpenCloseAnimator = closeAnim;
- closeAnim.start();
- }
-
- /**
- * Closes the popup without animation.
- */
- private void closeComplete() {
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel();
- mOpenCloseAnimator = null;
- }
- mIsOpen = false;
- mLauncher.getDragLayer().removeView(this);
- mLauncher.getDragLayer().removeView(mGradientView);
- }
-
- @Override
- public void logActionCommand(int command) {
- // TODO:
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_OPTIONS_POPUP) != 0;
- }
-
- private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- Rect startRect = new Rect();
- startRect.offset((int) (mTouchPoint.x - lp.x), (int) (mTouchPoint.y - lp.y));
-
- Rect endRect = new Rect(0, 0, lp.width, lp.height);
- if (getOutlineProvider() instanceof RevealOutlineAnimation) {
- ((RevealOutlineAnimation) getOutlineProvider()).getOutline(endRect);
- }
-
- return new RoundedRectRevealOutlineProvider
- (mOutlineRadius, mOutlineRadius, startRect, endRect);
- }
-
- private void animateOpen() {
- mIsOpen = true;
- final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
- openAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
-
- final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
- .createRevealAnimator(this, false);
- openAnim.play(revealAnim);
-
- Animator gradientAlpha = ObjectAnimator.ofFloat(mGradientView, ALPHA, 1);
- gradientAlpha.setInterpolator(Interpolators.ACCEL);
- openAnim.play(gradientAlpha);
-
- mOpenCloseAnimator = openAnim;
-
- openAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- }
- });
- openAnim.start();
- }
-
- public static void show(Launcher launcher, float x, float y) {
- DragLayer dl = launcher.getDragLayer();
- OptionsPopupView view = (OptionsPopupView) launcher.getLayoutInflater()
- .inflate(R.layout.longpress_options_menu, dl, false);
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) view.getLayoutParams();
-
- int maxWidth = dl.getWidth();
- int maxHeight = dl.getHeight();
- if (x <= 0 || y <= 0 || x >= maxWidth || y >= maxHeight) {
- x = maxWidth / 2;
- y = maxHeight / 2;
- }
- view.mTouchPoint.set(x, y);
-
- int height = lp.height;
-
- // Find a good width;
- int childCount = view.getChildCount();
- int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
- int widthSpec = MeasureSpec.makeMeasureSpec(maxWidth / childCount, MeasureSpec.AT_MOST);
- int maxChildWidth = 0;
-
- for (int i = 0; i < childCount; i ++) {
- View child = ((ViewGroup) view.getChildAt(i)).getChildAt(0);
- child.measure(widthSpec, heightSpec);
- maxChildWidth = Math.max(maxChildWidth, child.getMeasuredWidth());
- }
- Rect insets = dl.getInsets();
- int margin = (int) (2 * view.getElevation());
-
- int width = Math.min(maxWidth - insets.left - insets.right - 2 * margin,
- maxChildWidth * childCount);
- lp.width = width;
-
- // Position is towards the finger
- lp.customPosition = true;
- lp.x = Utilities.boundToRange((int) (x - width / 2), insets.left + margin,
- maxWidth - insets.right - width - margin);
- lp.y = Utilities.boundToRange((int) (y - height / 2), insets.top + margin,
- maxHeight - insets.bottom - height - margin);
-
- launcher.getDragLayer().addView(view.mGradientView);
- launcher.getDragLayer().addView(view);
- view.animateOpen();
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 777bcd3..d97b7b2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,47 +16,50 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
-import android.content.Context;
import android.graphics.Rect;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.quickstep.RecentsView;
+import com.android.quickstep.views.RecentsView;
/**
* Definition for overview state
*/
public class OverviewState extends LauncherState {
- private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED;
+ private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
+ | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI;
public OverviewState(int id) {
- super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+ this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+ }
+
+ protected OverviewState(int id, int transitionDuration, int stateFlags) {
+ super(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
}
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
Rect pageRect = new Rect();
- RecentsView.getScaledDownPageRect(launcher.getDeviceProfile(), launcher, pageRect);
- RecentsView rv = launcher.getOverviewPanel();
+ RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, pageRect);
if (launcher.getWorkspace().getNormalChildWidth() <= 0 || pageRect.isEmpty()) {
return super.getWorkspaceScaleAndTranslation(launcher);
}
- float overlap = 0;
- if (rv.getCurrentPage() >= rv.getFirstTaskIndex()) {
- overlap = launcher.getResources().getDimension(R.dimen.workspace_overview_offset_x);
- }
- return getScaleAndTranslationForPageRect(launcher, overlap, pageRect);
+ return getScaleAndTranslationForPageRect(launcher, pageRect);
+ }
+
+ @Override
+ public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+ return new float[] {1f, 0f};
}
@Override
@@ -72,8 +75,8 @@
}
@Override
- public float getVerticalProgress(Launcher launcher) {
- return getVerticalProgress(launcher.getDeviceProfile(), launcher);
+ public void onStateTransitionEnd(Launcher launcher) {
+ launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
}
@Override
@@ -82,51 +85,51 @@
}
public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
- final int centerPage = launcher.getWorkspace().getNextPage();
- return new PageAlphaProvider(ACCEL_2) {
+ return new PageAlphaProvider(DEACCEL_2) {
@Override
public float getPageAlpha(int pageIndex) {
- return pageIndex != centerPage ? 0 : 1f;
+ return 0;
}
};
}
- public static float[] getScaleAndTranslationForPageRect(Launcher launcher, float offsetX,
- Rect pageRect) {
+ public static float[] getScaleAndTranslationForPageRect(Launcher launcher, Rect pageRect) {
Workspace ws = launcher.getWorkspace();
float childWidth = ws.getNormalChildWidth();
- float childHeight = ws.getNormalChildHeight();
- float scale = pageRect.height() / childHeight;
+ float scale = pageRect.width() / childWidth;
Rect insets = launcher.getDragLayer().getInsets();
float halfHeight = ws.getExpectedHeight() / 2;
float childTop = halfHeight - scale * (halfHeight - ws.getPaddingTop() - insets.top);
float translationY = pageRect.top - childTop;
- // Align the workspace horizontally centered with the task rect
- float halfWidth = ws.getExpectedWidth() / 2;
- float childCenter = halfWidth -
- scale * (halfWidth - ws.getPaddingLeft() - insets.left - childWidth / 2);
- float translationX = pageRect.centerX() - childCenter;
-
- if (Utilities.isRtl(launcher.getResources())) {
- translationX -= offsetX / scale;
- } else {
- translationX += offsetX / scale;
- }
-
- return new float[] {scale, translationX, translationY};
+ return new float[] {scale, 0, translationY};
}
- public static float getVerticalProgress(DeviceProfile grid, Context context) {
- if (!grid.isVerticalBarLayout()) {
- return 1f;
+ @Override
+ public int getVisibleElements(Launcher launcher) {
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ return NONE;
+ } else {
+ return HOTSEAT_SEARCH_BOX | DRAG_HANDLE_INDICATOR |
+ (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
+ ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
}
+ }
- float total = grid.heightPx;
- float searchHeight = total - grid.availableHeightPx +
- context.getResources().getDimension(R.dimen.all_apps_search_box_full_height);
- return 1 - (searchHeight / total);
+ @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 1 - (getDefaultSwipeHeight(launcher)
+ / launcher.getAllAppsController().getShiftRange());
+ }
+
+ public static float getDefaultSwipeHeight(Launcher launcher) {
+ DeviceProfile dp = launcher.getDeviceProfile();
+ return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
deleted file mode 100644
index 1fd541a..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
+++ /dev/null
@@ -1,309 +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.uioverrides;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.View;
-
-import com.android.launcher3.AbstractFloatingView;
-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.dragndrop.DragLayer;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.util.TouchController;
-import com.android.quickstep.RecentsView;
-import com.android.quickstep.TaskView;
-
-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.DEACCEL_1_5;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-
-/**
- * Touch controller for swipe interaction in Overview state
- */
-public class OverviewSwipeController extends AnimatorListenerAdapter
- implements TouchController, SwipeDetector.Listener {
-
- private static final String TAG = "OverviewSwipeController";
-
- private static final float ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS = 0.1f;
- private static final int SINGLE_FRAME_MS = 16;
-
- // Progress after which the transition is assumed to be a success in case user does not fling
- private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
- private final Launcher mLauncher;
- private final SwipeDetector mDetector;
- private final RecentsView mRecentsView;
- private final int[] mTempCords = new int[2];
-
- private AnimatorPlaybackController mCurrentAnimation;
- private boolean mCurrentAnimationIsGoingUp;
-
- private boolean mNoIntercept;
- private boolean mSwipeDownEnabled;
-
- private float mDisplacementShift;
- private float mProgressMultiplier;
- private float mEndDisplacement;
-
- private TaskView mTaskBeingDragged;
-
- public OverviewSwipeController(Launcher launcher) {
- mLauncher = launcher;
- mRecentsView = launcher.getOverviewPanel();
- mDetector = new SwipeDetector(launcher, this, SwipeDetector.VERTICAL);
- }
-
- private boolean canInterceptTouch() {
- 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(OVERVIEW);
- }
-
- private boolean isEventOverHotseat(MotionEvent ev) {
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- return ev.getY() >
- mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher);
- } else {
- return mLauncher.getDragLayer().isEventOverHotseat(ev);
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
- Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
- mDetector.finishedScrolling();
- mCurrentAnimation = null;
- }
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = !canInterceptTouch();
- if (mNoIntercept) {
- return false;
- }
-
- // Now figure out which direction scroll events the controller will start
- // calling the callbacks.
- final int directionsToDetectScroll;
- boolean ignoreSlopWhenSettling = false;
-
- if (mCurrentAnimation != null) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
- ignoreSlopWhenSettling = true;
- } else {
- mTaskBeingDragged = null;
- mSwipeDownEnabled = true;
-
- int currentPage = mRecentsView.getCurrentPage();
- if (currentPage == 0) {
- // User is on home tile
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
- } else {
- View view = mRecentsView.getChildAt(currentPage);
- if (mLauncher.getDragLayer().isEventOverView(view, ev) &&
- view instanceof TaskView) {
- // The tile can be dragged down to open the task.
- mTaskBeingDragged = (TaskView) view;
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
- } else if (isEventOverHotseat(ev)) {
- // The hotseat is being dragged
- directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
- mSwipeDownEnabled = false;
- } else {
- mNoIntercept = true;
- return false;
- }
- }
- }
-
- mDetector.setDetectableScrollConditions(
- directionsToDetectScroll, ignoreSlopWhenSettling);
- }
-
- if (mNoIntercept) {
- return false;
- }
-
- onControllerTouchEvent(ev);
- return mDetector.isDraggingOrSettling();
- }
-
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- return mDetector.onTouchEvent(ev);
- }
-
- private void reInitAnimationController(boolean goingUp) {
- if (!goingUp && !mSwipeDownEnabled) {
- goingUp = true;
- }
- if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
- // No need to init
- return;
- }
- if (mCurrentAnimation != null) {
- mCurrentAnimation.setPlayFraction(0);
- }
- mCurrentAnimationIsGoingUp = goingUp;
- float range = mLauncher.getAllAppsController().getShiftRange();
- long maxDuration = (long) (2 * range);
- DragLayer dl = mLauncher.getDragLayer();
-
- if (mTaskBeingDragged == null) {
- // User is either going to all apps or home
- mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(goingUp ? ALL_APPS : NORMAL, maxDuration);
- if (goingUp) {
- mEndDisplacement = -range;
- } else {
- mEndDisplacement = EdgeSwipeController.getShiftRange(mLauncher);
- }
- } else {
- if (goingUp) {
- AnimatorSet anim = new AnimatorSet();
- ObjectAnimator translate = ObjectAnimator.ofFloat(
- mTaskBeingDragged, View.TRANSLATION_Y, -mTaskBeingDragged.getBottom());
- translate.setInterpolator(LINEAR);
- translate.setDuration(maxDuration);
- anim.play(translate);
-
- ObjectAnimator alpha = ObjectAnimator.ofFloat(mTaskBeingDragged, View.ALPHA, 0);
- alpha.setInterpolator(DEACCEL_1_5);
- alpha.setDuration(maxDuration);
- anim.play(alpha);
- mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration);
- mEndDisplacement = -mTaskBeingDragged.getBottom();
- } else {
- AnimatorSet anim = new AnimatorSet();
- // TODO: Setup a zoom animation
- mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration);
-
- mTempCords[1] = mTaskBeingDragged.getHeight();
- dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
- mEndDisplacement = dl.getHeight() - mTempCords[1];
- }
- }
-
- mCurrentAnimation.getTarget().addListener(this);
- mCurrentAnimation.dispatchOnStart();
- mProgressMultiplier = 1 / mEndDisplacement;
- }
-
- @Override
- public void onDragStart(boolean start) {
- if (mCurrentAnimation == null) {
- reInitAnimationController(mDetector.wasInitialTouchPositive());
- mDisplacementShift = 0;
- } else {
- mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
- mCurrentAnimation.pause();
- }
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- float totalDisplacement = displacement + mDisplacementShift;
- boolean isGoingUp =
- totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
- if (isGoingUp != mCurrentAnimationIsGoingUp) {
- reInitAnimationController(isGoingUp);
- }
- mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
- return true;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- final boolean goingToEnd;
-
- if (fling) {
- boolean goingUp = velocity < 0;
- if (!goingUp && !mSwipeDownEnabled) {
- goingToEnd = false;
- } else if (goingUp != mCurrentAnimationIsGoingUp) {
- // In case the fling is in opposite direction, make sure if is close enough
- // from the start position
- if (mCurrentAnimation.getProgressFraction()
- >= ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS) {
- // Not allowed
- goingToEnd = false;
- } else {
- reInitAnimationController(goingUp);
- goingToEnd = true;
- }
- } else {
- goingToEnd = true;
- }
- } else {
- goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
- }
-
- float progress = mCurrentAnimation.getProgressFraction();
- long animationDuration = SwipeDetector.calculateDuration(
- velocity, goingToEnd ? (1 - progress) : progress);
-
- float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
-
-
- mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd));
-
- ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
- anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
- anim.setDuration(animationDuration);
- anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
- anim.start();
- }
-
- private void onCurrentAnimationEnd(boolean wasSuccess) {
- // TODO: Might be a good time to log something.
- if (mTaskBeingDragged == null) {
- LauncherState state = wasSuccess ?
- (mCurrentAnimationIsGoingUp ? ALL_APPS : NORMAL) : OVERVIEW;
- mLauncher.getStateManager().goToState(state, false);
- } else if (wasSuccess) {
- if (mCurrentAnimationIsGoingUp) {
- mRecentsView.onTaskDismissed(mTaskBeingDragged);
- } else {
- mTaskBeingDragged.launchTask(false);
- }
- }
- mDetector.finishedScrolling();
- mTaskBeingDragged = null;
- mCurrentAnimation = null;
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
deleted file mode 100644
index 3ae8f41..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
+++ /dev/null
@@ -1,69 +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;
-
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.touch.SwipeDetector;
-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.VerticalSwipeController;
-
-/**
- * Extension of {@link VerticalSwipeController} which allows swipe up from OVERVIEW to ALL_APPS
- * Note that the swipe down is handled by {@link TwoStepSwipeController}.
- */
-public class OverviewSwipeUpController extends VerticalSwipeController {
-
- public OverviewSwipeUpController(Launcher l) {
- super(l, OVERVIEW);
- }
-
- @Override
- protected boolean shouldInterceptTouch(MotionEvent ev) {
- if (!mLauncher.isInState(OVERVIEW)) {
- return false;
- }
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- return ev.getY() >
- mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher);
- } else {
- return mLauncher.getDragLayer().isEventOverHotseat(ev);
- }
- }
-
- @Override
- protected int getSwipeDirection(MotionEvent ev) {
- return SwipeDetector.DIRECTION_POSITIVE;
- }
-
- @Override
- protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
- if (stateChanged) {
- // Transition complete. log the action
- mLauncher.getUserEventDispatcher().logActionOnContainer(
- wasFling ? Touch.FLING : Touch.SWIPE,
- Direction.UP,
- ContainerType.OVERVIEW,
- mLauncher.getWorkspace().getCurrentPage());
- }
-
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
new file mode 100644
index 0000000..1b65ca0
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -0,0 +1,297 @@
+/*
+ * 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.uioverrides;
+
+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.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.view.MotionEvent;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.TouchInteractionService;
+import com.android.quickstep.util.SysuiEventLogger;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Touch controller for handling various state transitions in portrait UI.
+ */
+public class PortraitStatesTouchController extends AbstractStateChangeTouchController {
+
+ private static final float TOTAL_DISTANCE_MULTIPLIER = 3f;
+ private static final float LINEAR_SCALE_LIMIT = 1 / TOTAL_DISTANCE_MULTIPLIER;
+
+ // Must be greater than LINEAR_SCALE_LIMIT;
+ private static final float MAXIMUM_DISTANCE_FACTOR = 0.9f;
+
+ // Maximum amount to overshoot.
+ private static final float MAX_OVERSHOOT = 0.3f;
+
+ private static final double PI_BY_2 = Math.PI / 2;
+
+ private InterpolatorWrapper mAllAppsInterpolatorWrapper = new InterpolatorWrapper();
+
+ // If true, we will finish the current animation instantly on second touch.
+ private boolean mFinishFastOnSecondTouch;
+
+ private final Interpolator mAllAppsDampedInterpolator = new Interpolator() {
+
+ private final double mAngleMultiplier = Math.PI /
+ (2 * (MAXIMUM_DISTANCE_FACTOR - LINEAR_SCALE_LIMIT));
+
+ @Override
+ public float getInterpolation(float v) {
+ if (v <= LINEAR_SCALE_LIMIT) {
+ return v * TOTAL_DISTANCE_MULTIPLIER;
+ }
+ float overshoot = (v - LINEAR_SCALE_LIMIT);
+ return (float) (1 + MAX_OVERSHOOT * Math.sin(overshoot * mAngleMultiplier));
+ }
+ };
+
+ private final Interpolator mOverviewBoundInterpolator = (v) -> {
+ if (v >= MAXIMUM_DISTANCE_FACTOR) {
+ return 1;
+ }
+ return FAST_OUT_SLOW_IN.getInterpolation(v / MAXIMUM_DISTANCE_FACTOR);
+ };
+
+ public PortraitStatesTouchController(Launcher l) {
+ super(l, SwipeDetector.VERTICAL);
+ }
+
+ @Override
+ protected boolean canInterceptTouch(MotionEvent ev) {
+ if (mCurrentAnimation != null) {
+ if (mFinishFastOnSecondTouch) {
+ // TODO: Animate to finish instead.
+ mCurrentAnimation.getAnimationPlayer().end();
+ }
+
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (mLauncher.isInState(ALL_APPS)) {
+ // In all-apps only listen if the container cannot scroll itself
+ if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
+ return false;
+ }
+ } else {
+ // For all other states, only listen if the event originated below the hotseat height
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ int hotseatHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
+ if (ev.getY() < (mLauncher.getDragLayer().getHeight() - hotseatHeight)) {
+ return false;
+ }
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected int getSwipeDirection(MotionEvent ev) {
+ final int directionsToDetectScroll;
+ if (mLauncher.isInState(ALL_APPS)) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
+ mStartContainerType = ContainerType.ALLAPPS;
+ } else if (mLauncher.isInState(NORMAL)) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ mStartContainerType = ContainerType.HOTSEAT;
+ } else if (mLauncher.isInState(OVERVIEW)) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ mStartContainerType = ContainerType.TASKSWITCHER;
+ } else {
+ return 0;
+ }
+ return directionsToDetectScroll;
+ }
+
+ @Override
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ if (fromState == ALL_APPS) {
+ // Should swipe down go to OVERVIEW instead?
+ return TouchInteractionService.isConnected() ?
+ mLauncher.getStateManager().getLastState() : NORMAL;
+ } else if (fromState == OVERVIEW) {
+ return isDragTowardPositive ? ALL_APPS : NORMAL;
+ } else if (isDragTowardPositive) {
+ return TouchInteractionService.isConnected() ? OVERVIEW : ALL_APPS;
+ }
+ return fromState;
+ }
+
+ private AnimatorSetBuilder getNormalToOverviewAnimation() {
+ mAllAppsInterpolatorWrapper.baseInterpolator = mAllAppsDampedInterpolator;
+
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ builder.setInterpolator(ANIM_VERTICAL_PROGRESS, mAllAppsInterpolatorWrapper);
+
+ builder.setInterpolator(ANIM_OVERVIEW_TRANSLATION, mOverviewBoundInterpolator);
+ return builder;
+ }
+
+ @Override
+ protected float initCurrentAnimation() {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+
+ float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
+ float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
+
+ float totalShift = endVerticalShift - startVerticalShift;
+
+ final AnimatorSetBuilder builder;
+
+ if (mFromState == NORMAL && mToState == OVERVIEW && totalShift != 0) {
+ builder = getNormalToOverviewAnimation();
+ totalShift = totalShift * TOTAL_DISTANCE_MULTIPLIER;
+ } else {
+ builder = new AnimatorSetBuilder();
+ }
+
+ if (mPendingAnimation != null) {
+ mPendingAnimation.finish(false);
+ mPendingAnimation = null;
+ }
+
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
+ if (recentsView.shouldSwipeDownLaunchApp() && mFromState == OVERVIEW && mToState == NORMAL
+ && taskView != null) {
+ mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
+ mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+
+ mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy);
+ } else {
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, builder, maxAccuracy);
+ }
+
+ if (totalShift == 0) {
+ totalShift = Math.signum(mFromState.ordinal - mToState.ordinal)
+ * OverviewState.getDefaultSwipeHeight(mLauncher);
+ }
+ return 1 / totalShift;
+ }
+
+ @Override
+ protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
+ LauncherState targetState, float velocity, boolean isFling) {
+ handleFirstSwipeToOverview(animator, expectedDuration, targetState, velocity, isFling);
+ super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
+ velocity, isFling);
+ }
+
+ private void handleFirstSwipeToOverview(final ValueAnimator animator,
+ final long expectedDuration, final LauncherState targetState, final float velocity,
+ final boolean isFling) {
+ if (mFromState == NORMAL && mToState == OVERVIEW && targetState == OVERVIEW) {
+ mFinishFastOnSecondTouch = true;
+
+ // Update all apps interpolator
+ float currentFraction = mCurrentAnimation.getProgressFraction();
+ float absVelocity = Math.abs(velocity);
+ float currentValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction);
+
+ if (isFling && absVelocity > 1 && currentFraction < LINEAR_SCALE_LIMIT) {
+
+ // TODO: Clean up these magic calculations
+ // Linearly interpolate the max value based on the velocity.
+ float maxValue = Math.max(absVelocity > 4 ? 1 + MAX_OVERSHOOT :
+ 1 + (absVelocity - 1) * MAX_OVERSHOOT / 3,
+ currentValue);
+ double angleToPeak = PI_BY_2 - Math.asin(currentValue / maxValue);
+
+ if (expectedDuration != 0 && angleToPeak != 0) {
+
+ float distanceLeft = 1 - currentFraction;
+ mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> {
+ float scaledF = (f - currentFraction) / distanceLeft;
+
+ if (scaledF < 0.5f) {
+ double angle = PI_BY_2 - angleToPeak + scaledF * angleToPeak / 0.5f;
+ return (float) (maxValue * Math.sin(angle));
+ }
+
+ scaledF = ((scaledF - .5f) / .5f);
+ double angle = PI_BY_2 + 3 * scaledF * PI_BY_2;
+ float amplitude = (1 - scaledF) * (1 - scaledF) * (maxValue - 1);
+ return 1 + (float) (amplitude * Math.sin(angle));
+ };
+
+ animator.setDuration(expectedDuration).setInterpolator(LINEAR);
+ return;
+ }
+ }
+
+ if (currentFraction < LINEAR_SCALE_LIMIT) {
+ mAllAppsInterpolatorWrapper.baseInterpolator = LINEAR;
+ return;
+ }
+ float extraValue = mAllAppsDampedInterpolator.getInterpolation(currentFraction) - 1;
+ float distanceLeft = 1 - currentFraction;
+
+ animator.setFloatValues(currentFraction, 1);
+ mAllAppsInterpolatorWrapper.baseInterpolator = (f) -> {
+ float scaledF = (f - currentFraction) / distanceLeft;
+
+ double angle = scaledF * 1.5 * Math.PI;
+ float amplitude = (1 - scaledF) * (1 - scaledF) * extraValue;
+ return 1 + (float) (amplitude * Math.sin(angle));
+ };
+ animator.setDuration(200).setInterpolator(LINEAR);
+ return;
+ }
+ mFinishFastOnSecondTouch = false;
+ }
+
+ @Override
+ protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+ super.onSwipeInteractionCompleted(targetState, logAction);
+ if (mFromState == NORMAL && targetState == OVERVIEW) {
+ SysuiEventLogger.writeDummyRecentsTransition(0);
+ }
+ }
+
+ private static class InterpolatorWrapper implements Interpolator {
+
+ public TimeInterpolator baseInterpolator = LINEAR;
+
+ @Override
+ public float getInterpolation(float v) {
+ return baseInterpolator.getInterpolation(v);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 9ea30f0..124ec20 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,142 +15,73 @@
*/
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATION;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
+import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.view.View;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.os.Build;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.PagedView;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.RecentsView;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.quickstep.views.LauncherRecentsView;
+@TargetApi(Build.VERSION_CODES.O)
public class RecentsViewStateController implements StateHandler {
private final Launcher mLauncher;
- private final RecentsView mRecentsView;
- private final WorkspaceCard mWorkspaceCard;
-
- private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress);
- // The fraction representing the visibility of the RecentsView. This allows delaying the
- // overall transition while the RecentsView is being shown or hidden.
- private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress);
-
- private boolean mIsRecentsScrollingToFirstTask;
+ private final LauncherRecentsView mRecentsView;
public RecentsViewStateController(Launcher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
- mRecentsView.setStateController(this);
-
- mWorkspaceCard = (WorkspaceCard) mRecentsView.getChildAt(0);
- mWorkspaceCard.setup(launcher);
}
@Override
public void setState(LauncherState state) {
- mWorkspaceCard.setWorkspaceScrollingEnabled(state == OVERVIEW);
- setVisibility(state == OVERVIEW);
- setTransitionProgress(state == OVERVIEW ? 1 : 0);
+ mRecentsView.setContentAlpha(state.overviewUi ? 1 : 0);
+ float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
+ mRecentsView.setAdjacentScale(scaleTranslationYFactor[0]);
+ mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]);
+ if (state.overviewUi) {
+ mRecentsView.updateEmptyMessage();
+ mRecentsView.resetTaskVisuals();
+ }
}
@Override
public void setStateWithAnimation(final LauncherState toState,
AnimatorSetBuilder builder, AnimationConfig config) {
- boolean settingEnabled = Utilities.getPrefs(mLauncher)
- .getBoolean("pref_scroll_to_first_task", false);
- mIsRecentsScrollingToFirstTask = mLauncher.isInState(NORMAL) && toState == OVERVIEW
- && settingEnabled;
+ PropertySetter setter = config.getProperSetter(builder);
+ float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
+ setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0],
+ builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
+ setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
+ builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
+ setter.setFloat(mRecentsView, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
+ AGGRESSIVE_EASE_IN_OUT);
- // Scroll to the workspace card before changing to the NORMAL state.
- int currPage = mRecentsView.getCurrentPage();
- if (toState == NORMAL && currPage != 0 && !config.userControlled) {
- int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION;
- int durationPerPage = maxSnapDuration / 10;
- int snapDuration = Math.min(maxSnapDuration, durationPerPage * currPage);
- mRecentsView.snapToPage(0, snapDuration);
- builder.setStartDelay(snapDuration);
+ if (!toState.overviewUi) {
+ builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
}
- ObjectAnimator progressAnim =
- mTransitionProgress.animateToValue(toState == OVERVIEW ? 1 : 0);
- progressAnim.setDuration(config.duration);
- progressAnim.setInterpolator(Interpolators.LINEAR);
- progressAnim.addListener(new AnimationSuccessListener() {
-
- @Override
- public void onAnimationStart(Animator animation) {
- mWorkspaceCard.setWorkspaceScrollingEnabled(mIsRecentsScrollingToFirstTask);
- }
-
- @Override
- public void onAnimationSuccess(Animator animator) {
- mWorkspaceCard.setWorkspaceScrollingEnabled(toState == OVERVIEW);
- mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
- }
- });
- builder.play(progressAnim);
-
- ObjectAnimator visibilityAnim = animateVisibility(toState == OVERVIEW);
- visibilityAnim.setDuration(config.duration);
- visibilityAnim.setInterpolator(Interpolators.LINEAR);
- builder.play(visibilityAnim);
- }
-
- public void setVisibility(boolean isVisible) {
- mVisibilityMultiplier.cancelAnimation();
- mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
- mVisibilityMultiplier.updateValue(isVisible ? 1 : 0);
- }
-
- public ObjectAnimator animateVisibility(boolean isVisible) {
- ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0);
- if (isVisible) {
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mRecentsView.setVisibility(View.VISIBLE);
- }
+ if (toState.overviewUi) {
+ ValueAnimator updateAnim = ValueAnimator.ofFloat(0, 1);
+ updateAnim.addUpdateListener(valueAnimator -> {
+ // While animating into recents, update the visible task data as needed
+ mRecentsView.loadVisibleTaskData();
});
- } else {
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mRecentsView.setVisibility(View.GONE);
- }
- });
+ updateAnim.setDuration(config.duration);
+ builder.play(updateAnim);
+ mRecentsView.updateEmptyMessage();
}
- return anim;
- }
-
- public void setTransitionProgress(float progress) {
- mTransitionProgress.cancelAnimation();
- mTransitionProgress.updateValue(progress);
- }
-
- private void onTransitionProgress() {
- applyProgress();
- if (mIsRecentsScrollingToFirstTask) {
- int scrollForFirstTask = mRecentsView.getScrollForPage(mRecentsView.getFirstTaskIndex());
- mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask));
- }
- }
-
- private void onVisibilityProgress() {
- applyProgress();
- }
-
- private void applyProgress() {
- mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java b/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java
deleted file mode 100644
index 651a753..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java
+++ /dev/null
@@ -1,51 +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;
-
-import android.animation.Animator;
-import android.util.SparseArray;
-
-import com.android.launcher3.anim.AnimatorSetBuilder;
-
-import java.util.Collections;
-import java.util.List;
-
-public class TaggedAnimatorSetBuilder extends AnimatorSetBuilder {
-
- /**
- * Map of the index in {@link #mAnims} to tag. All the animations in {@link #mAnims} starting
- * from this index correspond to the tag (until a new tag is specified for an index)
- */
- private final SparseArray<Object> mTags = new SparseArray<>();
-
- @Override
- public void startTag(Object obj) {
- mTags.put(mAnims.size(), obj);
- }
-
- public List<Animator> getAnimationsForTag(Object tag) {
- int startIndex = mTags.indexOfValue(tag);
- if (startIndex < 0) {
- return Collections.emptyList();
- }
- int startPos = mTags.keyAt(startIndex);
-
- int endIndex = startIndex + 1;
- int endPos = endIndex >= mTags.size() ? mAnims.size() : mTags.keyAt(endIndex);
-
- return mAnims.subList(startPos, endPos);
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
new file mode 100644
index 0000000..e73b219
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -0,0 +1,265 @@
+/*
+ * 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.uioverrides;
+
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.touch.SwipeDetector;
+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.views.BaseDragLayer;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * Touch controller for handling task view card swipes
+ */
+public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
+ extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "OverviewSwipeController";
+
+ private static final float ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS = 0.1f;
+ private static final int SINGLE_FRAME_MS = 16;
+
+ // Progress after which the transition is assumed to be a success in case user does not fling
+ private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+
+ protected final T mActivity;
+ private final SwipeDetector mDetector;
+ private final RecentsView mRecentsView;
+ private final int[] mTempCords = new int[2];
+
+ private PendingAnimation mPendingAnimation;
+ private AnimatorPlaybackController mCurrentAnimation;
+ private boolean mCurrentAnimationIsGoingUp;
+
+ private boolean mNoIntercept;
+
+ private float mDisplacementShift;
+ private float mProgressMultiplier;
+ private float mEndDisplacement;
+
+ private TaskView mTaskBeingDragged;
+
+ public TaskViewTouchController(T activity) {
+ mActivity = activity;
+ mRecentsView = activity.getOverviewPanel();
+ mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
+ }
+
+ private boolean canInterceptTouch() {
+ if (mCurrentAnimation != null) {
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
+ return false;
+ }
+ return isRecentsInteractive();
+ }
+
+ protected abstract boolean isRecentsInteractive();
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
+ Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
+ mDetector.finishedScrolling();
+ mCurrentAnimation = null;
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch();
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ final int directionsToDetectScroll;
+ boolean ignoreSlopWhenSettling = false;
+ if (mCurrentAnimation != null) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ ignoreSlopWhenSettling = true;
+ } else {
+ mTaskBeingDragged = null;
+
+ View view = mRecentsView.getChildAt(mRecentsView.getCurrentPage());
+ if (view instanceof TaskView && mActivity.getDragLayer().isEventOverView(view, ev)) {
+ // The tile can be dragged down to open the task.
+ mTaskBeingDragged = (TaskView) view;
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ } else {
+ mNoIntercept = true;
+ return false;
+ }
+ }
+
+ mDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, ignoreSlopWhenSettling);
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mDetector.onTouchEvent(ev);
+ }
+
+ private void reInitAnimationController(boolean goingUp) {
+ if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
+ // No need to init
+ return;
+ }
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setPlayFraction(0);
+ }
+ if (mPendingAnimation != null) {
+ mPendingAnimation.finish(false);
+ mPendingAnimation = null;
+ }
+
+ mCurrentAnimationIsGoingUp = goingUp;
+ BaseDragLayer dl = mActivity.getDragLayer();
+ long maxDuration = (long) (2 * dl.getHeight());
+
+ if (goingUp) {
+ mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
+ true /* animateTaskView */, true /* removeTask */, maxDuration);
+
+ mEndDisplacement = -mTaskBeingDragged.getHeight();
+ } else {
+ mPendingAnimation = mRecentsView.createTaskLauncherAnimation(
+ mTaskBeingDragged, maxDuration);
+ mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
+
+ mTempCords[1] = mTaskBeingDragged.getHeight();
+ dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
+ mEndDisplacement = dl.getHeight() - mTempCords[1];
+ }
+
+ mCurrentAnimation = AnimatorPlaybackController
+ .wrap(mPendingAnimation.anim, maxDuration);
+ mCurrentAnimation.getTarget().addListener(this);
+ mCurrentAnimation.dispatchOnStart();
+ mProgressMultiplier = 1 / mEndDisplacement;
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ if (mCurrentAnimation == null) {
+ reInitAnimationController(mDetector.wasInitialTouchPositive());
+ mDisplacementShift = 0;
+ } else {
+ mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
+ mCurrentAnimation.pause();
+ }
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float totalDisplacement = displacement + mDisplacementShift;
+ boolean isGoingUp =
+ totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
+ if (isGoingUp != mCurrentAnimationIsGoingUp) {
+ reInitAnimationController(isGoingUp);
+ }
+ mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ final boolean goingToEnd;
+ final int logAction;
+ if (fling) {
+ logAction = Touch.FLING;
+ boolean goingUp = velocity < 0;
+ if (goingUp != mCurrentAnimationIsGoingUp) {
+ // In case the fling is in opposite direction, make sure if is close enough
+ // from the start position
+ if (mCurrentAnimation.getProgressFraction()
+ >= ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS) {
+ // Not allowed
+ goingToEnd = false;
+ } else {
+ reInitAnimationController(goingUp);
+ goingToEnd = true;
+ }
+ } else {
+ goingToEnd = true;
+ }
+ } else {
+ logAction = Touch.SWIPE;
+ goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
+ }
+
+ float progress = mCurrentAnimation.getProgressFraction();
+ long animationDuration = SwipeDetector.calculateDuration(
+ velocity, goingToEnd ? (1 - progress) : progress);
+
+ float nextFrameProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
+
+ mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
+ anim.setDuration(animationDuration);
+ anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ anim.start();
+ }
+
+ private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
+ if (mPendingAnimation != null) {
+ mPendingAnimation.finish(wasSuccess);
+ mPendingAnimation = null;
+ }
+ if (wasSuccess) {
+ if (!mCurrentAnimationIsGoingUp) {
+ mActivity.getUserEventDispatcher().logTaskLaunch(logAction,
+ Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent());
+ }
+ }
+ mDetector.finishedScrolling();
+ mTaskBeingDragged = null;
+ mCurrentAnimation = null;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
deleted file mode 100644
index fb59946..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ /dev/null
@@ -1,496 +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;
-
-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.anim.SpringAnimationHandler.Y_DIRECTION;
-import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.support.animation.SpringAnimation;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.touch.SwipeDetector;
-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.FloatRange;
-import com.android.launcher3.util.TouchController;
-import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsView;
-import com.android.quickstep.TouchInteractionService;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-
-import java.util.ArrayList;
-
-/**
- * Handles vertical touch gesture on the DragLayer
- */
-public class TwoStepSwipeController extends AnimatorListenerAdapter
- implements TouchController, SwipeDetector.Listener {
-
- private static final String TAG = "TwoStepSwipeController";
-
- private static final float RECATCH_REJECTION_FRACTION = .0875f;
- private static final int SINGLE_FRAME_MS = 16;
- private static final long QUICK_SNAP_TO_OVERVIEW_DURATION = 250;
-
- // Progress after which the transition is assumed to be a success in case user does not fling
- private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
- /**
- * Index of the vertical swipe handles in {@link LauncherStateManager#getStateHandlers()}.
- */
- private static final int SWIPE_HANDLER_INDEX = 0;
-
- /**
- * Index of various UI handlers in {@link LauncherStateManager#getStateHandlers()} not related
- * to vertical swipe.
- */
- private static final int OTHER_HANDLERS_START_INDEX = SWIPE_HANDLER_INDEX + 1;
-
- // Swipe progress range (when starting from NORMAL state) where OVERVIEW state is allowed
- private static final float MIN_PROGRESS_TO_OVERVIEW = 0.1f;
- private static final float MAX_PROGRESS_TO_OVERVIEW = 0.4f;
-
- private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0;
- private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1;
- private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
- private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
- private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5;
- private static final int FLAG_DISABLED_BACK_TARGET = 1 << 6;
-
- private final Launcher mLauncher;
- private final SwipeDetector mDetector;
-
- private boolean mNoIntercept;
- private int mStartContainerType;
-
- private DragPauseDetector mDragPauseDetector;
- private FloatRange mOverviewProgressRange;
- private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder;
- private AnimatorSet mQuickOverviewAnimation;
- private boolean mAnimatingToOverview;
- private CroppedAnimationController mCroppedAnimationController;
-
- private AnimatorPlaybackController mCurrentAnimation;
- private LauncherState mFromState;
- private LauncherState mToState;
-
- private float mStartProgress;
- // Ratio of transition process [0, 1] to drag displacement (px)
- private float mProgressMultiplier;
-
- private SpringAnimationHandler[] mSpringHandlers;
-
- public TwoStepSwipeController(Launcher l) {
- mLauncher = l;
- mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
- }
-
- private boolean canInterceptTouch(MotionEvent ev) {
- if (mCurrentAnimation != null) {
- // If we are already animating from a previous state, we can intercept.
- return true;
- }
- if (mLauncher.isInState(NORMAL)) {
- if ((ev.getEdgeFlags() & EDGE_NAV_BAR) != 0 &&
- !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- // On normal swipes ignore edge swipes
- return false;
- }
- } else if (mLauncher.isInState(ALL_APPS)) {
- if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
- return false;
- }
- } else {
- // Don't listen for the swipe gesture if we are already in some other state.
- return false;
- }
- if (mAnimatingToOverview) {
- return false;
- }
- if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) {
- Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
- clearState();
- }
- }
-
- private void initSprings() {
- AllAppsContainerView appsView = mLauncher.getAppsView();
-
- SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
- if (handler == null) {
- mSpringHandlers = new SpringAnimationHandler[0];
- return;
- }
-
- ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
- handlers.add(handler);
-
- SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
- if (searchSpring != null) {
- SpringAnimationHandler searchHandler =
- new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
- searchHandler.add(searchSpring, true /* setDefaultValues */);
- handlers.add(searchHandler);
- }
-
- mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = !canInterceptTouch(ev);
- if (mNoIntercept) {
- return false;
- }
-
- // Now figure out which direction scroll events the controller will start
- // calling the callbacks.
- final int directionsToDetectScroll;
- boolean ignoreSlopWhenSettling = false;
-
- if (mCurrentAnimation != null) {
- if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
- } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
- } else {
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
- ignoreSlopWhenSettling = true;
- }
- } else {
- if (mLauncher.isInState(ALL_APPS)) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
- mStartContainerType = ContainerType.ALLAPPS;
- } else {
- directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
- mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ?
- ContainerType.HOTSEAT : ContainerType.WORKSPACE;
- }
- }
-
- mDetector.setDetectableScrollConditions(
- directionsToDetectScroll, ignoreSlopWhenSettling);
-
- if (mSpringHandlers == null) {
- initSprings();
- }
- }
-
- if (mNoIntercept) {
- return false;
- }
-
- onControllerTouchEvent(ev);
- return mDetector.isDraggingOrSettling();
- }
-
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.addMovement(ev);
- }
- return mDetector.onTouchEvent(ev);
- }
-
- @Override
- public void onDragStart(boolean start) {
- if (mCurrentAnimation == null) {
- float range = getShiftRange();
- long maxAccuracy = (long) (2 * range);
-
- mDragPauseDetector = new DragPauseDetector(this::onDragPauseDetected);
- mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
- if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
- mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_TWO_TARGETS);
- }
-
- mOverviewProgressRange = new FloatRange();
- mOverviewProgressRange.start = mLauncher.isInState(NORMAL)
- ? MIN_PROGRESS_TO_OVERVIEW
- : 1 - MAX_PROGRESS_TO_OVERVIEW;
- mOverviewProgressRange.end = mOverviewProgressRange.start
- + MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW;
-
- // Build current animation
- mFromState = mLauncher.getStateManager().getState();
- mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
-
- if (mToState == NORMAL && mLauncher.getStateManager().getLastState() == OVERVIEW) {
- mToState = OVERVIEW;
- mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_BACK_TARGET);
- }
-
- mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder();
- mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
- mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
-
- if (!TouchInteractionService.isConnected()) {
- mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
- }
-
- mCurrentAnimation.getTarget().addListener(this);
- mStartProgress = 0;
- mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
- mCurrentAnimation.dispatchOnStart();
- } else {
- mCurrentAnimation.pause();
- mStartProgress = mCurrentAnimation.getProgressFraction();
-
- mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
- updatePauseDetectorRangeFlag();
- }
-
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.skipToEnd();
- }
- }
-
- private float getShiftRange() {
- return mLauncher.getAllAppsController().getShiftRange();
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- float deltaProgress = mProgressMultiplier * displacement;
- mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress);
-
- updatePauseDetectorRangeFlag();
- mDragPauseDetector.onDrag(velocity);
-
- return true;
- }
-
- private void updatePauseDetectorRangeFlag() {
- if (mOverviewProgressRange.contains(mCurrentAnimation.getProgressFraction())) {
- mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
- } else {
- mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
- }
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
-
- final int logAction;
- LauncherState targetState;
- final float progress = mCurrentAnimation.getProgressFraction();
-
- if (fling) {
- logAction = Touch.FLING;
- targetState = velocity < 0 ? ALL_APPS : mLauncher.getStateManager().getLastState();
- // snap to top or bottom using the release velocity
- } else {
- logAction = Touch.SWIPE;
- targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
- }
-
- if (fling && targetState == ALL_APPS) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- }
-
- float endProgress;
-
- if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
- targetState = OVERVIEW;
- endProgress = OVERVIEW.getVerticalProgress(mLauncher);
- if (mFromState == NORMAL) {
- endProgress = 1 - endProgress;
- }
- } else if (targetState == mToState) {
- endProgress = 1;
- } else {
- endProgress = 0;
- }
-
- LauncherState targetStateFinal = targetState;
- mCurrentAnimation.setEndAction(() ->
- onSwipeInteractionCompleted(targetStateFinal, logAction));
-
- float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
-
- ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
- anim.setFloatValues(nextFrameProgress, endProgress);
- anim.setDuration(
- SwipeDetector.calculateDuration(velocity, Math.abs(endProgress - progress)));
- anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
- anim.start();
- }
-
- private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- if (targetState != mFromState) {
- // Transition complete. log the action
- mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
- mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
- mStartContainerType, mLauncher.getWorkspace().getCurrentPage());
- }
- clearState();
-
- // TODO: mQuickOverviewAnimation might still be running in which changing a state instantly
- // may cause a jump. Animate the state change with a short duration in this case?
- mLauncher.getStateManager().goToState(targetState, false /* animated */);
- }
-
- private void onDragPauseDetected() {
- final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1);
- twoStepAnimator.setDuration(mCurrentAnimation.getDuration());
- StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
-
- // Change the current animation to only play the vertical handle
- AnimatorSet anim = new AnimatorSet();
- anim.playTogether(mTaggedAnimatorSetBuilder.getAnimationsForTag(
- handlers[SWIPE_HANDLER_INDEX]));
- anim.play(twoStepAnimator);
- mCurrentAnimation = mCurrentAnimation.cloneFor(anim);
-
- AnimatorSetBuilder builder = new AnimatorSetBuilder();
- AnimationConfig config = new AnimationConfig();
- config.duration = QUICK_SNAP_TO_OVERVIEW_DURATION;
- for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
- handlers[i].setStateWithAnimation(OVERVIEW, builder, config);
- }
- mQuickOverviewAnimation = builder.build();
- mQuickOverviewAnimation.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- onQuickOverviewAnimationComplete(twoStepAnimator);
- }
- });
- mQuickOverviewAnimation.start();
- }
-
- private void onQuickOverviewAnimationComplete(ValueAnimator animator) {
- if (mAnimatingToOverview) {
- return;
- }
-
- // For the remainder to the interaction, the user can either go to the ALL_APPS state or
- // the OVERVIEW state.
- // The remaining state handlers are on the OVERVIEW state. Create one animation towards the
- // ALL_APPS state and only call it when the user moved above the current range.
- AnimationConfig config = new AnimationConfig();
- config.duration = (long) (2 * getShiftRange());
- config.userControlled = true;
-
- AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder();
- StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
- for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
- handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config);
- }
-
- mCroppedAnimationController = new CroppedAnimationController(
- AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration),
- new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0));
- animator.addUpdateListener(mCroppedAnimationController);
- }
-
- private void clearState() {
- mCurrentAnimation = null;
- mTaggedAnimatorSetBuilder = null;
- if (mDragPauseDetector != null) {
- mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_CANCEL_STATE);
- }
- mDragPauseDetector = null;
-
- if (mQuickOverviewAnimation != null) {
- mQuickOverviewAnimation.cancel();
- mQuickOverviewAnimation = null;
- }
- mCroppedAnimationController = null;
- mAnimatingToOverview = false;
-
- mDetector.finishedScrolling();
- }
-
- /**
- * {@link AnimatorUpdateListener} which controls another animation for a fraction of range
- */
- private static class CroppedAnimationController implements AnimatorUpdateListener {
-
- private final AnimatorPlaybackController mTarget;
- private final FloatRange mRange;
-
- CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) {
- mTarget = target;
- mRange = range;
- }
-
-
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float fraction = valueAnimator.getAnimatedFraction();
-
- if (mRange.start < mRange.end) {
- if (fraction <= mRange.start) {
- mTarget.setPlayFraction(0);
- } else if (fraction >= mRange.end) {
- mTarget.setPlayFraction(1);
- } else {
- mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
- }
- } else if (mRange.start > mRange.end) {
- if (fraction >= mRange.start) {
- mTarget.setPlayFraction(0);
- } else if (fraction <= mRange.end) {
- mTarget.setPlayFraction(1);
- } else {
- mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
- }
- } else {
- // mRange.start == mRange.end
- mTarget.setPlayFraction(0);
- }
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index b4f40c2..c1590f6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,71 +16,109 @@
package com.android.launcher3.uioverrides;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.PointF;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.quickstep.OverviewInteractionState.KEY_SWIPE_UP_ENABLED;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
-import com.android.quickstep.RecentsView;
-import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.WindowManagerWrapper;
public class UiFactory {
- private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
- "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
-
- public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD;
-
public static TouchController[] createTouchControllers(Launcher launcher) {
- if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+ SharedPreferences prefs = getPrefs(launcher);
+ boolean swipeUpEnabled = prefs.getBoolean(KEY_SWIPE_UP_ENABLED, true);
+ if (!swipeUpEnabled) {
return new TouchController[] {
- new IgnoreTouchesInQuickScrub(),
- new EdgeSwipeController(launcher),
- new TwoStepSwipeController(launcher),
- new OverviewSwipeController(launcher)};
+ launcher.getDragController(),
+ new LandscapeStatesTouchController(launcher),
+ new LauncherTaskViewcontroller(launcher)};
+ }
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ return new TouchController[] {
+ launcher.getDragController(),
+ new LandscapeStatesTouchController(launcher),
+ new LandscapeEdgeSwipeController(launcher),
+ new LauncherTaskViewcontroller(launcher)};
} else {
return new TouchController[] {
- new IgnoreTouchesInQuickScrub(),
- new TwoStepSwipeController(launcher),
- new OverviewSwipeController(launcher)};
+ launcher.getDragController(),
+ new PortraitStatesTouchController(launcher),
+ new LauncherTaskViewcontroller(launcher)};
}
}
- public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
- return null;
- }
-
public static StateHandler[] getStateHandler(Launcher launcher) {
return new StateHandler[] {
launcher.getAllAppsController(), launcher.getWorkspace(),
new RecentsViewStateController(launcher)};
}
- public static void onWorkspaceLongPress(Launcher launcher, PointF touchPoint) {
- OptionsPopupView.show(launcher, touchPoint.x, touchPoint.y);
- }
-
- public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
- BitmapRenderer renderer) {
- if (USE_HARDWARE_BITMAP && !forceSoftwareRenderer) {
- return RecentsTransition.createHardwareBitmap(width, height, renderer::render);
- } else {
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- renderer.render(new Canvas(result));
- return result;
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) {
+ boolean shouldBackButtonBeHidden = launcher != null
+ && launcher.getStateManager().getState().hideBackButton
+ && launcher.hasWindowFocus();
+ if (shouldBackButtonBeHidden) {
+ // Show the back button if there is a floating view visible.
+ shouldBackButtonBeHidden = AbstractFloatingView.getTopOpenView(launcher) == null;
}
+ OverviewInteractionState.getInstance(launcher)
+ .setBackButtonVisible(!shouldBackButtonBeHidden);
}
public static void resetOverview(Launcher launcher) {
RecentsView recents = launcher.getOverviewPanel();
recents.reset();
}
+
+ public static void onStart(Context context) {
+ RecentsModel model = RecentsModel.getInstance(context);
+ if (model != null) {
+ model.onStart();
+ }
+ }
+
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) {
+ LauncherState state = launcher.getStateManager().getState();
+ DeviceProfile profile = launcher.getDeviceProfile();
+ WindowManagerWrapper.getInstance().setShelfHeight(
+ state != ALL_APPS && launcher.isUserActive() && !profile.isVerticalBarLayout(),
+ profile.hotseatBarSizePx);
+
+ if (state == NORMAL) {
+ launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
+ }
+ }
+
+ public static void onTrimMemory(Context context, int level) {
+ RecentsModel model = RecentsModel.getInstance(context);
+ if (model != null) {
+ model.onTrimMemory(level);
+ }
+ }
+
+ private static class LauncherTaskViewcontroller extends TaskViewTouchController<Launcher> {
+
+ public LauncherTaskViewcontroller(Launcher activity) {
+ super(activity);
+ }
+
+ @Override
+ protected boolean isRecentsInteractive() {
+ return mActivity.isInState(OVERVIEW);
+ }
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
deleted file mode 100644
index 8533502..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
+++ /dev/null
@@ -1,127 +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;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.View.OnClickListener;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
-import com.android.quickstep.RecentsView;
-import com.android.quickstep.RecentsView.PageCallbacks;
-import com.android.quickstep.RecentsView.ScrollState;
-
-public class WorkspaceCard extends View implements PageCallbacks, OnClickListener {
-
- private final Rect mTempRect = new Rect();
-
- private Launcher mLauncher;
- private Workspace mWorkspace;
-
- private float mLinearInterpolationForPage2 = 1;
- private float mTranslateXPage0, mTranslateXPage1;
- private float mExtraScrollShift;
-
- private boolean mIsWorkspaceScrollingEnabled;
-
- public WorkspaceCard(Context context) {
- this(context, null);
- }
-
- public WorkspaceCard(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public WorkspaceCard(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOnClickListener(this);
- }
-
- /**
- * Draw nothing.
- */
- @Override
- public void draw(Canvas canvas) { }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
-
- // Initiate data
- mLinearInterpolationForPage2 = RecentsView.getScaledDownPageRect(
- mLauncher.getDeviceProfile(), mLauncher, mTempRect);
-
- float[] scale = OverviewState.getScaleAndTranslationForPageRect(mLauncher, 0, mTempRect);
- mTranslateXPage0 = scale[1];
- mTranslateXPage1 = OverviewState
- .getScaleAndTranslationForPageRect(mLauncher,
- getResources().getDimension(R.dimen.workspace_overview_offset_x) / scale[0],
- mTempRect)[1];
-
- mExtraScrollShift = 0;
- if (mWorkspace != null && getWidth() > 0) {
- float workspaceWidth = mWorkspace.getNormalChildWidth() * scale[0];
- mExtraScrollShift = (workspaceWidth - getWidth()) / 2;
- setScaleX(workspaceWidth / getWidth());
- }
- }
-
- @Override
- public void onClick(View view) {
- mLauncher.getStateManager().goToState(NORMAL);
- }
-
- public void setup(Launcher launcher) {
- mLauncher = launcher;
- mWorkspace = mLauncher.getWorkspace();
- }
-
- public void setWorkspaceScrollingEnabled(boolean isEnabled) {
- mIsWorkspaceScrollingEnabled = isEnabled;
- }
-
- @Override
- public int onPageScroll(ScrollState scrollState) {
- float factor = scrollState.linearInterpolation;
- float translateX = scrollState.distanceFromScreenCenter;
- if (mIsWorkspaceScrollingEnabled) {
- float shift = factor * (mTranslateXPage1 - mTranslateXPage0);
- mWorkspace.setTranslationX(shift + mTranslateXPage0);
- translateX += shift;
- }
-
- setTranslationX(translateX);
-
- // If the workspace card is still the first page, shift all the other pages.
- if (scrollState.linearInterpolation > mLinearInterpolationForPage2) {
- scrollState.prevPageExtraWidth = 0;
- } else if (mLinearInterpolationForPage2 > 0) {
- scrollState.prevPageExtraWidth = mExtraScrollShift *
- (1 - scrollState.linearInterpolation / mLinearInterpolationForPage2);
- } else {
- scrollState.prevPageExtraWidth = mExtraScrollShift;
- }
- return SCROLL_TYPE_WORKSPACE;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
new file mode 100644
index 0000000..3e96c44
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -0,0 +1,407 @@
+/*
+ * 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;
+
+import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.Nullable;
+import android.support.annotation.UiThread;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppTransitionManagerImpl;
+import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.views.LauncherLayoutListener;
+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.ActivityOptionsCompat;
+import com.android.systemui.shared.system.AssistDataReceiver;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+
+import java.util.function.BiPredicate;
+
+/**
+ * Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
+ */
+public interface ActivityControlHelper<T extends BaseDraggingActivity> {
+
+ LayoutListener createLayoutListener(T activity);
+
+ void onQuickstepGestureStarted(T activity, boolean activityVisible);
+
+ void onQuickInteractionStart(T activity, boolean activityVisible);
+
+ void executeOnNextDraw(T activity, TaskView targetView, Runnable action);
+
+ void onTransitionCancelled(T activity, boolean activityVisible);
+
+ int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
+
+ void onSwipeUpComplete(T activity);
+
+ void prepareRecentsUI(T activity, boolean activityVisible);
+
+ AnimatorPlaybackController createControllerForVisibleActivity(T activity);
+
+ AnimatorPlaybackController createControllerForHiddenActivity(T activity, int transitionLength);
+
+ ActivityInitListener createActivityInitListener(BiPredicate<T, Boolean> onInitListener);
+
+ void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
+ final RecentsAnimationListener remoteAnimationListener);
+
+ void startRecentsFromButton(Context context, Intent intent,
+ RecentsAnimationListener remoteAnimationListener);
+
+ @UiThread
+ @Nullable
+ RecentsView getVisibleRecentsView();
+
+ @UiThread
+ boolean switchToRecentsIfVisible();
+
+ class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
+
+ @Override
+ public LayoutListener createLayoutListener(Launcher activity) {
+ return new LauncherLayoutListener(activity);
+ }
+
+ @Override
+ public void onQuickstepGestureStarted(Launcher activity, boolean activityVisible) {
+ activity.onQuickstepGestureStarted(activityVisible);
+ }
+
+ @Override
+ public void onQuickInteractionStart(Launcher activity, boolean activityVisible) {
+ activity.getStateManager().goToState(FAST_OVERVIEW, activityVisible);
+ }
+
+ @Override
+ public void executeOnNextDraw(Launcher activity, TaskView targetView, Runnable action) {
+ ViewOnDrawExecutor executor = new ViewOnDrawExecutor() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (!isCompleted()) {
+ runAllTasks();
+ }
+ }
+ };
+ executor.attachTo(activity, targetView, false /* waitForLoadAnimation */);
+ executor.execute(action);
+ }
+
+ @Override
+ public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+ RecentsView.getPageRect(dp, context, outRect);
+ if (dp.isVerticalBarLayout()) {
+ Rect targetInsets = dp.getInsets();
+ int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
+ return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset;
+ } else {
+ return dp.heightPx - outRect.bottom;
+ }
+ }
+
+ @Override
+ public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
+ LauncherState startState = activity.getStateManager().getRestState();
+ activity.getStateManager().goToState(startState, activityVisible);
+ }
+
+ @Override
+ public void onSwipeUpComplete(Launcher activity) {
+ // Re apply state in case we did something funky during the transition.
+ activity.getStateManager().reapplyState();
+ }
+
+ @Override
+ public void prepareRecentsUI(Launcher activity, boolean activityVisible) {
+ LauncherState startState = activity.getStateManager().getState();
+ if (startState.disableRestore) {
+ startState = activity.getStateManager().getRestState();
+ }
+ activity.getStateManager().setRestState(startState);
+
+ if (!activityVisible) {
+ // Since the launcher is not visible, we can safely reset the scroll position.
+ // This ensures then the next swipe up to all-apps starts from scroll 0.
+ activity.getAppsView().reset(false /* animate */);
+ activity.getStateManager().goToState(OVERVIEW, false);
+
+ // Optimization, hide the all apps view to prevent layout while initializing
+ activity.getAppsView().getContentView().setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public AnimatorPlaybackController createControllerForVisibleActivity(Launcher activity) {
+ DeviceProfile dp = activity.getDeviceProfile();
+ long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
+ return activity.getStateManager().createAnimationToNewWorkspace(OVERVIEW, accuracy);
+ }
+
+ @Override
+ public AnimatorPlaybackController createControllerForHiddenActivity(
+ Launcher activity, int transitionLength) {
+ AllAppsTransitionController controller = activity.getAllAppsController();
+ AnimatorSet anim = new AnimatorSet();
+ if (activity.getDeviceProfile().isVerticalBarLayout()) {
+ // TODO:
+ } else {
+ float scrollRange = Math.max(controller.getShiftRange(), 1);
+ float progressDelta = (transitionLength / scrollRange);
+
+ float endProgress = OVERVIEW.getVerticalProgress(activity);
+ float startProgress = endProgress + progressDelta;
+ ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(
+ controller, ALL_APPS_PROGRESS, startProgress, endProgress);
+ shiftAnim.setInterpolator(LINEAR);
+ anim.play(shiftAnim);
+ }
+
+ // TODO: Link this animation to state animation, so that it is cancelled
+ // automatically on state change
+ anim.setDuration(transitionLength * 2);
+ return AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+ }
+
+ @Override
+ public ActivityInitListener createActivityInitListener(
+ BiPredicate<Launcher, Boolean> onInitListener) {
+ return new LauncherInitListener(onInitListener);
+ }
+
+ @Override
+ public void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
+ final RecentsAnimationListener remoteAnimationListener) {
+ ActivityManagerWrapper.getInstance().startRecentsActivity(
+ intent, assistDataReceiver, remoteAnimationListener, null, null);
+ }
+
+ @Override
+ public void startRecentsFromButton(Context context, Intent intent,
+ RecentsAnimationListener remoteAnimationListener) {
+ // We should use the remove animation for the fallback activity recents button case,
+ // it works better with PiP. In Launcher, we have already registered the remote
+ // animation definition, which takes priority over explicitly defined remote
+ // animations in the provided activity options when starting the activity, so we
+ // just register a remote animation factory to get a callback to handle this.
+ LauncherAppTransitionManagerImpl appTransitionManager =
+ (LauncherAppTransitionManagerImpl) getLauncher().getAppTransitionManager();
+ appTransitionManager.setRemoteAnimationOverride(new RecentsAnimationActivityOptions(
+ remoteAnimationListener, () -> {
+ // Once the controller is finished, also reset the remote animation override
+ appTransitionManager.setRemoteAnimationOverride(null);
+ }));
+ context.startActivity(intent);
+ }
+
+ @Nullable
+ @UiThread
+ private Launcher getLauncher() {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ return null;
+ }
+ return (Launcher) app.getModel().getCallback();
+ }
+
+ @Nullable
+ @UiThread
+ private Launcher getVisibleLaucher() {
+ Launcher launcher = getLauncher();
+ return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
+ launcher : null;
+ }
+
+ @Nullable
+ @Override
+ public RecentsView getVisibleRecentsView() {
+ Launcher launcher = getVisibleLaucher();
+ return launcher != null && launcher.isInState(OVERVIEW)
+ ? launcher.getOverviewPanel() : null;
+ }
+
+ @Override
+ public boolean switchToRecentsIfVisible() {
+ Launcher launcher = getVisibleLaucher();
+ if (launcher != null) {
+ launcher.getStateManager().goToState(OVERVIEW);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ class FallbackActivityControllerHelper implements ActivityControlHelper<RecentsActivity> {
+
+ @Override
+ public void onQuickstepGestureStarted(RecentsActivity activity, boolean activityVisible) {
+ // TODO:
+ }
+
+ @Override
+ public void onQuickInteractionStart(RecentsActivity activity, boolean activityVisible) {
+ // TODO:
+ }
+
+ @Override
+ public void executeOnNextDraw(RecentsActivity activity, TaskView targetView,
+ Runnable action) {
+ // TODO:
+ new Handler(Looper.getMainLooper()).post(action);
+ }
+
+ @Override
+ public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
+ // TODO:
+ }
+
+ @Override
+ public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+ FallbackRecentsView.getCenterPageRect(dp, context, outRect);
+ if (dp.isVerticalBarLayout()) {
+ Rect targetInsets = dp.getInsets();
+ int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
+ return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset;
+ } else {
+ return dp.heightPx - outRect.bottom;
+ }
+ }
+
+ @Override
+ public void onSwipeUpComplete(RecentsActivity activity) {
+ // TODO:
+ }
+
+ @Override
+ public void prepareRecentsUI(RecentsActivity activity, boolean activityVisible) {
+ // TODO:
+ }
+
+ @Override
+ public AnimatorPlaybackController createControllerForVisibleActivity(
+ RecentsActivity activity) {
+ DeviceProfile dp = activity.getDeviceProfile();
+ return createControllerForHiddenActivity(activity, Math.max(dp.widthPx, dp.heightPx));
+ }
+
+ @Override
+ public AnimatorPlaybackController createControllerForHiddenActivity(
+ RecentsActivity activity, int transitionLength) {
+ // We do not animate anything. Create a empty controller
+ AnimatorSet anim = new AnimatorSet();
+ return AnimatorPlaybackController.wrap(anim, transitionLength * 2);
+ }
+
+ @Override
+ public LayoutListener createLayoutListener(RecentsActivity activity) {
+ // We do not change anything as part of layout changes in fallback activity. Return a
+ // default layout listener.
+ return new LayoutListener() {
+ @Override
+ public void open() { }
+
+ @Override
+ public void setHandler(WindowTransformSwipeHandler handler) { }
+
+ @Override
+ public void finish() { }
+ };
+ }
+
+ @Override
+ public ActivityInitListener createActivityInitListener(
+ BiPredicate<RecentsActivity, Boolean> onInitListener) {
+ return new RecentsActivityTracker(onInitListener);
+ }
+
+ @Override
+ public void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
+ final RecentsAnimationListener remoteAnimationListener) {
+ // We can use the normal recents animation for swipe up
+ ActivityManagerWrapper.getInstance().startRecentsActivity(
+ intent, assistDataReceiver, remoteAnimationListener, null, null);
+ }
+
+ @Override
+ public void startRecentsFromButton(Context context, Intent intent,
+ RecentsAnimationListener remoteAnimationListener) {
+ // We should use the remove animation for the fallback activity recents button case,
+ // it works better with PiP. For the fallback activity, we should not have registered
+ // the launcher app transition manager, so we should just start the remote animation here.
+ ActivityOptions options = ActivityOptionsCompat.makeRemoteAnimation(
+ new RemoteAnimationAdapterCompat(
+ new RecentsAnimationActivityOptions(remoteAnimationListener, null),
+ 10000, 10000));
+ context.startActivity(intent, options.toBundle());
+ }
+
+ @Nullable
+ @Override
+ public RecentsView getVisibleRecentsView() {
+ RecentsActivity activity = RecentsActivityTracker.getCurrentActivity();
+ if (activity != null && activity.hasWindowFocus()) {
+ return activity.getOverviewPanel();
+ }
+ return null;
+ }
+
+ @Override
+ public boolean switchToRecentsIfVisible() {
+ return false;
+ }
+ }
+
+ interface LayoutListener {
+
+ void open();
+
+ void setHandler(WindowTransformSwipeHandler handler);
+
+ void finish();
+ }
+
+ interface ActivityInitListener {
+
+ void register();
+
+ void unregister();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 214b3f3..84dfa45 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -77,6 +77,12 @@
}
}
+ public void finishAnimation() {
+ if (mValueAnimator != null && mValueAnimator.isRunning()) {
+ mValueAnimator.end();
+ }
+ }
+
public ObjectAnimator getCurrentAnimation() {
return mValueAnimator;
}
diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
deleted file mode 100644
index 21b032b..0000000
--- a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
+++ /dev/null
@@ -1,47 +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;
-
-import android.support.annotation.WorkerThread;
-
-import com.android.launcher3.states.InternalStateHandler;
-import com.android.quickstep.TouchConsumer.InteractionType;
-
-public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
-
- protected Runnable mGestureEndCallback;
-
- public void setGestureEndCallback(Runnable gestureEndCallback) {
- mGestureEndCallback = gestureEndCallback;
- }
-
- public void reset() {}
-
- @WorkerThread
- public abstract void onGestureStarted();
-
- @WorkerThread
- public abstract void onGestureEnded(float endVelocity);
-
- public abstract void updateInteractionType(@InteractionType int interactionType);
-
- public abstract void onQuickScrubEnd();
-
- public abstract void onQuickScrubProgress(float progress);
-
- @WorkerThread
- public abstract void updateDisplacement(float displacement);
-}
diff --git a/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
new file mode 100644
index 0000000..b92678a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/DeferredTouchConsumer.java
@@ -0,0 +1,99 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.Choreographer;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+
+/**
+ * A TouchConsumer which defers all events on the UIThread until the consumer is created.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class DeferredTouchConsumer implements TouchConsumer {
+
+ private final VelocityTracker mVelocityTracker;
+ private final DeferredTouchProvider mTouchProvider;
+
+ private MotionEventQueue mMyQueue;
+ private TouchConsumer mTarget;
+
+ public DeferredTouchConsumer(DeferredTouchProvider touchProvider) {
+ mVelocityTracker = VelocityTracker.obtain();
+ mTouchProvider = touchProvider;
+ }
+
+ @Override
+ public void accept(MotionEvent event) {
+ mTarget.accept(event);
+ }
+
+ @Override
+ public void reset() {
+ mTarget.reset();
+ }
+
+ @Override
+ public void updateTouchTracking(int interactionType) {
+ mTarget.updateTouchTracking(interactionType);
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ mTarget.onQuickScrubEnd();
+ }
+
+ @Override
+ public void onQuickScrubProgress(float progress) {
+ mTarget.onQuickScrubProgress(progress);
+ }
+
+ @Override
+ public void preProcessMotionEvent(MotionEvent ev) {
+ mVelocityTracker.addMovement(ev);
+ }
+
+ @Override
+ public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ mMyQueue = queue;
+ return null;
+ }
+
+ @Override
+ public void deferInit() {
+ mTarget = mTouchProvider.createTouchConsumer(mVelocityTracker);
+ mTarget.getIntrimChoreographer(mMyQueue);
+ }
+
+ @Override
+ public boolean forceToLauncherConsumer() {
+ return mTarget.forceToLauncherConsumer();
+ }
+
+ @Override
+ public boolean deferNextEventToMainThread() {
+ // If our target is still null, defer the next target as well
+ TouchConsumer target = mTarget;
+ return target == null ? true : target.deferNextEventToMainThread();
+ }
+
+ public interface DeferredTouchProvider {
+
+ TouchConsumer createTouchConsumer(VelocityTracker tracker);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
new file mode 100644
index 0000000..12757c0
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/InstantAppResolverImpl.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstantAppInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.util.InstantAppResolver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Implementation of InstantAppResolver using platform APIs
+ */
+@SuppressWarnings("unused")
+public class InstantAppResolverImpl extends InstantAppResolver {
+
+ private static final String TAG = "InstantAppResolverImpl";
+ public static final String COMPONENT_CLASS_MARKER = "@instantapp";
+
+ private final PackageManager mPM;
+
+ public InstantAppResolverImpl(Context context)
+ throws NoSuchMethodException, ClassNotFoundException {
+ mPM = context.getPackageManager();
+ }
+
+ @Override
+ public boolean isInstantApp(ApplicationInfo info) {
+ return info.isInstantApp();
+ }
+
+ @Override
+ public boolean isInstantApp(AppInfo info) {
+ ComponentName cn = info.getTargetComponent();
+ return cn != null && cn.getClassName().equals(COMPONENT_CLASS_MARKER);
+ }
+
+ @Override
+ public List<ApplicationInfo> getInstantApps() {
+ try {
+ List<ApplicationInfo> result = new ArrayList<>();
+ for (InstantAppInfo iai : mPM.getInstantApps()) {
+ ApplicationInfo info = iai.getApplicationInfo();
+ if (info != null) {
+ result.add(info);
+ }
+ }
+ return result;
+ } catch (SecurityException se) {
+ Log.w(TAG, "getInstantApps failed. Launcher may not be the default home app.", se);
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling API: getInstantApps", e);
+ }
+ return super.getInstantApps();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
new file mode 100644
index 0000000..f5e1f6e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherSearchIndexablesProvider.java
@@ -0,0 +1,96 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.pm.ResolveInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.os.Build;
+import android.provider.SearchIndexablesContract.XmlResource;
+import android.provider.SearchIndexablesProvider;
+import android.util.Xml;
+
+import com.android.launcher3.R;
+import com.android.launcher3.graphics.IconShapeOverride;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+import static android.provider.SearchIndexablesContract.INDEXABLES_RAW_COLUMNS;
+import static android.provider.SearchIndexablesContract.INDEXABLES_XML_RES_COLUMNS;
+import static android.provider.SearchIndexablesContract.NON_INDEXABLES_KEYS_COLUMNS;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class LauncherSearchIndexablesProvider extends SearchIndexablesProvider {
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor queryXmlResources(String[] strings) {
+ MatrixCursor cursor = new MatrixCursor(INDEXABLES_XML_RES_COLUMNS);
+ ResolveInfo settingsActivity = getContext().getPackageManager().resolveActivity(
+ new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+ .setPackage(getContext().getPackageName()), 0);
+ cursor.newRow()
+ .add(XmlResource.COLUMN_XML_RESID, R.xml.indexable_launcher_prefs)
+ .add(XmlResource.COLUMN_INTENT_ACTION, Intent.ACTION_APPLICATION_PREFERENCES)
+ .add(XmlResource.COLUMN_INTENT_TARGET_PACKAGE, getContext().getPackageName())
+ .add(XmlResource.COLUMN_INTENT_TARGET_CLASS, settingsActivity.activityInfo.name);
+ return cursor;
+ }
+
+ @Override
+ public Cursor queryRawData(String[] projection) {
+ return new MatrixCursor(INDEXABLES_RAW_COLUMNS);
+ }
+
+ @Override
+ public Cursor queryNonIndexableKeys(String[] projection) {
+ MatrixCursor cursor = new MatrixCursor(NON_INDEXABLES_KEYS_COLUMNS);
+ if (!getContext().getSystemService(LauncherApps.class).hasShortcutHostPermission()) {
+ // We are not the current launcher. Hide all preferences
+ try (XmlResourceParser parser = getContext().getResources()
+ .getXml(R.xml.indexable_launcher_prefs)) {
+ final int depth = parser.getDepth();
+ final int[] attrs = new int[] { android.R.attr.key };
+ int type;
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ TypedArray a = getContext().obtainStyledAttributes(
+ Xml.asAttributeSet(parser), attrs);
+ cursor.addRow(new String[] {a.getString(0)});
+ a.recycle();
+ }
+ }
+ } catch (IOException |XmlPullParserException e) {
+ throw new RuntimeException(e);
+ }
+ } else if (!IconShapeOverride.isSupported(getContext())) {
+ cursor.addRow(new String[] {IconShapeOverride.KEY_PREFERENCE});
+ }
+ return cursor;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
index fae9b66..538e23c 100644
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -16,17 +16,20 @@
package com.android.quickstep;
import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_MASK;
import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
import android.annotation.TargetApi;
import android.os.Build;
+import android.util.Log;
import android.view.Choreographer;
import android.view.MotionEvent;
import com.android.systemui.shared.system.ChoreographerCompat;
import java.util.ArrayList;
-import java.util.function.Consumer;
/**
* Helper class for batching input events
@@ -34,6 +37,25 @@
@TargetApi(Build.VERSION_CODES.O)
public class MotionEventQueue {
+ private static final String TAG = "MotionEventQueue";
+
+ private static final int ACTION_VIRTUAL = ACTION_MASK - 1;
+
+ private static final int ACTION_QUICK_SCRUB_START =
+ ACTION_VIRTUAL | (1 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_QUICK_SCRUB_PROGRESS =
+ ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_QUICK_SCRUB_END =
+ ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_RESET =
+ ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_DEFER_INIT =
+ ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB =
+ ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_QUICK_STEP =
+ ACTION_VIRTUAL | (7 << ACTION_POINTER_INDEX_SHIFT);
+
private final EventArray mEmptyArray = new EventArray();
private final Object mExecutionLock = new Object();
@@ -46,45 +68,48 @@
private final Choreographer mMainChoreographer;
- private Consumer<MotionEvent> mConsumer;
+ private final TouchConsumer mConsumer;
private Choreographer mInterimChoreographer;
private Choreographer mCurrentChoreographer;
private Runnable mCurrentRunnable;
- public MotionEventQueue(Choreographer choreographer, Consumer<MotionEvent> consumer) {
+ public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
mMainChoreographer = choreographer;
mConsumer = consumer;
-
mCurrentChoreographer = mMainChoreographer;
mCurrentRunnable = mMainFrameCallback;
- }
- public void setConsumer(Consumer<MotionEvent> consumer) {
- synchronized (mExecutionLock) {
- mConsumer = consumer;
- }
+ setInterimChoreographer(consumer.getIntrimChoreographer(this));
}
public void setInterimChoreographer(Choreographer choreographer) {
synchronized (mExecutionLock) {
synchronized (mArrays) {
- mInterimChoreographer = choreographer;
- if (choreographer == null) {
- mCurrentChoreographer = mMainChoreographer;
- mCurrentRunnable = mMainFrameCallback;
- } else {
- mCurrentChoreographer = mInterimChoreographer;
- mCurrentRunnable = mInterimFrameCallback;
- }
-
+ setInterimChoreographerLocked(choreographer);
ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
}
}
}
+ private void setInterimChoreographerLocked(Choreographer choreographer) {
+ mInterimChoreographer = choreographer;
+ if (choreographer == null) {
+ mCurrentChoreographer = mMainChoreographer;
+ mCurrentRunnable = mMainFrameCallback;
+ } else {
+ mCurrentChoreographer = mInterimChoreographer;
+ mCurrentRunnable = mInterimFrameCallback;
+ }
+ }
+
public void queue(MotionEvent event) {
+ mConsumer.preProcessMotionEvent(event);
+ queueNoPreProcess(event);
+ }
+
+ private void queueNoPreProcess(MotionEvent event) {
synchronized (mArrays) {
EventArray array = mArrays[mCurrentIndex];
if (array.isEmpty()) {
@@ -116,7 +141,36 @@
int size = array.size();
for (int i = 0; i < size; i++) {
MotionEvent event = array.get(i);
- mConsumer.accept(event);
+ if (event.getActionMasked() == ACTION_VIRTUAL) {
+ switch (event.getAction()) {
+ case ACTION_QUICK_SCRUB_START:
+ mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
+ break;
+ case ACTION_QUICK_SCRUB_PROGRESS:
+ mConsumer.onQuickScrubProgress(event.getX());
+ break;
+ case ACTION_QUICK_SCRUB_END:
+ mConsumer.onQuickScrubEnd();
+ break;
+ case ACTION_RESET:
+ mConsumer.reset();
+ break;
+ case ACTION_DEFER_INIT:
+ mConsumer.deferInit();
+ break;
+ case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
+ mConsumer.onShowOverviewFromAltTab();
+ mConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
+ break;
+ case ACTION_QUICK_STEP:
+ mConsumer.onQuickStep(event.getX(), event.getY(), event.getEventTime());
+ break;
+ default:
+ Log.e(TAG, "Invalid virtual event: " + event.getAction());
+ }
+ } else {
+ mConsumer.accept(event);
+ }
event.recycle();
}
array.clear();
@@ -135,6 +189,43 @@
}
}
+ private void queueVirtualAction(int action, float progress) {
+ queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0));
+ }
+
+ public void onQuickScrubStart() {
+ queueVirtualAction(ACTION_QUICK_SCRUB_START, 0);
+ }
+
+ public void onOverviewShownFromAltTab() {
+ queueVirtualAction(ACTION_SHOW_OVERVIEW_FROM_ALT_TAB, 0);
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ queueVirtualAction(ACTION_QUICK_SCRUB_PROGRESS, progress);
+ }
+
+ public void onQuickScrubEnd() {
+ queueVirtualAction(ACTION_QUICK_SCRUB_END, 0);
+ }
+
+ public void onQuickStep(MotionEvent event) {
+ event.setAction(ACTION_QUICK_STEP);
+ queueNoPreProcess(event);
+ }
+
+ public void reset() {
+ queueVirtualAction(ACTION_RESET, 0);
+ }
+
+ public void deferInit() {
+ queueVirtualAction(ACTION_DEFER_INIT, 0);
+ }
+
+ public TouchConsumer getConsumer() {
+ return mConsumer;
+ }
+
private static class EventArray extends ArrayList<MotionEvent> {
public int lastEventAction = ACTION_CANCEL;
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
deleted file mode 100644
index 40246e2..0000000
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ /dev/null
@@ -1,403 +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.quickstep;
-
-
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
-import static com.android.quickstep.TouchConsumer.isInteractionQuick;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
-import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.os.Build;
-import android.support.annotation.UiThread;
-import android.view.View;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.Launcher.OnResumeCallback;
-import com.android.launcher3.LauncherAppState;
-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.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.TraceHelper;
-import com.android.quickstep.TouchConsumer.InteractionType;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-@TargetApi(Build.VERSION_CODES.O)
-public class NavBarSwipeInteractionHandler extends BaseSwipeInteractionHandler implements
- OnResumeCallback {
-
- private static final int STATE_LAUNCHER_READY = 1 << 0;
- private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 4;
- private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 5;
- private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 6;
-
- private static final long MAX_SWIPE_DURATION = 200;
- private static final long MIN_SWIPE_DURATION = 80;
- private static final int QUICK_SWITCH_SNAP_DURATION = 120;
-
- // Ideal velocity for a smooth transition
- private static final float PIXEL_PER_MS = 2f;
-
- private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
-
- private final Rect mStableInsets = new Rect();
- private final Rect mSourceRect = new Rect();
- private final Rect mTargetRect = new Rect();
- private final Rect mCurrentRect = new Rect();
- private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect);
-
- // Shift in the range of [0, 1].
- // 0 => preview snapShot is completely visible, and hotseat is completely translated down
- // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
- // visible.
- private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
-
- // Activity multiplier in the range of [0, 1]. When the activity becomes visible, this is
- // animated to 1, so allow for a smooth transition.
- private final AnimatedFloat mActivityMultiplier = new AnimatedFloat(this::updateFinalShift);
-
- private final int mRunningTaskId;
- private final Context mContext;
-
- private final MultiStateCallback mStateCallback;
-
- private Launcher mLauncher;
- private SnapshotDragView mDragView;
- private RecentsView mRecentsView;
- private QuickScrubController mQuickScrubController;
- private Hotseat mHotseat;
-
- private boolean mWasLauncherAlreadyVisible;
-
- private boolean mLauncherReady;
- private boolean mTouchEndHandled;
- private float mCurrentDisplacement;
-
- private @InteractionType int mInteractionType;
- private boolean mStartedQuickScrubFromHome;
-
- private Bitmap mTaskSnapshot;
-
- NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context,
- @InteractionType int interactionType) {
- mContext = context;
- mInteractionType = interactionType;
- mRunningTaskId = runningTaskInfo.id;
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-
- DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
- // TODO: If in multi window mode, dp = dp.getMultiWindowProfile()
- dp = dp.copy(mContext);
- // TODO: Use different insets for multi-window mode
- dp.updateInsets(mStableInsets);
- RecentsView.getPageRect(dp, mContext, mTargetRect);
- mSourceRect.set(0, 0, dp.widthPx - mStableInsets.left - mStableInsets.right,
- dp.heightPx - mStableInsets.top - mStableInsets.bottom);
-
- // Build the state callback
- mStateCallback = new MultiStateCallback();
- mStateCallback.addCallback(STATE_LAUNCHER_READY, this::onLauncherReady);
- mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_APP, this::resumeLastTask);
- mStateCallback.addCallback(
- STATE_SCALED_SNAPSHOT_RECENTS | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
- this::onAnimationToLauncherComplete);
- mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_APP,
- this::cleanupLauncher);
- }
-
- private void onLauncherReady() {
- mLauncherReady = true;
- executeFrameUpdate();
-
- long duration = Math.min(MAX_SWIPE_DURATION,
- Math.max((long) (-mCurrentDisplacement / PIXEL_PER_MS), MIN_SWIPE_DURATION));
- if (mCurrentShift.getCurrentAnimation() != null) {
- ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
- long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
-
- // TODO: Find a better heuristic
- duration = (duration + theirDuration) / 2;
- }
- ObjectAnimator anim = mActivityMultiplier.animateToValue(1)
- .setDuration(duration);
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
- }
- });
- anim.start();
- }
-
- public void setTaskSnapshot(Bitmap taskSnapshot) {
- mTaskSnapshot = taskSnapshot;
- }
-
- @Override
- public void onLauncherResume() {
- TraceHelper.partitionSection("TouchInt", "Launcher On resume");
- mDragView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- mDragView.getViewTreeObserver().removeOnPreDrawListener(this);
- mStateCallback.setState(STATE_LAUNCHER_READY);
- TraceHelper.partitionSection("TouchInt", "Launcher drawn");
- return true;
- }
- });
- }
-
- @Override
- protected boolean init(Launcher launcher, boolean alreadyOnHome) {
- launcher.setOnResumeCallback(this);
- mLauncher = launcher;
- mRecentsView = launcher.getOverviewPanel();
- mRecentsView.showTask(mRunningTaskId);
- mHotseat = mLauncher.getHotseat();
- mWasLauncherAlreadyVisible = alreadyOnHome;
-
- AbstractFloatingView.closeAllOpenViews(mLauncher, alreadyOnHome);
- mLauncher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome);
-
- mDragView = new SnapshotDragView(mLauncher, mTaskSnapshot);
- mLauncher.getDragLayer().addView(mDragView);
- mDragView.setPivotX(0);
- mDragView.setPivotY(0);
-
- if (isInteractionQuick(mInteractionType)) {
- updateUiForQuickScrub();
- }
-
- // Optimization
- if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- // All-apps search box is visible in vertical bar layout.
- mLauncher.getAppsView().setVisibility(View.GONE);
- }
- TraceHelper.partitionSection("TouchInt", "Launcher on new intent");
- return false;
- }
-
- public void updateInteractionType(@InteractionType int interactionType) {
- Preconditions.assertUIThread();
- if (mInteractionType != INTERACTION_NORMAL) {
- throw new IllegalArgumentException(
- "Can't change interaction type from " + mInteractionType);
- }
- if (!isInteractionQuick(interactionType)) {
- throw new IllegalArgumentException(
- "Can't change interaction type to " + interactionType);
- }
- mInteractionType = interactionType;
-
- if (mLauncher != null) {
- updateUiForQuickScrub();
- }
- }
-
- private void updateUiForQuickScrub() {
- mStartedQuickScrubFromHome = mWasLauncherAlreadyVisible;
- mQuickScrubController = mRecentsView.getQuickScrubController();
- mQuickScrubController.onQuickScrubStart(mStartedQuickScrubFromHome);
- animateToProgress(1f, MAX_SWIPE_DURATION);
- if (mStartedQuickScrubFromHome) {
- mDragView.setVisibility(View.INVISIBLE);
- }
- }
-
- @UiThread
- public void updateDisplacement(float displacement) {
- mCurrentDisplacement = displacement;
- executeFrameUpdate();
- }
-
- private void executeFrameUpdate() {
- if (mLauncherReady) {
- final float displacement = -mCurrentDisplacement;
- int hotseatSize = getHotseatSize();
- float translation = Utilities.boundToRange(displacement, 0, hotseatSize);
- float shift = hotseatSize == 0 ? 0 : translation / hotseatSize;
- mCurrentShift.updateValue(shift);
- }
- }
-
- @UiThread
- private void updateFinalShift() {
- if (!mLauncherReady || mStartedQuickScrubFromHome) {
- return;
- }
-
- float shift = mCurrentShift.value * mActivityMultiplier.value;
-
- AllAppsTransitionController controller = mLauncher.getAllAppsController();
- float range = getHotseatSize() / controller.getShiftRange();
- controller.setProgress(1 + (1 - shift) * range);
-
- mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
-
- float scale = (float) mCurrentRect.width() / mSourceRect.width();
- mDragView.setTranslationX(mCurrentRect.left - mStableInsets.left * scale * shift);
- mDragView.setTranslationY(mCurrentRect.top - mStableInsets.top * scale * shift);
- mDragView.setScaleX(scale);
- mDragView.setScaleY(scale);
- // TODO: mDragView.getViewBounds().setClipLeft((int) (mStableInsets.left * shift));
- mDragView.getViewBounds().setClipTop((int) (mStableInsets.top * shift));
- // TODO: mDragView.getViewBounds().setClipRight((int) (mStableInsets.right * shift));
- mDragView.getViewBounds().setClipBottom((int) (mStableInsets.bottom * shift));
- }
-
- private int getHotseatSize() {
- return mLauncher.getDeviceProfile().isVerticalBarLayout()
- ? mHotseat.getWidth() : mHotseat.getHeight();
- }
-
- @Override
- public void onGestureStarted() { }
-
- @UiThread
- public void onGestureEnded(float endVelocity) {
- if (mTouchEndHandled) {
- return;
- }
- mTouchEndHandled = true;
-
- Resources res = mContext.getResources();
- float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity);
- boolean isFling = Math.abs(endVelocity) > flingThreshold;
-
- long duration = MAX_SWIPE_DURATION;
- final float endShift;
- if (!isFling) {
- endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
- } else {
- endShift = endVelocity < 0 ? 1 : 0;
- float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity);
- if (Math.abs(endVelocity) > minFlingVelocity && mLauncherReady) {
- float distanceToTravel = (endShift - mCurrentShift.value) * getHotseatSize();
-
- // 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. 5.
- duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
- }
- }
-
- animateToProgress(endShift, duration);
- }
-
- /** Animates to the given progress, where 0 is the current app and 1 is overview. */
- private void animateToProgress(float progress, long duration) {
- ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
- anim.setInterpolator(Interpolators.SCROLL);
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- mStateCallback.setState((Float.compare(mCurrentShift.value, 0) == 0)
- ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS);
- }
- });
- anim.start();
- }
-
- @UiThread
- private void resumeLastTask() {
- RecentsTaskLoadPlan loadPlan = RecentsModel.getInstance(mContext).getLastLoadPlan();
- if (loadPlan != null) {
- Task task = loadPlan.getTaskStack().findTaskWithId(mRunningTaskId);
- if (task != null) {
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
- null, null);
- }
- }
- }
-
- public void reset() {
- mCurrentShift.cancelAnimation();
- if (mGestureEndCallback != null) {
- mGestureEndCallback.run();
- }
- }
-
- private void cleanupLauncher() {
- reset();
-
- // TODO: These should be done as part of ActivityOptions#OnAnimationStarted
- mLauncher.getStateManager().reapplyState();
- mLauncher.setOnResumeCallback(() -> mDragView.close(false));
- }
-
- private void onAnimationToLauncherComplete() {
- reset();
-
- mDragView.close(false);
- View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
- if (currentRecentsPage instanceof TaskView) {
- ((TaskView) currentRecentsPage).animateIconToScale(1f);
- }
- if (mInteractionType == INTERACTION_QUICK_SWITCH) {
- for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) {
- TaskView taskView = (TaskView) mRecentsView.getPageAt(i);
- if (taskView.getTask().key.id != mRunningTaskId) {
- mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION);
- taskView.postDelayed(() -> {taskView.launchTask(true);},
- QUICK_SWITCH_SNAP_DURATION);
- break;
- }
- }
- } else if (mInteractionType == INTERACTION_QUICK_SCRUB) {
- if (mQuickScrubController != null) {
- mQuickScrubController.snapToPageForCurrentQuickScrubSection();
- }
- }
- }
-
- public void onQuickScrubEnd() {
- if (mQuickScrubController != null) {
- mQuickScrubController.onQuickScrubEnd();
- } else {
- // TODO:
- }
- }
-
- public void onQuickScrubProgress(float progress) {
- if (mQuickScrubController != null) {
- mQuickScrubController.onQuickScrubProgress(progress);
- } else {
- // TODO:
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
new file mode 100644
index 0000000..f875bb7
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager.TaskDescription;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.LruCache;
+import android.util.SparseArray;
+
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.graphics.DrawableFactory;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.systemui.shared.recents.model.IconLoader;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
+
+/**
+ * Extension of {@link IconLoader} with icon normalization support
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class NormalizedIconLoader extends IconLoader {
+
+ private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
+ private final DrawableFactory mDrawableFactory;
+ private LauncherIcons mLauncherIcons;
+
+ public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+ super(context, iconCache, activityInfoCache);
+ mDrawableFactory = DrawableFactory.get(context);
+ }
+
+ @Override
+ public Drawable getDefaultIcon(int userId) {
+ synchronized (mDefaultIcons) {
+ BitmapInfo info = mDefaultIcons.get(userId);
+ if (info == null) {
+ info = getBitmapInfo(Resources.getSystem()
+ .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
+ mDefaultIcons.put(userId, info);
+ }
+
+ return new FastBitmapDrawable(info);
+ }
+ }
+
+ @Override
+ protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
+ return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
+ false));
+ }
+
+ private synchronized BitmapInfo getBitmapInfo(Drawable drawable, int userId,
+ int primaryColor, boolean isInstantApp) {
+ if (mLauncherIcons == null) {
+ mLauncherIcons = LauncherIcons.obtain(mContext);
+ }
+
+ mLauncherIcons.setWrapperBackgroundColor(primaryColor);
+ // User version code O, so that the icon is always wrapped in an adaptive icon container.
+ return mLauncherIcons.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+ Build.VERSION_CODES.O, isInstantApp);
+ }
+
+ @Override
+ protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
+ TaskDescription desc) {
+ BitmapInfo bitmapInfo = getBitmapInfo(
+ activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
+ userId,
+ desc.getPrimaryColor(),
+ activityInfo.applicationInfo.isInstantApp());
+ return mDrawableFactory.newIcon(bitmapInfo, activityInfo);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index 86e28f2..4d695de 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -21,23 +21,22 @@
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
+import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_DRAG_SLOP_PX;
-import static com.android.quickstep.RemoteRunnable.executeSafely;
-
+import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityOptions;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
-import android.util.Log;
+import android.os.SystemClock;
+import android.view.Choreographer;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -46,56 +45,69 @@
import android.view.WindowManager;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.Utilities;
import com.android.launcher3.util.TraceHelper;
-import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.AssistDataReceiver;
import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RecentsAnimationListener;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Touch consumer for handling events originating from an activity other than Launcher
*/
+@TargetApi(Build.VERSION_CODES.P)
public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
- private static final String TAG = "ActivityTouchConsumer";
private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
+ private static final int[] DEFERRED_HIT_TARGETS = false
+ ? new int[] {HIT_TARGET_BACK, HIT_TARGET_OVERVIEW} : new int[] {HIT_TARGET_BACK};
private final RunningTaskInfo mRunningTask;
private final RecentsModel mRecentsModel;
private final Intent mHomeIntent;
- private final ISystemUiProxy mISystemUiProxy;
+ private final ActivityControlHelper mActivityControlHelper;
private final MainThreadExecutor mMainThreadExecutor;
+ private final Choreographer mBackgroundThreadChoreographer;
+ private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
private int mActivePointerId = INVALID_POINTER_ID;
- private boolean mTouchThresholdCrossed;
- private int mTouchSlop;
+ private boolean mPassedInitialSlop;
private float mStartDisplacement;
- private BaseSwipeInteractionHandler mInteractionHandler;
+ private WindowTransformSwipeHandler mInteractionHandler;
private int mDisplayRotation;
private Rect mStableInsets = new Rect();
private VelocityTracker mVelocityTracker;
+ private MotionEventQueue mEventQueue;
+ private boolean mIsGoingToHome;
public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
- RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy,
- MainThreadExecutor mainThreadExecutor) {
+ RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
+ MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
+ @HitTarget int downHitTarget, VelocityTracker velocityTracker) {
super(base);
mRunningTask = runningTaskInfo;
mRecentsModel = recentsModel;
mHomeIntent = homeIntent;
- mVelocityTracker = VelocityTracker.obtain();
- mISystemUiProxy = systemUiProxy;
+ mVelocityTracker = velocityTracker;
+ mActivityControlHelper = activityControl;
mMainThreadExecutor = mainThreadExecutor;
+ mBackgroundThreadChoreographer = backgroundThreadChoreographer;
+ mIsDeferredDownTarget = Arrays.binarySearch(DEFERRED_HIT_TARGETS, downHitTarget) >= 0;
+ }
+
+ @Override
+ public void onShowOverviewFromAltTab() {
+ startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis());
}
@Override
@@ -109,12 +121,12 @@
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
- mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
- mTouchThresholdCrossed = false;
+ mPassedInitialSlop = false;
- // Start the window animation on down to give more time for launcher to draw
- if (!isUsingScreenShot()) {
- startTouchTrackingForWindowAnimation();
+ // Start the window animation on down to give more time for launcher to draw if the
+ // user didn't start the gesture over the back button
+ if (!mIsDeferredDownTarget) {
+ startTouchTrackingForWindowAnimation(ev.getEventTime());
}
Display display = getSystemService(WindowManager.class).getDefaultDisplay();
@@ -141,31 +153,19 @@
break;
}
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+ float displacement = getDisplacement(ev);
+ if (!mPassedInitialSlop && Math.abs(displacement) > QUICK_STEP_DRAG_SLOP_PX) {
+ mPassedInitialSlop = true;
+ mStartDisplacement = displacement;
- float displacement = ev.getY(pointerIndex) - mDownPos.y;
- if (isNavBarOnRight()) {
- displacement = ev.getX(pointerIndex) - mDownPos.x;
- } else if (isNavBarOnLeft()) {
- displacement = mDownPos.x - ev.getX(pointerIndex);
- }
- if (!mTouchThresholdCrossed) {
- mTouchThresholdCrossed = Math.abs(displacement) >= mTouchSlop;
- if (mTouchThresholdCrossed) {
- mStartDisplacement = Math.signum(displacement) * mTouchSlop;
-
- if (isUsingScreenShot()) {
- startTouchTrackingForScreenshotAnimation();
- }
-
- // Notify the handler that the gesture has actually started
- mInteractionHandler.onGestureStarted();
-
- // Notify the system that we have started tracking the event
- if (mISystemUiProxy != null) {
- executeSafely(mISystemUiProxy::onRecentsAnimationStarted);
- }
+ // If we deferred starting the window animation on touch down, then
+ // start tracking now
+ if (mIsDeferredDownTarget) {
+ startTouchTrackingForWindowAnimation(ev.getEventTime());
}
- } else {
+ }
+
+ if (mPassedInitialSlop && mInteractionHandler != null) {
// Move
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
@@ -182,6 +182,14 @@
}
}
+ private void notifyGestureStarted() {
+ if (mInteractionHandler == null) {
+ return;
+ }
+ // Notify the handler that the gesture has actually started
+ mInteractionHandler.onGestureStarted();
+ }
+
private boolean isNavBarOnRight() {
return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
}
@@ -190,117 +198,65 @@
return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
}
- private boolean isUsingScreenShot() {
- return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true);
- }
-
- /**
- * Called when the gesture has started.
- */
- private void startTouchTrackingForScreenshotAnimation() {
+ private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
// Create the shared handler
- final NavBarSwipeInteractionHandler handler =
- new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL);
-
- TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
-
- // Start the recents activity on a background thread
- BackgroundExecutor.get().submit(() -> {
- // Get the snap shot before
- handler.setTaskSnapshot(getCurrentTaskSnapshot());
-
- // Start the launcher activity with our custom handler
- Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
- startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
- TraceHelper.partitionSection("TouchInt", "Home started");
- });
+ final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
+ mRunningTask, this, touchTimeMs, mActivityControlHelper);
// Preload the plan
mRecentsModel.loadTasks(mRunningTask.id, null);
mInteractionHandler = handler;
- mInteractionHandler.setGestureEndCallback(this::onFinish);
- }
-
- private Bitmap getCurrentTaskSnapshot() {
- TraceHelper.beginSection("TaskSnapshot");
- // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
- Point displaySize = new Point();
- Display display = getSystemService(WindowManager.class).getDefaultDisplay();
- display.getRealSize(displaySize);
- int rotation = display.getRotation();
- // The rotation is backwards in landscape, so flip it.
- if (rotation == Surface.ROTATION_270) {
- rotation = Surface.ROTATION_90;
- } else if (rotation == Surface.ROTATION_90) {
- rotation = Surface.ROTATION_270;
- }
- try {
- return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
- false, rotation).toBitmap();
- } catch (Exception e) {
- Log.e(TAG, "Error capturing snapshot", e);
-
- // Return a dummy bitmap
- Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
- bitmap.eraseColor(Color.WHITE);
- return bitmap;
- } finally {
- TraceHelper.endSection("TaskSnapshot");
- }
- }
-
- private void startTouchTrackingForWindowAnimation() {
- // Create the shared handler
- final WindowTransformSwipeHandler handler =
- new WindowTransformSwipeHandler(mRunningTask, this);
-
- // Preload the plan
- mRecentsModel.loadTasks(mRunningTask.id, null);
- mInteractionHandler = handler;
- handler.setGestureEndCallback(this::onFinish);
+ handler.setGestureEndCallback(mEventQueue::reset);
CountDownLatch drawWaitLock = new CountDownLatch(1);
handler.setLauncherOnDrawCallback(() -> {
drawWaitLock.countDown();
if (handler == mInteractionHandler) {
- switchToMainConsumer();
+ switchToMainChoreographer();
}
});
- handler.initWhenReady(mMainThreadExecutor);
+ handler.initWhenReady();
- Runnable startActivity = () -> ActivityManagerWrapper.getInstance()
- .startRecentsActivity(mHomeIntent,
+ TraceHelper.beginSection("RecentsController");
+ Runnable startActivity = () -> mActivityControlHelper.startRecentsFromSwipe(mHomeIntent,
new AssistDataReceiver() {
@Override
public void onHandleAssistData(Bundle bundle) {
- // Pass to AIAI
+ mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
}
},
new RecentsAnimationListener() {
public void onAnimationStart(
RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps) {
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
if (mInteractionHandler == handler) {
- handler.setRecentsAnimation(controller, apps);
-
+ TraceHelper.partitionSection("RecentsController", "Received");
+ handler.onRecentsAnimationStart(controller, apps, homeContentInsets,
+ minimizedHomeBounds);
} else {
+ TraceHelper.endSection("RecentsController", "Finishing no handler");
controller.finish(false /* toHome */);
}
}
public void onAnimationCanceled() {
+ TraceHelper.endSection("RecentsController",
+ "Cancelled: " + mInteractionHandler);
if (mInteractionHandler == handler) {
- handler.setRecentsAnimation(null, null);
+ handler.onRecentsAnimationCanceled();
}
}
- }, null, null);
+ });
if (Looper.myLooper() != Looper.getMainLooper()) {
startActivity.run();
- try {
- drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- // We have waited long enough for launcher to draw
+ if (!mIsDeferredDownTarget) {
+ try {
+ drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ // We have waited long enough for launcher to draw
+ }
}
} else {
// We should almost always get touch-town on background thread. This is an edge case
@@ -314,7 +270,7 @@
* the animation can still be running.
*/
private void finishTouchTracking() {
- if (mTouchThresholdCrossed) {
+ if (mPassedInitialSlop && mInteractionHandler != null) {
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
@@ -322,39 +278,43 @@
: isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
: mVelocityTracker.getYVelocity(mActivePointerId);
mInteractionHandler.onGestureEnded(velocity);
- } else if (!isUsingScreenShot()) {
+ } else {
// Since we start touch tracking on DOWN, we may reach this state without actually
// starting the gesture. In that case, just cleanup immediately.
reset();
+
+ // Also clean up in case the system has handled the UP and canceled the animation before
+ // we had a chance to start the recents animation. In such a case, we will not receive
+ ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
+ true /* restoreHomeStackPosition */);
}
mVelocityTracker.recycle();
mVelocityTracker = null;
-
- onTouchTrackingComplete();
}
@Override
public void reset() {
// Clean up the old interaction handler
if (mInteractionHandler != null) {
- final BaseSwipeInteractionHandler handler = mInteractionHandler;
- mMainThreadExecutor.execute(handler::reset);
+ final WindowTransformSwipeHandler handler = mInteractionHandler;
mInteractionHandler = null;
+ mIsGoingToHome = handler.mIsGoingToHome;
+ mMainThreadExecutor.execute(handler::reset);
}
}
@Override
public void updateTouchTracking(int interactionType) {
- mMainThreadExecutor.execute(() -> {
- if (mInteractionHandler != null) {
- mInteractionHandler.updateInteractionType(interactionType);
- }
- });
+ notifyGestureStarted();
+ if (mInteractionHandler != null) {
+ mInteractionHandler.updateInteractionType(interactionType);
+ }
}
@Override
- public boolean shouldUseBackgroundConsumer() {
- return !isUsingScreenShot();
+ public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ mEventQueue = queue;
+ return mBackgroundThreadChoreographer;
}
@Override
@@ -371,13 +331,26 @@
}
}
- private void onFinish() {
- mInteractionHandler = null;
+ @Override
+ public void onQuickStep(float eventX, float eventY, long eventTime) {
+ notifyGestureStarted();
}
- public void onTouchTrackingComplete() { }
+ private float getDisplacement(MotionEvent ev) {
+ float eventX = ev.getX();
+ float eventY = ev.getY();
+ float displacement = eventY - mDownPos.y;
+ if (isNavBarOnRight()) {
+ displacement = eventX - mDownPos.x;
+ } else if (isNavBarOnLeft()) {
+ displacement = mDownPos.x - eventX;
+ }
+ return displacement;
+ }
- public void switchToMainConsumer() { }
+ public void switchToMainChoreographer() {
+ mEventQueue.setInterimChoreographer(null);
+ }
@Override
public void preProcessMotionEvent(MotionEvent ev) {
@@ -388,4 +361,15 @@
}
}
}
+
+ @Override
+ public boolean forceToLauncherConsumer() {
+ return mIsGoingToHome;
+ }
+
+ @Override
+ public boolean deferNextEventToMainThread() {
+ // TODO: Consider also check if the eventQueue is using mainThread of not.
+ return mInteractionHandler != null;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
new file mode 100644
index 0000000..8e59578
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -0,0 +1,373 @@
+/*
+ * 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;
+
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ActivityManager.RecentTaskInfo;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.support.annotation.UiThread;
+import android.support.annotation.WorkerThread;
+import android.util.SparseArray;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
+import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
+import com.android.quickstep.util.SysuiEventLogger;
+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.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.AssistDataReceiver;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Helper class to handle various atomic commands for switching between Overview.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class OverviewCommandHelper extends InternalStateHandler {
+
+ private static final int RID_RESET_SWIPE_HANDLER = 0;
+ private static final int RID_CANCEL_CONTROLLER = 1;
+ private static final int RID_CANCEL_ZOOM_OUT_ANIMATION = 2;
+
+ private static final long RECENTS_LAUNCH_DURATION = 200;
+
+ private static final String TAG = "OverviewCommandHelper";
+ private static final boolean DEBUG_START_FALLBACK_ACTIVITY = false;
+
+ private final Context mContext;
+ private final ActivityManagerWrapper mAM;
+ private final RecentsModel mRecentsModel;
+ private final MainThreadExecutor mMainThreadExecutor;
+
+ public final Intent homeIntent;
+ public final ComponentName launcher;
+
+ private final SparseArray<Runnable> mCurrentCommandFinishRunnables = new SparseArray<>();
+ // Monotonically increasing command ids.
+ private int mCurrentCommandId = 0;
+
+ private long mLastToggleTime;
+ private WindowTransformSwipeHandler mWindowTransformSwipeHandler;
+
+ private final Point mWindowSize = new Point();
+ private final Rect mTaskTargetRect = new Rect();
+ private final RectF mTempTaskTargetRect = new RectF();
+
+ public OverviewCommandHelper(Context context) {
+ mContext = context;
+ mAM = ActivityManagerWrapper.getInstance();
+ mMainThreadExecutor = new MainThreadExecutor();
+ mRecentsModel = RecentsModel.getInstance(mContext);
+
+ homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(context.getPackageName())
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ ResolveInfo info = context.getPackageManager().resolveActivity(homeIntent, 0);
+
+ if (DEBUG_START_FALLBACK_ACTIVITY) {
+ launcher = new ComponentName(context, RecentsActivity.class);
+ homeIntent.addCategory(Intent.CATEGORY_DEFAULT)
+ .removeCategory(Intent.CATEGORY_HOME);
+ } else {
+ launcher = new ComponentName(context.getPackageName(), info.activityInfo.name);
+ }
+
+ // Clear the packageName as system can fail to dedupe it b/64108432
+ homeIntent.setComponent(launcher).setPackage(null);
+ }
+
+ private void openRecents() {
+ Intent intent = addToIntent(new Intent(homeIntent));
+ mContext.startActivity(intent);
+ initWhenReady();
+ }
+
+ @UiThread
+ private void addFinishCommand(int requestId, int id, Runnable action) {
+ if (requestId < mCurrentCommandId) {
+ action.run();
+ } else {
+ mCurrentCommandFinishRunnables.put(id, action);
+ }
+ }
+
+ @UiThread
+ private void clearFinishCommand(int requestId, int id) {
+ if (requestId == mCurrentCommandId) {
+ mCurrentCommandFinishRunnables.remove(id);
+ }
+ }
+
+ @UiThread
+ private void initSwipeHandler(ActivityControlHelper helper, long time,
+ Consumer<WindowTransformSwipeHandler> onAnimationInitCallback) {
+ final int commandId = mCurrentCommandId;
+ final RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask();
+ final int runningTaskId = runningTask.id;
+ final WindowTransformSwipeHandler handler =
+ new WindowTransformSwipeHandler(runningTask, mContext, time, helper);
+
+ // Preload the plan
+ mRecentsModel.loadTasks(runningTaskId, null);
+ mWindowTransformSwipeHandler = handler;
+
+ mTempTaskTargetRect.setEmpty();
+ handler.setGestureEndCallback(() -> {
+ if (mWindowTransformSwipeHandler == handler) {
+ mWindowTransformSwipeHandler = null;
+ mTempTaskTargetRect.setEmpty();
+ }
+ clearFinishCommand(commandId, RID_RESET_SWIPE_HANDLER);
+ clearFinishCommand(commandId, RID_CANCEL_CONTROLLER);
+ });
+ handler.initWhenReady();
+ addFinishCommand(commandId, RID_RESET_SWIPE_HANDLER, handler::reset);
+
+ TraceHelper.beginSection(TAG);
+ Runnable startActivity = () -> helper.startRecentsFromButton(mContext,
+ addToIntent(homeIntent),
+ new RecentsAnimationListener() {
+ public void onAnimationStart(
+ RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
+ if (mWindowTransformSwipeHandler == handler) {
+ TraceHelper.partitionSection(TAG, "Received");
+ handler.onRecentsAnimationStart(controller, apps, homeContentInsets,
+ minimizedHomeBounds);
+ mTempTaskTargetRect.set(handler.getTargetRect(mWindowSize));
+
+ ThumbnailData thumbnail = mAM.getTaskThumbnail(runningTaskId,
+ true /* reducedResolution */);
+ mMainThreadExecutor.execute(() -> {
+ addFinishCommand(commandId,
+ RID_CANCEL_CONTROLLER, () -> controller.finish(true));
+ if (commandId == mCurrentCommandId) {
+ onAnimationInitCallback.accept(handler);
+
+ // The animation has started, which means the other activity
+ // should be paused, lets update the thumbnail
+ handler.switchToScreenshotImmediate(thumbnail);
+ }
+ });
+ } else {
+ TraceHelper.endSection(TAG, "Finishing no handler");
+ controller.finish(false /* toHome */);
+ }
+ }
+
+ public void onAnimationCanceled() {
+ TraceHelper.endSection(TAG, "Cancelled: " + handler);
+ if (mWindowTransformSwipeHandler == handler) {
+ handler.onRecentsAnimationCanceled();
+ }
+ }
+ });
+
+ // We should almost always get touch-town on background thread. This is an edge case
+ // when the background Choreographer has not yet initialized.
+ BackgroundExecutor.get().submit(startActivity);
+ }
+
+ @UiThread
+ private void startZoomOutAnim(final WindowTransformSwipeHandler handler) {
+ final int commandId = mCurrentCommandId;
+ ValueAnimator anim = ValueAnimator.ofInt(0, -handler.getTransitionLength());
+ anim.addUpdateListener((a) -> handler.updateDisplacement((Integer) a.getAnimatedValue()));
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ handler.onGestureEnded(0);
+ clearFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION);
+ }
+ });
+ handler.onGestureStarted();
+ anim.setDuration(RECENTS_LAUNCH_DURATION);
+ anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ anim.start();
+ addFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION, anim::cancel);
+ }
+
+ public void onOverviewToggle() {
+ // If currently screen pinning, do not enter overview
+ if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
+ return;
+ }
+
+ ActivityManagerWrapper.getInstance().closeSystemWindows("recentapps");
+ long time = SystemClock.elapsedRealtime();
+ mMainThreadExecutor.execute(() -> {
+ long elapsedTime = time - mLastToggleTime;
+ mLastToggleTime = time;
+
+ mCurrentCommandId++;
+ mTempTaskTargetRect.round(mTaskTargetRect);
+ int runnableCount = mCurrentCommandFinishRunnables.size();
+ if (runnableCount > 0) {
+ for (int i = 0; i < runnableCount; i++) {
+ mCurrentCommandFinishRunnables.valueAt(i).run();
+ }
+ mCurrentCommandFinishRunnables.clear();
+ }
+
+ // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
+ // the menu activity which takes window focus, prevening the right condition from
+ // being run below
+ ActivityControlHelper helper = getActivityControlHelper();
+ RecentsView recents = helper.getVisibleRecentsView();
+ if (recents != null) {
+ // Launch the next task
+ recents.showNextTask();
+ } else {
+ if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+ // The user tried to launch back into overview too quickly, either after
+ // launching an app, or before overview has actually shown, just ignore for now
+ return;
+ }
+
+ // Start overview
+ if (helper.switchToRecentsIfVisible()) {
+ SysuiEventLogger.writeDummyRecentsTransition(0);
+ // Do nothing
+ } else {
+ initSwipeHandler(helper, time, this::startZoomOutAnim);
+ }
+ }
+ });
+ }
+
+ public void onOverviewShown() {
+ getLauncher().runOnUiThread(() -> {
+ if (isOverviewAlmostVisible()) {
+ final RecentsView rv = getLauncher().getOverviewPanel();
+ rv.snapToTaskAfterNext();
+ } else {
+ openRecents();
+ }
+ }
+ );
+ }
+
+ public void onOverviewHidden() {
+ getLauncher().runOnUiThread(() -> {
+ if (isOverviewAlmostVisible()) {
+ final RecentsView rv = getLauncher().getOverviewPanel();
+ rv.launchNextTask();
+ }
+ }
+ );
+ }
+
+ @WorkerThread
+ private void startLastTask() {
+ // TODO: This should go through recents model.
+ List<RecentTaskInfo> tasks = mAM.getRecentTasks(2, UserHandle.myUserId());
+ if (tasks.size() > 1) {
+ RecentTaskInfo rti = tasks.get(1);
+
+ final ActivityOptions options;
+ if (!mTaskTargetRect.isEmpty()) {
+ final Rect targetRect = new Rect(mTaskTargetRect);
+ targetRect.offset(Utilities.isRtl(mContext.getResources())
+ ? - mTaskTargetRect.width() : mTaskTargetRect.width(), 0);
+ final AppTransitionAnimationSpecCompat specCompat =
+ new AppTransitionAnimationSpecCompat(rti.id, null, targetRect);
+ AppTransitionAnimationSpecsFuture specFuture =
+ new AppTransitionAnimationSpecsFuture(mMainThreadExecutor.getHandler()) {
+
+ @Override
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+ return Collections.singletonList(specCompat);
+ }
+ };
+ options = RecentsTransition.createAspectScaleAnimation(mContext,
+ mMainThreadExecutor.getHandler(), true /* scaleUp */,
+ specFuture, () -> {});
+ } else {
+ options = ActivityOptions.makeBasic();
+ }
+ mAM.startActivityFromRecents(rti.id, options);
+ }
+ }
+
+ private boolean isOverviewAlmostVisible() {
+ if (clearReference()) {
+ return true;
+ }
+ if (!mAM.getRunningTask().topActivity.equals(launcher)) {
+ return false;
+ }
+ Launcher launcher = getLauncher();
+ return launcher != null && launcher.isStarted() && launcher.isInState(OVERVIEW);
+ }
+
+ private Launcher getLauncher() {
+ return (Launcher) LauncherAppState.getInstance(mContext).getModel().getCallback();
+ }
+
+ @Override
+ protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
+ launcher.getStateManager().goToState(OVERVIEW, alreadyOnHome);
+ clearReference();
+ return false;
+ }
+
+ public ActivityControlHelper getActivityControlHelper() {
+ if (DEBUG_START_FALLBACK_ACTIVITY) {
+ return new FallbackActivityControllerHelper();
+ } else {
+ return new LauncherActivityControllerHelper();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
new file mode 100644
index 0000000..22b1757
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -0,0 +1,155 @@
+/*
+ * 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;
+
+import static com.android.launcher3.Utilities.getPrefs;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON;
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+
+import java.util.concurrent.ExecutionException;
+
+/**
+ * Sets overview interaction flags, such as:
+ *
+ * - FLAG_DISABLE_QUICK_SCRUB
+ * - FLAG_DISABLE_SWIPE_UP
+ * - FLAG_HIDE_BACK_BUTTON
+ * - FLAG_SHOW_OVERVIEW_BUTTON
+ *
+ * @see com.android.systemui.shared.system.NavigationBarCompat.InteractionType and associated flags.
+ */
+public class OverviewInteractionState implements OnSharedPreferenceChangeListener {
+
+ private static final String TAG = "OverviewFlags";
+
+ // We do not need any synchronization for this variable as its only written on UI thread.
+ private static OverviewInteractionState INSTANCE;
+
+ public static OverviewInteractionState getInstance(final Context context) {
+ if (INSTANCE == null) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ INSTANCE = new OverviewInteractionState(context.getApplicationContext());
+ } else {
+ try {
+ return new MainThreadExecutor().submit(
+ () -> OverviewInteractionState.getInstance(context)).get();
+ } catch (InterruptedException|ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ public static final String KEY_SWIPE_UP_ENABLED = "pref_enable_quickstep";
+
+ private static final int MSG_SET_PROXY = 200;
+ private static final int MSG_SET_BACK_BUTTON_VISIBLE = 201;
+ private static final int MSG_SET_SWIPE_UP_ENABLED = 202;
+
+ private final Handler mUiHandler;
+ private final Handler mBgHandler;
+
+ // These are updated on the background thread
+ private ISystemUiProxy mISystemUiProxy;
+ private boolean mBackButtonVisible = true;
+ private boolean mSwipeUpEnabled = true;
+
+ private OverviewInteractionState(Context context) {
+ mUiHandler = new Handler(this::handleUiMessage);
+ mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
+
+ SharedPreferences prefs = getPrefs(context);
+ prefs.registerOnSharedPreferenceChangeListener(this);
+ onSharedPreferenceChanged(prefs, KEY_SWIPE_UP_ENABLED);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String s) {
+ if (KEY_SWIPE_UP_ENABLED.equals(s)) {
+ mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
+ boolean swipeUpEnabled = prefs.getBoolean(s, true);
+ mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED,
+ swipeUpEnabled ? 1 : 0, 0).sendToTarget();
+ }
+ }
+
+ public void setBackButtonVisible(boolean visible) {
+ mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_VISIBLE);
+ mUiHandler.obtainMessage(MSG_SET_BACK_BUTTON_VISIBLE, visible ? 1 : 0, 0)
+ .sendToTarget();
+ }
+
+ public void setSystemUiProxy(ISystemUiProxy proxy) {
+ mBgHandler.obtainMessage(MSG_SET_PROXY, proxy).sendToTarget();
+ }
+
+ private boolean handleUiMessage(Message msg) {
+ mBgHandler.obtainMessage(msg.what, msg.arg1, msg.arg2).sendToTarget();
+ return true;
+ }
+
+ private boolean handleBgMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_PROXY:
+ mISystemUiProxy = (ISystemUiProxy) msg.obj;
+ break;
+ case MSG_SET_BACK_BUTTON_VISIBLE:
+ mBackButtonVisible = msg.arg1 != 0;
+ break;
+ case MSG_SET_SWIPE_UP_ENABLED:
+ mSwipeUpEnabled = msg.arg1 != 0;
+ break;
+ }
+ applyFlags();
+ return true;
+ }
+
+ @WorkerThread
+ private void applyFlags() {
+ if (mISystemUiProxy == null) {
+ return;
+ }
+
+ int flags;
+ if (mSwipeUpEnabled) {
+ flags = mBackButtonVisible ? 0 : FLAG_HIDE_BACK_BUTTON;
+ } else {
+ flags = FLAG_DISABLE_SWIPE_UP | FLAG_DISABLE_QUICK_SCRUB | FLAG_SHOW_OVERVIEW_BUTTON;
+ }
+ try {
+ mISystemUiProxy.setInteractionState(flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to update overview interaction flags", e);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index 3e65ffe..fd089b5 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -19,8 +19,14 @@
import android.view.HapticFeedbackConstants;
import com.android.launcher3.Alarm;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseActivity;
import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
/**
* Responds to quick scrub callbacks to page through and launch recent tasks.
@@ -30,70 +36,120 @@
*/
public class QuickScrubController implements OnAlarmListener {
- private static final int NUM_QUICK_SCRUB_SECTIONS = 5;
+ public static final int QUICK_SCRUB_START_DURATION = 210;
+
+ /**
+ * Snap to a new page when crossing these thresholds. The first and last auto-advance.
+ */
+ private static final float[] QUICK_SCRUB_THRESHOLDS = new float[] {
+ 0.05f, 0.35f, 0.65f, 0.95f
+ };
+
+ private static final boolean ENABLE_AUTO_ADVANCE = true;
private static final long AUTO_ADVANCE_DELAY = 500;
+ private static final int QUICKSCRUB_SNAP_DURATION_PER_PAGE = 325;
private static final int QUICKSCRUB_END_SNAP_DURATION_PER_PAGE = 60;
- private Launcher mLauncher;
- private Alarm mAutoAdvanceAlarm;
- private RecentsView mRecentsView;
+ private final Alarm mAutoAdvanceAlarm;
+ private final RecentsView mRecentsView;
+ private final BaseActivity mActivity;
+ private boolean mInQuickScrub;
private int mQuickScrubSection;
- private int mStartPage;
+ private boolean mStartedFromHome;
+ private boolean mFinishedTransitionToQuickScrub;
- public QuickScrubController(Launcher launcher) {
- mLauncher = launcher;
- mAutoAdvanceAlarm = new Alarm();
- mAutoAdvanceAlarm.setOnAlarmListener(this);
- }
-
- public void onQuickScrubStart(boolean startingFromHome) {
- mRecentsView = mLauncher.getOverviewPanel();
- mStartPage = startingFromHome ? 0 : mRecentsView.getFirstTaskIndex();
- mQuickScrubSection = 0;
- }
-
- public void onQuickScrubEnd() {
- mAutoAdvanceAlarm.cancelAlarm();
- if (mRecentsView == null) {
- } else {
- int page = mRecentsView.getNextPage();
- // Settle on the page then launch it.
- int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
- * QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
- mRecentsView.snapToPage(page, snapDuration);
- mRecentsView.postDelayed(() -> {
- if (page < mRecentsView.getFirstTaskIndex()) {
- mRecentsView.getPageAt(page).performClick();
- } else {
- ((TaskView) mRecentsView.getPageAt(page)).launchTask(true);
- }
- }, snapDuration);
+ public QuickScrubController(BaseActivity activity, RecentsView recentsView) {
+ mActivity = activity;
+ mRecentsView = recentsView;
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm = new Alarm();
+ mAutoAdvanceAlarm.setOnAlarmListener(this);
}
}
- public void onQuickScrubProgress(float progress) {
- int quickScrubSection = Math.round(progress * NUM_QUICK_SCRUB_SECTIONS);
- if (quickScrubSection != mQuickScrubSection) {
- int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
- goToPageWithHaptic(pageToGoTo);
- if (quickScrubSection == NUM_QUICK_SCRUB_SECTIONS || quickScrubSection == 0) {
- mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ public void onQuickScrubStart(boolean startingFromHome) {
+ mInQuickScrub = true;
+ mStartedFromHome = startingFromHome;
+ mQuickScrubSection = 0;
+ mFinishedTransitionToQuickScrub = false;
+
+ snapToNextTaskIfAvailable();
+ mActivity.getUserEventDispatcher().resetActionDurationMillis();
+ }
+
+ public void onQuickScrubEnd() {
+ mInQuickScrub = false;
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm.cancelAlarm();
+ }
+ int page = mRecentsView.getNextPage();
+ Runnable launchTaskRunnable = () -> {
+ TaskView taskView = ((TaskView) mRecentsView.getPageAt(page));
+ if (taskView != null) {
+ taskView.launchTask(true);
} else {
- mAutoAdvanceAlarm.cancelAlarm();
+ // Break out of quick scrub so user can interact with launcher.
+ mActivity.onBackPressed();
+ }
+ };
+ int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
+ * QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
+ if (mRecentsView.snapToPage(page, snapDuration)) {
+ // Settle on the page then launch it
+ mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
+ } else {
+ // No page move needed, just launch it
+ launchTaskRunnable.run();
+ }
+ mActivity.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP,
+ ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
+ ContainerType.WORKSPACE : ContainerType.APP);
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ int quickScrubSection = 0;
+ for (float threshold : QUICK_SCRUB_THRESHOLDS) {
+ if (progress < threshold) {
+ break;
+ }
+ quickScrubSection++;
+ }
+ if (quickScrubSection != mQuickScrubSection) {
+ boolean cameFromAutoAdvance = mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
+ || mQuickScrubSection == 0;
+ int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
+ if (mFinishedTransitionToQuickScrub && !cameFromAutoAdvance) {
+ goToPageWithHaptic(pageToGoTo);
+ }
+ if (ENABLE_AUTO_ADVANCE) {
+ if (quickScrubSection == QUICK_SCRUB_THRESHOLDS.length || quickScrubSection == 0) {
+ mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ } else {
+ mAutoAdvanceAlarm.cancelAlarm();
+ }
}
mQuickScrubSection = quickScrubSection;
}
}
- public void snapToPageForCurrentQuickScrubSection() {
- goToPageWithHaptic(mRecentsView.getCurrentPage() + mQuickScrubSection);
+ public void onFinishedTransitionToQuickScrub() {
+ mFinishedTransitionToQuickScrub = true;
+ }
+
+ public void snapToNextTaskIfAvailable() {
+ if (!mStartedFromHome && mInQuickScrub && mRecentsView.getChildCount() > 0) {
+ mRecentsView.snapToPage(mRecentsView.getNextPage() + 1, QUICK_SCRUB_START_DURATION);
+ }
}
private void goToPageWithHaptic(int pageToGoTo) {
+ pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
if (pageToGoTo != mRecentsView.getNextPage()) {
- mRecentsView.snapToPage(pageToGoTo);
- mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+ int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage())
+ * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
+ mRecentsView.snapToPage(pageToGoTo, duration);
+ mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
}
}
@@ -101,12 +157,14 @@
@Override
public void onAlarm(Alarm alarm) {
int currPage = mRecentsView.getNextPage();
- if (mQuickScrubSection == NUM_QUICK_SCRUB_SECTIONS
+ if (mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
&& currPage < mRecentsView.getPageCount() - 1) {
goToPageWithHaptic(currPage + 1);
- } else if (mQuickScrubSection == 0 && currPage > mStartPage) {
+ } else if (mQuickScrubSection == 0 && currPage > 0) {
goToPageWithHaptic(currPage - 1);
}
- mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index f92d773..cf60fdf 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -15,34 +15,96 @@
*/
package com.android.quickstep;
-import android.app.ListActivity;
+import android.app.ActivityOptions;
import android.os.Bundle;
-import android.os.UserHandle;
-import android.support.annotation.Nullable;
-import android.widget.ArrayAdapter;
+import android.view.View;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsRootView;
/**
* A simple activity to show the recently launched tasks
*/
-public class RecentsActivity extends ListActivity {
+public class RecentsActivity extends BaseDraggingActivity {
- private ArrayAdapter<Task> mAdapter;
+ private RecentsRootView mRecentsRootView;
+ private FallbackRecentsView mFallbackRecentsView;
@Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(this);
- plan.preloadPlan(new PreloadOptions(), new RecentsTaskLoader(this, 1, 1, 0), -1,
- UserHandle.myUserId());
+ // In case we are reusing IDP, create a copy so that we dont conflict with Launcher
+ // activity.
+ LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+ setDeviceProfile(appState != null
+ ? appState.getInvariantDeviceProfile().getDeviceProfile(this).copy(this)
+ : new InvariantDeviceProfile(this).getDeviceProfile(this));
- mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
- mAdapter.addAll(plan.getTaskStack().getTasks());
- setListAdapter(mAdapter);
+ setContentView(R.layout.fallback_recents_activity);
+ mRecentsRootView = findViewById(R.id.drag_layer);
+ mFallbackRecentsView = findViewById(R.id.overview_panel);
+
+ mRecentsRootView.setup();
+
+ getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+ Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+ RecentsActivityTracker.onRecentsActivityCreate(this);
+ }
+
+ @Override
+ public BaseDragLayer getDragLayer() {
+ return mRecentsRootView;
+ }
+
+ @Override
+ public View getRootView() {
+ return mRecentsRootView;
+ }
+
+ @Override
+ public <T extends View> T getOverviewPanel() {
+ return (T) mFallbackRecentsView;
+ }
+
+ @Override
+ public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
+ return null;
+ }
+
+ @Override
+ public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
+ return null;
+ }
+
+ @Override
+ public void invalidateParent(ItemInfo info) { }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ UiFactory.onStart(this);
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ UiFactory.onTrimMemory(this, level);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ RecentsActivityTracker.onRecentsActivityDestroy(this);
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
new file mode 100644
index 0000000..5bd606e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
@@ -0,0 +1,81 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+
+import java.lang.ref.WeakReference;
+import java.util.function.BiPredicate;
+
+/**
+ * Utility class to track create/destroy for RecentsActivity
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class RecentsActivityTracker implements ActivityInitListener {
+
+ private static final Object LOCK = new Object();
+ private static WeakReference<RecentsActivityTracker> sTracker = new WeakReference<>(null);
+ private static WeakReference<RecentsActivity> sCurrentActivity = new WeakReference<>(null);
+
+ private final BiPredicate<RecentsActivity, Boolean> mOnInitListener;
+
+ public RecentsActivityTracker(BiPredicate<RecentsActivity, Boolean> onInitListener) {
+ mOnInitListener = onInitListener;
+ }
+
+ @Override
+ public void register() {
+ synchronized (LOCK) {
+ sTracker = new WeakReference<>(this);
+ }
+ }
+
+ @Override
+ public void unregister() {
+ synchronized (LOCK) {
+ if (sTracker.get() == this) {
+ sTracker.clear();
+ }
+ }
+ }
+
+ public static void onRecentsActivityCreate(RecentsActivity activity) {
+ synchronized (LOCK) {
+ RecentsActivityTracker tracker = sTracker.get();
+ if (tracker != null && tracker.mOnInitListener.test(activity, false)) {
+ sTracker.clear();
+ }
+ sCurrentActivity = new WeakReference<>(activity);
+ }
+ }
+
+ public static void onRecentsActivityDestroy(RecentsActivity activity) {
+ synchronized (LOCK) {
+ if (sCurrentActivity.get() == activity) {
+ sCurrentActivity.clear();
+ }
+ }
+ }
+
+ public static RecentsActivity getCurrentActivity() {
+ synchronized (LOCK) {
+ return sCurrentActivity.get();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java b/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
new file mode 100644
index 0000000..a25e192
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import android.graphics.Rect;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.util.function.Consumer;
+
+/**
+ * Class to create activity options to emulate recents transition.
+ */
+public class RecentsAnimationActivityOptions implements RemoteAnimationRunnerCompat {
+
+ private final RecentsAnimationListener mListener;
+ private final Runnable mFinishCallback;
+
+ public RecentsAnimationActivityOptions(RecentsAnimationListener listener,
+ Runnable finishCallback) {
+ mListener = listener;
+ mFinishCallback = finishCallback;
+ }
+
+ @Override
+ public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
+ Runnable runnable) {
+ showOpeningTarget(targetCompats);
+ RemoteRecentsAnimationControllerCompat dummyRecentsAnim =
+ new RemoteRecentsAnimationControllerCompat(() -> {
+ runnable.run();
+ if (mFinishCallback != null) {
+ mFinishCallback.run();
+ }
+ });
+
+ Rect insets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(insets);
+ mListener.onAnimationStart(dummyRecentsAnim, targetCompats, insets, null);
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ mListener.onAnimationCanceled();
+ }
+
+ private void showOpeningTarget(RemoteAnimationTargetCompat[] targetCompats) {
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat target : targetCompats) {
+ int layer = target.mode == RemoteAnimationTargetCompat.MODE_CLOSING
+ ? Integer.MAX_VALUE
+ : target.prefixOrderIndex;
+ t.setLayer(target.leash, layer);
+ t.show(target.leash);
+ }
+ t.apply();
+ }
+
+ private class RemoteRecentsAnimationControllerCompat extends RecentsAnimationControllerCompat {
+
+ final Runnable mFinishCallback;
+
+ public RemoteRecentsAnimationControllerCompat(Runnable finishCallback) {
+ mFinishCallback = finishCallback;
+ }
+
+ @Override
+ public ThumbnailData screenshotTask(int taskId) {
+ return new ThumbnailData();
+ }
+
+ @Override
+ public void setInputConsumerEnabled(boolean enabled) { }
+
+ @Override
+ public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { }
+
+ @Override
+ public void finish(boolean toHome) {
+ // This should never be called with toHome == false
+ if (mFinishCallback != null) {
+ mFinishCallback.run();
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java b/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
new file mode 100644
index 0000000..fdeb0c1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
@@ -0,0 +1,117 @@
+/*
+ * 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;
+
+import android.graphics.Rect;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Helper class to interpolate the animation between a task view representation and an actual
+ * window.
+ */
+public class RecentsAnimationInterpolator {
+
+ public static class TaskWindowBounds {
+ public float taskScale = 1f;
+ public float taskX = 0f;
+ public float taskY = 0f;
+
+ public float winScale = 1f;
+ public float winX = 0f;
+ public float winY = 0f;
+ public Rect winCrop = new Rect();
+
+ @Override
+ public String toString() {
+ return "taskScale=" + taskScale + " taskX=" + taskX + " taskY=" + taskY
+ + " winScale=" + winScale + " winX=" + winX + " winY=" + winY
+ + " winCrop=" + winCrop;
+ }
+ }
+
+ private TaskWindowBounds mTmpTaskWindowBounds = new TaskWindowBounds();
+ private Rect mTmpInsets = new Rect();
+
+ private Rect mWindow;
+ private Rect mInsetWindow;
+ private Rect mInsets;
+ private Rect mTask;
+ private Rect mTaskInsets;
+ private Rect mThumbnail;
+
+ private float mInitialTaskScale;
+ private float mInitialTaskTranslationX;
+ private float mFinalTaskScale;
+ private Rect mScaledTask;
+ private Rect mTargetTask;
+ private Rect mSrcWindow;
+
+ public RecentsAnimationInterpolator(Rect window, Rect insets, Rect task, Rect taskInsets,
+ float taskScale, float taskTranslationX) {
+ mWindow = window;
+ mInsets = insets;
+ mTask = task;
+ mTaskInsets = taskInsets;
+ mInsetWindow = new Rect(window);
+ Utilities.insetRect(mInsetWindow, insets);
+
+ mThumbnail = new Rect(task);
+ Utilities.insetRect(mThumbnail, taskInsets);
+ mInitialTaskScale = taskScale;
+ mInitialTaskTranslationX = taskTranslationX;
+ mFinalTaskScale = (float) mInsetWindow.width() / mThumbnail.width();
+ mScaledTask = new Rect(task);
+ Utilities.scaleRectAboutCenter(mScaledTask, mFinalTaskScale);
+ Rect finalScaledTaskInsets = new Rect(taskInsets);
+ Utilities.scaleRect(finalScaledTaskInsets, mFinalTaskScale);
+ mTargetTask = new Rect(mInsetWindow);
+ mTargetTask.offsetTo(window.left + insets.left - finalScaledTaskInsets.left,
+ window.top + insets.top - finalScaledTaskInsets.top);
+
+ float initialWinScale = 1f / mFinalTaskScale;
+ Rect scaledWindow = new Rect(mInsetWindow);
+ Utilities.scaleRectAboutCenter(scaledWindow, initialWinScale);
+ Rect scaledInsets = new Rect(insets);
+ Utilities.scaleRect(scaledInsets, initialWinScale);
+ mSrcWindow = new Rect(scaledWindow);
+ mSrcWindow.offsetTo(mThumbnail.left - scaledInsets.left,
+ mThumbnail.top - scaledInsets.top);
+ }
+
+ public TaskWindowBounds interpolate(float t) {
+ mTmpTaskWindowBounds.taskScale = Utilities.mapRange(t,
+ mInitialTaskScale, mFinalTaskScale);
+ mTmpTaskWindowBounds.taskX = Utilities.mapRange(t,
+ mInitialTaskTranslationX, mTargetTask.left - mScaledTask.left);
+ mTmpTaskWindowBounds.taskY = Utilities.mapRange(t,
+ 0, mTargetTask.top - mScaledTask.top);
+
+ float taskScale = Utilities.mapRange(t, 1, mFinalTaskScale);
+ mTmpTaskWindowBounds.winScale = taskScale / mFinalTaskScale;
+ mTmpTaskWindowBounds.winX = Utilities.mapRange(t,
+ mSrcWindow.left, 0);
+ mTmpTaskWindowBounds.winY = Utilities.mapRange(t,
+ mSrcWindow.top, 0);
+
+ mTmpInsets.set(mInsets);
+ Utilities.scaleRect(mTmpInsets, (1f - t));
+ mTmpTaskWindowBounds.winCrop.set(mWindow);
+ Utilities.insetRect(mTmpTaskWindowBounds.winCrop, mTmpInsets);
+
+ return mTmpTaskWindowBounds;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 7c98317..12f8d52 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep;
+import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -27,10 +28,13 @@
public RecentsAnimationControllerCompat controller;
public RemoteAnimationTargetCompat[] targets;
- private boolean mInputConsumerEnabled;
+ private boolean mInputConsumerEnabled = false;
+ private boolean mBehindSystemBars = true;
+ private boolean mSplitScreenMinimized = false;
public synchronized void setController(
RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] targets) {
+ TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
this.controller = controller;
this.targets = targets;
@@ -39,12 +43,21 @@
}
}
- public void finish(boolean toHome) {
+ /**
+ * @param onFinishComplete A callback that runs after the animation controller has finished
+ * on the background thread.
+ */
+ public void finish(boolean toHome, Runnable onFinishComplete) {
BackgroundExecutor.get().submit(() -> {
synchronized (this) {
+ TraceHelper.endSection("RecentsController",
+ "Finish " + controller + ", toHome=" + toHome);
if (controller != null) {
controller.setInputConsumerEnabled(false);
controller.finish(toHome);
+ if (onFinishComplete != null) {
+ onFinishComplete.run();
+ }
}
}
});
@@ -55,6 +68,8 @@
if (mInputConsumerEnabled) {
BackgroundExecutor.get().submit(() -> {
synchronized (this) {
+ TraceHelper.partitionSection("RecentsController",
+ "Enabling consumer on " + controller);
if (controller != null) {
controller.setInputConsumerEnabled(true);
}
@@ -62,4 +77,42 @@
});
}
}
+
+ public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
+ if (mBehindSystemBars == behindSystemBars) {
+ return;
+ }
+ mBehindSystemBars = behindSystemBars;
+ BackgroundExecutor.get().submit(() -> {
+ synchronized (this) {
+ TraceHelper.partitionSection("RecentsController",
+ "Setting behind system bars on " + controller);
+ if (controller != null) {
+ controller.setAnimationTargetsBehindSystemBars(behindSystemBars);
+ }
+ }
+ });
+ }
+
+ /**
+ * NOTE: As a workaround for conflicting animations (Launcher animating the task leash, and
+ * SystemUI resizing the docked stack, which resizes the task), we currently only set the
+ * minimized mode, and not the inverse.
+ * TODO: Synchronize the minimize animation with the launcher animation
+ */
+ public void setSplitScreenMinimizedForTransaction(boolean minimized) {
+ if (mSplitScreenMinimized || !minimized) {
+ return;
+ }
+ mSplitScreenMinimized = minimized;
+ BackgroundExecutor.get().submit(() -> {
+ synchronized (this) {
+ TraceHelper.partitionSection("RecentsController",
+ "Setting minimize dock on " + controller);
+ if (controller != null) {
+ controller.setSplitScreenMinimized(minimized);
+ }
+ }
+ });
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index d10c5a6..4652f2d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,22 +16,36 @@
package com.android.quickstep;
import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.os.Build;
+import android.os.Bundle;
import android.os.Looper;
import android.os.UserHandle;
+import android.support.annotation.WorkerThread;
+import android.util.LruCache;
+import android.util.SparseArray;
+import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
+import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.recents.model.IconLoader;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.recents.model.TaskKeyLruCache;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
@@ -40,7 +54,6 @@
*/
@TargetApi(Build.VERSION_CODES.O)
public class RecentsModel extends TaskStackChangeListener {
-
// We do not need any synchronization for this variable as its only written on UI thread.
private static RecentsModel INSTANCE;
@@ -60,6 +73,9 @@
return INSTANCE;
}
+ private final SparseArray<Bundle> mCachedAssistData = new SparseArray<>(1);
+ private final ArrayList<AssistDataListener> mAssistDataListeners = new ArrayList<>();
+
private final Context mContext;
private final RecentsTaskLoader mRecentsTaskLoader;
private final MainThreadExecutor mMainThreadExecutor;
@@ -68,21 +84,36 @@
private int mLastLoadPlanId;
private int mTaskChangeId;
private ISystemUiProxy mSystemUiProxy;
+ private boolean mClearAssistCacheOnStackChange = true;
+ private final boolean mPreloadTasksInBackground;
+ private final AccessibilityManager mAccessibilityManager;
private RecentsModel(Context context) {
mContext = context;
+ ActivityManager activityManager =
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ mPreloadTasksInBackground = !activityManager.isLowRamDevice();
+ mMainThreadExecutor = new MainThreadExecutor();
+
Resources res = context.getResources();
mRecentsTaskLoader = new RecentsTaskLoader(mContext,
res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
- res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
- mRecentsTaskLoader.startLoader(mContext);
+ res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0) {
- mMainThreadExecutor = new MainThreadExecutor();
+ @Override
+ protected IconLoader createNewIconLoader(Context context,
+ TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+ return new NormalizedIconLoader(context, iconCache, activityInfoCache);
+ }
+ };
+ mRecentsTaskLoader.startLoader(mContext);
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
mTaskChangeId = 1;
loadTasks(-1, null);
+ mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
}
public RecentsTaskLoader getRecentsTaskLoader() {
@@ -112,7 +143,7 @@
// Preload the plan
RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
PreloadOptions opts = new PreloadOptions();
- opts.loadTitles = false;
+ opts.loadTitles = mAccessibilityManager.isEnabled();
loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
// Set the load plan on UI thread
mMainThreadExecutor.execute(() -> {
@@ -128,8 +159,50 @@
}
@Override
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+ mTaskChangeId++;
+ }
+
+ @Override
+ public void onActivityUnpinned() {
+ mTaskChangeId++;
+ }
+
+ @Override
public void onTaskStackChanged() {
mTaskChangeId++;
+
+ Preconditions.assertUIThread();
+ if (mClearAssistCacheOnStackChange) {
+ mCachedAssistData.clear();
+ } else {
+ mClearAssistCacheOnStackChange = true;
+ }
+ }
+
+ @Override
+ public void onTaskStackChangedBackground() {
+ int userId = UserHandle.myUserId();
+ if (!mPreloadTasksInBackground || !checkCurrentUserId(userId, false /* debug */)) {
+ // TODO: Only register this for the current user
+ return;
+ }
+
+ // Preload a fixed number of task icons/thumbnails in the background
+ ActivityManager.RunningTaskInfo runningTaskInfo =
+ ActivityManagerWrapper.getInstance().getRunningTask();
+ RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(mContext);
+ RecentsTaskLoadPlan.Options launchOpts = new RecentsTaskLoadPlan.Options();
+ launchOpts.runningTaskId = runningTaskInfo != null ? runningTaskInfo.id : -1;
+ launchOpts.numVisibleTasks = 2;
+ launchOpts.numVisibleTaskThumbnails = 2;
+ launchOpts.onlyLoadForCache = true;
+ launchOpts.onlyLoadPausedActivities = true;
+ launchOpts.loadThumbnails = true;
+ PreloadOptions preloadOpts = new PreloadOptions();
+ preloadOpts.loadTitles = mAccessibilityManager.isEnabled();
+ plan.preloadPlan(preloadOpts, mRecentsTaskLoader, -1, userId);
+ mRecentsTaskLoader.loadTasks(plan, launchOpts);
}
public boolean isLoadPlanValid(int resultId) {
@@ -147,4 +220,53 @@
public ISystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
}
+
+ public void onStart() {
+ mRecentsTaskLoader.startLoader(mContext);
+ mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(true);
+ }
+
+ public void onTrimMemory(int level) {
+ if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
+ // We already stop the loader in UI_HIDDEN, so stop the high res loader as well
+ mRecentsTaskLoader.getHighResThumbnailLoader().setVisible(false);
+ }
+ mRecentsTaskLoader.onTrimMemory(level);
+ }
+
+ @WorkerThread
+ public void preloadAssistData(int taskId, Bundle data) {
+ mMainThreadExecutor.execute(() -> {
+ mCachedAssistData.put(taskId, data);
+ // We expect a stack change callback after the assist data is set. So ignore the
+ // very next stack change callback.
+ mClearAssistCacheOnStackChange = false;
+
+ int count = mAssistDataListeners.size();
+ for (int i = 0; i < count; i++) {
+ mAssistDataListeners.get(i).onAssistDataReceived(taskId);
+ }
+ });
+ }
+
+ public Bundle getAssistData(int taskId) {
+ Preconditions.assertUIThread();
+ return mCachedAssistData.get(taskId);
+ }
+
+ public void addAssistDataListener(AssistDataListener listener) {
+ mAssistDataListeners.add(listener);
+ }
+
+ public void removeAssistDataListener(AssistDataListener listener) {
+ mAssistDataListeners.remove(listener);
+ }
+
+ /**
+ * Callback for receiving assist data
+ */
+ public interface AssistDataListener {
+
+ void onAssistDataReceived(int taskId);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
deleted file mode 100644
index 8e03f37..0000000
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ /dev/null
@@ -1,497 +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.quickstep;
-
-import android.animation.LayoutTransition;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.Shader;
-import android.graphics.Shader.TileMode;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.PagedView;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.OverviewState;
-import com.android.launcher3.uioverrides.RecentsViewStateController;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.TaskStack;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-
-import java.util.ArrayList;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.quickstep.TaskView.CURVE_FACTOR;
-import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR;
-
-/**
- * A list of recent tasks.
- */
-public class RecentsView extends PagedView implements Insettable {
-
- private static final Rect sTempStableInsets = new Rect();
-
- public static final int SCROLL_TYPE_NONE = 0;
- public static final int SCROLL_TYPE_TASK = 1;
- public static final int SCROLL_TYPE_WORKSPACE = 2;
-
- private final Launcher mLauncher;
- private QuickScrubController mQuickScrubController;
- private final ScrollState mScrollState = new ScrollState();
- private boolean mOverviewStateEnabled;
- private boolean mTaskStackListenerRegistered;
- private LayoutTransition mLayoutTransition;
-
- /**
- * TODO: Call reloadIdNeeded in onTaskStackChanged.
- */
- private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
- @Override
- public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
- for (int i = mFirstTaskIndex; i < getChildCount(); i++) {
- final TaskView taskView = (TaskView) getChildAt(i);
- if (taskView.getTask().key.id == taskId) {
- taskView.getThumbnail().setThumbnail(taskView.getTask(), snapshot);
- return;
- }
- }
- }
- };
-
- private RecentsViewStateController mStateController;
- private int mFirstTaskIndex;
-
- private final RecentsModel mModel;
- private int mLoadPlanId = -1;
-
- // Only valid until the launcher state changes to NORMAL
- private int mRunningTaskId = -1;
-
- private Bitmap mScrim;
- private Paint mFadePaint;
- private Shader mFadeShader;
- private Matrix mFadeMatrix;
- private boolean mScrimOnLeft;
-
- public RecentsView(Context context) {
- this(context, null);
- }
-
- public RecentsView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
- enableFreeScroll(true);
- setupLayoutTransition();
- setClipToOutline(true);
-
- mLauncher = Launcher.getLauncher(context);
- mQuickScrubController = new QuickScrubController(mLauncher);
- mModel = RecentsModel.getInstance(context);
-
- mScrollState.isRtl = mIsRtl;
- }
-
- public void updateThumbnail(int taskId, ThumbnailData thumbnailData) {
- for (int i = mFirstTaskIndex; i < getChildCount(); i++) {
- final TaskView taskView = (TaskView) getChildAt(i);
- if (taskView.getTask().key.id == taskId) {
- taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
- return;
- }
- }
- }
-
- private void setupLayoutTransition() {
- // We want to show layout transitions when pages are deleted, to close the gap.
- mLayoutTransition = new LayoutTransition();
- mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
- mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-
- mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
- mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
- setLayoutTransition(mLayoutTransition);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mFirstTaskIndex = getPageCount();
- }
-
- @Override
- protected void onWindowVisibilityChanged(int visibility) {
- super.onWindowVisibilityChanged(visibility);
- updateTaskStackListenerState();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- updateTaskStackListenerState();
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- updateTaskStackListenerState();
- }
-
- @Override
- public void setInsets(Rect insets) {
- mInsets.set(insets);
- DeviceProfile dp = Launcher.getLauncher(getContext()).getDeviceProfile();
- Rect padding = getPadding(dp, getContext());
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- lp.bottomMargin = padding.bottom;
- setLayoutParams(lp);
-
- setPadding(padding.left, padding.top, padding.right, 0);
-
- if (dp.isVerticalBarLayout()) {
- boolean wasScrimOnLeft = mScrimOnLeft;
- mScrimOnLeft = dp.isSeascape();
-
- if (mScrim == null || wasScrimOnLeft != mScrimOnLeft) {
- Drawable scrim = getContext().getDrawable(mScrimOnLeft
- ? R.drawable.recents_horizontal_scrim_left
- : R.drawable.recents_horizontal_scrim_right);
- if (scrim instanceof BitmapDrawable) {
- mScrim = ((BitmapDrawable) scrim).getBitmap();
- mFadePaint = new Paint();
- mFadePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
- mFadeShader = new BitmapShader(mScrim, TileMode.CLAMP, TileMode.REPEAT);
- mFadeMatrix = new Matrix();
- } else {
- mScrim = null;
- }
- }
- } else {
- mScrim = null;
- mFadePaint = null;
- mFadeShader = null;
- mFadeMatrix = null;
- }
- }
-
- public int getFirstTaskIndex() {
- return mFirstTaskIndex;
- }
-
- public void setStateController(RecentsViewStateController stateController) {
- mStateController = stateController;
- }
-
- public RecentsViewStateController getStateController() {
- return mStateController;
- }
-
- public void setOverviewStateEnabled(boolean enabled) {
- mOverviewStateEnabled = enabled;
- updateTaskStackListenerState();
- }
-
- private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
- final RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
- TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
- if (stack == null) {
- removeAllViews();
- return;
- }
-
- // Ensure there are as many views as there are tasks in the stack (adding and trimming as
- // necessary)
- final LayoutInflater inflater = LayoutInflater.from(getContext());
- final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
- setLayoutTransition(null);
-
- final int requiredChildCount = tasks.size() + mFirstTaskIndex;
- for (int i = getChildCount(); i < requiredChildCount; i++) {
- final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
- addView(taskView);
- }
- while (getChildCount() > requiredChildCount) {
- final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
- removeView(taskView);
- loader.unloadTaskData(taskView.getTask());
- }
- setLayoutTransition(mLayoutTransition);
-
- // Rebind all task views
- for (int i = tasks.size() - 1; i >= 0; i--) {
- final Task task = tasks.get(i);
- final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1 + mFirstTaskIndex);
- taskView.bind(task);
- loader.loadTaskData(task);
- }
- }
-
- private void updateTaskStackListenerState() {
- boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow()
- && getWindowVisibility() == VISIBLE;
- if (registerStackListener != mTaskStackListenerRegistered) {
- if (registerStackListener) {
- ActivityManagerWrapper.getInstance()
- .registerTaskStackListener(mTaskStackListener);
- reloadIfNeeded();
- } else {
- ActivityManagerWrapper.getInstance()
- .unregisterTaskStackListener(mTaskStackListener);
- }
- mTaskStackListenerRegistered = registerStackListener;
- }
- }
-
- private static Rect getPadding(DeviceProfile profile, Context context) {
- WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets);
- Rect padding = new Rect(profile.workspacePadding);
-
- float taskWidth = profile.widthPx - sTempStableInsets.left - sTempStableInsets.right;
- float taskHeight = profile.heightPx - sTempStableInsets.top - sTempStableInsets.bottom;
-
- float overviewHeight, overviewWidth;
- if (profile.isVerticalBarLayout()) {
- // Use the same padding on both sides for symmetry.
- float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right);
- float availableHeight = profile.availableHeightPx - padding.top
- - sTempStableInsets.top - Math.max(padding.bottom,
- profile.heightPx * (1 - OverviewState.getVerticalProgress(profile, context)));
- float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
- overviewHeight = taskHeight * scaledRatio;
- overviewWidth = taskWidth * scaledRatio;
-
- } else {
- overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
- - sTempStableInsets.top;
- overviewWidth = taskWidth * overviewHeight / taskHeight;
- }
-
- padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top
- - Math.round(overviewHeight);
- padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
- return padding;
- }
-
- /**
- * Sets the {@param outRect} to match the position of the first tile such that it is scaled
- * down to match the 2nd taskView.
- * @return returns the factor which determines the scaling factor for the second task.
- */
- public static float getScaledDownPageRect(DeviceProfile dp, Context context, Rect outRect) {
- getPageRect(dp, context, outRect);
-
- int pageSpacing = context.getResources()
- .getDimensionPixelSize(R.dimen.recents_page_spacing);
- float halfScreenWidth = dp.widthPx * 0.5f;
- float halfPageWidth = outRect.width() * 0.5f;
- float pageCenter = outRect.right + pageSpacing + halfPageWidth;
- float distanceFromCenter = Math.abs(halfScreenWidth - pageCenter);
- float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
- float linearInterpolation = Math.min(1, distanceFromCenter / distanceToReachEdge);
-
- float scale = 1 - CURVE_INTERPOLATOR.getInterpolation(linearInterpolation) * CURVE_FACTOR;
-
- int topMargin = context.getResources()
- .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- outRect.top -= topMargin;
- Utilities.scaleRectAboutCenter(outRect, scale);
- outRect.top += (int) (scale * topMargin);
- return linearInterpolation;
- }
-
- public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
- Rect targetPadding = getPadding(grid, context);
- Rect insets = grid.getInsets();
- outRect.set(
- targetPadding.left + insets.left,
- targetPadding.top + insets.top,
- grid.widthPx - targetPadding.right - insets.right,
- grid.heightPx - targetPadding.bottom - insets.bottom);
- outRect.top += context.getResources()
- .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- }
-
- @Override
- public void computeScroll() {
- super.computeScroll();
- updateCurveProperties();
- }
-
- /**
- * Scales and adjusts translation of adjacent pages as if on a curved carousel.
- */
- private void updateCurveProperties() {
- if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
- return;
- }
- final int halfPageWidth = mScrollState.halfPageWidth = getNormalChildWidth() / 2;
- mScrollState.lastScrollType = SCROLL_TYPE_NONE;
- final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth;
- final int halfScreenWidth = getMeasuredWidth() / 2;
- final int pageSpacing = mPageSpacing;
-
- final int pageCount = getPageCount();
- for (int i = 0; i < pageCount; i++) {
- View page = getPageAt(i);
- int pageCenter = page.getLeft() + halfPageWidth;
- mScrollState.distanceFromScreenCenter = screenCenter - pageCenter;
- float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
- mScrollState.linearInterpolation = Math.min(1,
- Math.abs(mScrollState.distanceFromScreenCenter) / distanceToReachEdge);
- mScrollState.lastScrollType = ((PageCallbacks) page).onPageScroll(mScrollState);
- }
- }
-
- public void onTaskDismissed(TaskView taskView) {
- ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
- removeView(taskView);
- if (getTaskCount() == 0) {
- mLauncher.getStateManager().goToState(NORMAL);
- }
- }
-
- public void reset() {
- mRunningTaskId = -1;
- setCurrentPage(0);
- }
-
- public int getTaskCount() {
- return getChildCount() - mFirstTaskIndex;
- }
-
- /**
- * Reloads the view if anything in recents changed.
- */
- public void reloadIfNeeded() {
- if (!mModel.isLoadPlanValid(mLoadPlanId)) {
- mLoadPlanId = mModel.loadTasks(mRunningTaskId, this::applyLoadPlan);
- }
- }
-
- /**
- * Ensures that the first task in the view represents {@param task} and reloads the view
- * if needed. This allows the swipe-up gesture to assume that the first tile always
- * corresponds to the correct task.
- * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
- * is called.
- * Also scrolls the view to this task
- */
- public void showTask(int runningTaskId) {
- boolean needsReload = false;
- if (getTaskCount() == 0) {
- needsReload = true;
- // Add an empty view for now
- setLayoutTransition(null);
- final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
- .inflate(R.layout.task, this, false);
- addView(taskView, mFirstTaskIndex);
- setLayoutTransition(mLayoutTransition);
- }
- if (!needsReload) {
- needsReload = !mModel.isLoadPlanValid(mLoadPlanId);
- }
- if (needsReload) {
- mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
- }
- mRunningTaskId = runningTaskId;
- setCurrentPage(mFirstTaskIndex);
- if (mCurrentPage >= mFirstTaskIndex) {
- ((TaskView) getPageAt(mCurrentPage)).setIconScale(0);
- }
- }
-
- public QuickScrubController getQuickScrubController() {
- return mQuickScrubController;
- }
-
- @Override
- public void draw(Canvas canvas) {
- if (mScrim == null) {
- super.draw(canvas);
- return;
- }
-
- final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
-
- int length = mScrim.getWidth();
- int height = getHeight();
- int saveCount = canvas.getSaveCount();
-
- int scrimLeft;
- if (mScrimOnLeft) {
- scrimLeft = getScrollX();
- } else {
- scrimLeft = getScrollX() + getWidth() - length;
- }
- canvas.saveLayer(scrimLeft, 0, scrimLeft + length, height, null, flags);
- super.draw(canvas);
-
- mFadeMatrix.setTranslate(scrimLeft, 0);
- mFadeShader.setLocalMatrix(mFadeMatrix);
- mFadePaint.setShader(mFadeShader);
- canvas.drawRect(scrimLeft, 0, scrimLeft + length, height, mFadePaint);
- canvas.restoreToCount(saveCount);
- }
-
- public interface PageCallbacks {
-
- /**
- * Updates the page UI based on scroll params and returns the type of scroll
- * effect performed.
- *
- * @see #SCROLL_TYPE_NONE
- * @see #SCROLL_TYPE_TASK
- * @see #SCROLL_TYPE_WORKSPACE
- */
- int onPageScroll(ScrollState scrollState);
- }
-
- public static class ScrollState {
-
- public boolean isRtl;
- public int lastScrollType;
-
- public int halfPageWidth;
- public float distanceFromScreenCenter;
- public float linearInterpolation;
-
- public float prevPageExtraWidth;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/SimpleTaskView.java b/quickstep/src/com/android/quickstep/SimpleTaskView.java
deleted file mode 100644
index 8425fa3..0000000
--- a/quickstep/src/com/android/quickstep/SimpleTaskView.java
+++ /dev/null
@@ -1,52 +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.quickstep;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.WindowManager;
-
-/**
- * A simple view which keeps its size proportional to the display size
- */
-public class SimpleTaskView extends View {
-
- private static final Point sTempPoint = new Point();
-
- public SimpleTaskView(Context context) {
- super(context);
- }
-
- public SimpleTaskView(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public SimpleTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int height = MeasureSpec.getSize(heightMeasureSpec);
- getContext().getSystemService(WindowManager.class)
- .getDefaultDisplay().getRealSize(sTempPoint);
-
- int width = (int) ((float) height * sTempPoint.x / sTempPoint.y);
- setMeasuredDimension(width, height);
- }
-}
diff --git a/quickstep/src/com/android/quickstep/SnapshotDragView.java b/quickstep/src/com/android/quickstep/SnapshotDragView.java
deleted file mode 100644
index 2ef3942..0000000
--- a/quickstep/src/com/android/quickstep/SnapshotDragView.java
+++ /dev/null
@@ -1,92 +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.quickstep;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.systemui.shared.recents.view.AnimateableViewBounds;
-
-/**
- * Floating view which shows the task snapshot allowing it to be dragged and placed.
- */
-public class SnapshotDragView extends AbstractFloatingView implements Insettable {
-
- private final Launcher mLauncher;
- private final Bitmap mSnapshot;
- private final AnimateableViewBounds mViewBounds;
-
- public SnapshotDragView(Launcher launcher, Bitmap snapshot) {
- super(launcher, null);
- mLauncher = launcher;
- mSnapshot = snapshot;
- mViewBounds = new AnimateableViewBounds(this, 0);
- setWillNotDraw(false);
- setClipToOutline(true);
- setOutlineProvider(mViewBounds);
- }
-
- AnimateableViewBounds getViewBounds() {
- return mViewBounds;
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mSnapshot != null) {
- setMeasuredDimension(mSnapshot.getWidth(), mSnapshot.getHeight());
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- @Override
- public void setInsets(Rect insets) {
-
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mSnapshot != null) {
- canvas.drawBitmap(mSnapshot, 0, 0, null);
- }
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- return false;
- }
-
- @Override
- protected void handleClose(boolean animate) {
- // We dont suupport animate.
- mLauncher.getDragLayer().removeView(this);
- }
-
- @Override
- public void logActionCommand(int command) {
- // We should probably log the weather
- }
-
- @Override
- protected boolean isOfType(int type) {
- return (type & TYPE_QUICKSTEP_PREVIEW) != 0;
- }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index c2fb7be..66969c6 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -23,6 +23,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
@@ -47,7 +48,7 @@
public static class TaskOverlay {
- public void setTaskInfo(ThumbnailData thumbnail, Matrix matrix) { }
+ public void setTaskInfo(Task task, ThumbnailData thumbnail, Matrix matrix) { }
public void reset() { }
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index ca99253..2ebf252 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -16,28 +16,41 @@
package com.android.quickstep;
-import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
+import android.view.ViewTreeObserver.OnPreDrawListener;
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.InstantAppResolver;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
+import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
+import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.util.Collections;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -59,26 +72,27 @@
}
@Override
- public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
return null;
}
- public View.OnClickListener getOnClickListener(final Launcher launcher, final TaskView view) {
+ public View.OnClickListener getOnClickListener(BaseDraggingActivity activity, TaskView view) {
Task task = view.getTask();
ShortcutInfo dummyInfo = new ShortcutInfo();
dummyInfo.intent = new Intent();
ComponentName component = task.getTopComponent();
dummyInfo.intent.setComponent(component);
- dummyInfo.user = UserHandle.getUserHandleForUid(task.key.userId);
- dummyInfo.title = TaskUtils.getTitle(launcher, task);
+ dummyInfo.user = UserHandle.of(task.key.userId);
+ dummyInfo.title = TaskUtils.getTitle(activity, task);
- return getOnClickListenerForTask(launcher, task, dummyInfo);
+ return getOnClickListenerForTask(activity, task, dummyInfo);
}
- protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher,
- final Task task, final ItemInfo dummyInfo) {
- return mSystemShortcut.getOnClickListener(launcher, dummyInfo);
+ protected View.OnClickListener getOnClickListenerForTask(
+ BaseDraggingActivity activity, Task task, ItemInfo dummyInfo) {
+ return mSystemShortcut.getOnClickListener(activity, dummyInfo);
}
public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
@@ -87,9 +101,13 @@
}
}
- public static class SplitScreen extends TaskSystemShortcut {
+ public static class SplitScreen extends TaskSystemShortcut implements OnPreDrawListener,
+ DeviceProfile.OnDeviceProfileChangeListener, View.OnLayoutChangeListener {
private Handler mHandler;
+ private RecentsView mRecentsView;
+ private TaskView mTaskView;
+ private BaseDraggingActivity mActivity;
public SplitScreen() {
super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
@@ -97,27 +115,95 @@
}
@Override
- public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
- if (launcher.getDeviceProfile().inMultiWindowMode()) {
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, TaskView taskView) {
+ if (activity.getDeviceProfile().isMultiWindowMode) {
return null;
}
+ final Task task = taskView.getTask();
+ final int taskId = task.key.id;
+ if (!task.isDockable) {
+ return null;
+ }
+ mActivity = activity;
+ mRecentsView = activity.getOverviewPanel();
+ mTaskView = taskView;
+ final TaskThumbnailView thumbnailView = taskView.getThumbnail();
return (v -> {
- Task task = taskView.getTask();
- final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(true);
- final Consumer<Boolean> resultCallback = success -> {
- if (success) {
- launcher.<RecentsView>getOverviewPanel().removeView(taskView);
- }
- };
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
- task.key, options, resultCallback, mHandler);
+ AbstractFloatingView.closeOpenViews(activity, true,
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
- // TODO improve transition; see:
- // DockedFirstAnimationFrameEvent
- // RecentsTransitionHelper#composeDockAnimationSpec
- // WindowManagerWrapper#overridePendingAppTransitionMultiThumbFuture
+ if (ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
+ ActivityOptionsCompat.makeSplitScreenOptions(true))) {
+ ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
+ try {
+ sysUiProxy.onSplitScreenInvoked();
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to notify SysUI of split screen: ", e);
+ return;
+ }
+
+ // Add a device profile change listener to kick off animating the side tasks
+ // once we enter multiwindow mode and relayout
+ activity.addOnDeviceProfileChangeListener(this);
+
+ final Runnable animStartedListener = () -> {
+ // Hide the task view and wait for the window to be resized
+ // TODO: Consider animating in launcher and do an in-place start activity
+ // afterwards
+ mRecentsView.addIgnoreResetTask(mTaskView);
+ mTaskView.setAlpha(0f);
+ mTaskView.getViewTreeObserver().addOnPreDrawListener(SplitScreen.this);
+ };
+
+ final int[] position = new int[2];
+ thumbnailView.getLocationOnScreen(position);
+ final int width = (int) (thumbnailView.getWidth() * taskView.getScaleX());
+ final int height = (int) (thumbnailView.getHeight() * taskView.getScaleY());
+ final Rect taskBounds = new Rect(position[0], position[1],
+ position[0] + width, position[1] + height);
+
+ Bitmap thumbnail = RecentsTransition.drawViewIntoHardwareBitmap(
+ taskBounds.width(), taskBounds.height(), thumbnailView, 1f,
+ Color.BLACK);
+ AppTransitionAnimationSpecsFuture future =
+ new AppTransitionAnimationSpecsFuture(mHandler) {
+ @Override
+ public List<AppTransitionAnimationSpecCompat> composeSpecs() {
+ return Collections.singletonList(new AppTransitionAnimationSpecCompat(
+ taskId, thumbnail, taskBounds));
+ }
+ };
+ WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
+ future, animStartedListener, mHandler, true /* scaleUp */);
+ }
});
}
+
+ @Override
+ public boolean onPreDraw() {
+ mTaskView.getViewTreeObserver().removeOnPreDrawListener(this);
+ WindowManagerWrapper.getInstance().endProlongedAnimations();
+ return true;
+ }
+
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mActivity.removeOnDeviceProfileChangeListener(this);
+ if (dp.isMultiWindowMode) {
+ mTaskView.getRootView().addOnLayoutChangeListener(this);
+ }
+ }
+
+ @Override
+ public void onLayoutChange(View v, int l, int t, int r, int b,
+ int oldL, int oldT, int oldR, int oldB) {
+ mTaskView.getRootView().removeOnLayoutChangeListener(this);
+ mRecentsView.removeIgnoreResetTask(mTaskView);
+
+ // Start animating in the side pages once launcher has been resized
+ mRecentsView.dismissTask(mTaskView, false, false);
+ }
}
public static class Pin extends TaskSystemShortcut {
@@ -130,12 +216,17 @@
}
@Override
- public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
- ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy();
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, TaskView taskView) {
+ ISystemUiProxy sysUiProxy = RecentsModel.getInstance(activity).getSystemUiProxy();
if (sysUiProxy == null) {
return null;
}
- if (!ActivityManagerWrapper.getInstance().isLockToAppEnabled()) {
+ if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
+ return null;
+ }
+ if (ActivityManagerWrapper.getInstance().isLockToAppActive()) {
+ // We shouldn't be able to pin while an app is locked.
return null;
}
return view -> {
@@ -159,11 +250,11 @@
}
@Override
- protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task,
- ItemInfo itemInfo) {
- if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher,
+ protected View.OnClickListener getOnClickListenerForTask(
+ BaseDraggingActivity activity, Task task, ItemInfo itemInfo) {
+ if (InstantAppResolver.newInstance(activity).isInstantApp(activity,
task.getTopComponent().getPackageName())) {
- return mSystemShortcut.createOnClickListener(launcher, itemInfo);
+ return mSystemShortcut.createOnClickListener(activity, itemInfo);
}
return null;
}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
deleted file mode 100644
index 36a0601..0000000
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ /dev/null
@@ -1,206 +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.quickstep;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ComposeShader;
-import android.graphics.LightingColorFilter;
-import android.graphics.LinearGradient;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Shader;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-
-/**
- * A task in the Recents view.
- */
-public class TaskThumbnailView extends View {
-
- private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
-
- private final float mCornerRadius;
- private final float mFadeLength;
-
- private final TaskOverlay mOverlay;
- private final Paint mPaint = new Paint();
-
- private final Matrix mMatrix = new Matrix();
-
- private ThumbnailData mThumbnailData;
- protected BitmapShader mBitmapShader;
-
- private float mDimAlpha = 1f;
-
- public TaskThumbnailView(Context context) {
- this(context, null);
- }
-
- public TaskThumbnailView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
- mFadeLength = getResources().getDimension(R.dimen.task_fade_length);
- mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
- }
-
- public void bind() {
- mOverlay.reset();
- }
-
- /**
- * Updates this thumbnail.
- */
- public void setThumbnail(Task task, ThumbnailData thumbnailData) {
- mPaint.setColor(task == null ? Color.BLACK : task.colorBackground | 0xFF000000);
-
- if (thumbnailData != null && thumbnailData.thumbnail != null) {
- Bitmap bm = thumbnailData.thumbnail;
- bm.prepareToDraw();
- mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
- mPaint.setShader(mBitmapShader);
- mThumbnailData = thumbnailData;
- updateThumbnailMatrix();
- } else {
- mBitmapShader = null;
- mThumbnailData = null;
- mPaint.setShader(null);
- mOverlay.reset();
- }
- updateThumbnailPaintFilter();
- }
-
- /**
- * Sets the alpha of the dim layer on top of this view.
- */
- public void setDimAlpha(float dimAlpha) {
- mDimAlpha = dimAlpha;
- updateThumbnailPaintFilter();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
- mCornerRadius, mCornerRadius, mPaint);
- }
-
- private void updateThumbnailPaintFilter() {
- int mul = (int) (mDimAlpha * 255);
- if (mBitmapShader != null) {
- mPaint.setColorFilter(getLightingColorFilter(mul));
- } else {
- mPaint.setColorFilter(null);
- mPaint.setColor(Color.argb(255, mul, mul, mul));
- }
- invalidate();
- }
-
- private void updateThumbnailMatrix() {
- if (mBitmapShader != null && mThumbnailData != null) {
- float scale = mThumbnailData.scale;
- float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
- (mThumbnailData.insets.left + mThumbnailData.insets.right) * scale;
- float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
- (mThumbnailData.insets.top + mThumbnailData.insets.bottom) * scale;
- final float thumbnailScale;
-
- if (getMeasuredWidth() == 0) {
- // If we haven't measured , skip the thumbnail drawing and only draw the background
- // color
- thumbnailScale = 0f;
- } else {
- final Configuration configuration =
- getContext().getApplicationContext().getResources().getConfiguration();
- final DeviceProfile profile = Launcher.getLauncher(getContext()).getDeviceProfile();
- if (configuration.orientation == mThumbnailData.orientation) {
- // If we are in the same orientation as the screenshot, just scale it to the
- // width of the task view
- thumbnailScale = getMeasuredWidth() / thumbnailWidth;
- } else if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
- // Scale the landscape thumbnail up to app size, then scale that to the task
- // view size to match other portrait screenshots
- thumbnailScale = ((float) getMeasuredWidth() / profile.widthPx);
- } else {
- // Otherwise, scale the screenshot to fit 1:1 in the current orientation
- thumbnailScale = 1;
- }
- }
- mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
- -mThumbnailData.insets.top * scale);
- mMatrix.postScale(thumbnailScale, thumbnailScale);
- mBitmapShader.setLocalMatrix(mMatrix);
-
- float bitmapHeight = Math.max(thumbnailHeight * thumbnailScale, 0);
- Shader shader = mBitmapShader;
- if (bitmapHeight < getMeasuredHeight()) {
- int color = mPaint.getColor();
- LinearGradient fade = new LinearGradient(
- 0, bitmapHeight - mFadeLength, 0, bitmapHeight,
- color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
- shader = new ComposeShader(fade, shader, Mode.DST_OVER);
- }
-
- float bitmapWidth = Math.max(thumbnailWidth * thumbnailScale, 0);
- if (bitmapWidth < getMeasuredWidth()) {
- int color = mPaint.getColor();
- LinearGradient fade = new LinearGradient(
- bitmapWidth - mFadeLength, 0, bitmapWidth, 0,
- color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
- shader = new ComposeShader(fade, shader, Mode.DST_OVER);
- }
- mPaint.setShader(shader);
- }
-
- mOverlay.setTaskInfo(mThumbnailData, mMatrix);
- invalidate();
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- updateThumbnailMatrix();
- }
-
- private static LightingColorFilter getLightingColorFilter(int dimColor) {
- if (dimColor < 0) {
- dimColor = 0;
- } else if (dimColor > 255) {
- dimColor = 255;
- }
- if (sDimFilterCache[dimColor] == null) {
- sDimFilterCache[dimColor] =
- new LightingColorFilter(Color.argb(255, dimColor, dimColor, dimColor), 0);
- }
- return sDimFilterCache[dimColor];
- }
-}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index a95e7c1..2df951b 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,10 +16,14 @@
package com.android.quickstep;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.util.Log;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserManagerCompat;
import com.android.systemui.shared.recents.model.Task;
/**
@@ -27,16 +31,21 @@
* TODO: remove this once we switch to getting the icon and label from IconCache.
*/
public class TaskUtils {
+
private static final String TAG = "TaskUtils";
- public static CharSequence getTitle(Launcher launcher, Task task) {
- PackageManager pm = launcher.getPackageManager();
- try {
- return pm.getPackageInfo(task.getTopComponent().getPackageName(), 0)
- .applicationInfo.loadLabel(pm);
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Failed to get title for task " + task, e);
+ public static CharSequence getTitle(Context context, Task task) {
+ LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(context);
+ UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(context);
+ PackageManager packageManager = context.getPackageManager();
+ UserHandle user = UserHandle.of(task.key.userId);
+ ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo(
+ task.getTopComponent().getPackageName(), 0, user);
+ if (applicationInfo == null) {
+ Log.e(TAG, "Failed to get title for task " + task);
+ return "";
}
- return "";
+ return userManagerCompat.getBadgedLabelForUser(
+ applicationInfo.loadLabel(packageManager), user);
}
}
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
deleted file mode 100644
index 6a70e2d..0000000
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ /dev/null
@@ -1,249 +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.quickstep;
-
-import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK;
-import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
-
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.launcher3.R;
-import com.android.quickstep.RecentsView.PageCallbacks;
-import com.android.quickstep.RecentsView.ScrollState;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * A task in the Recents view.
- */
-public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks {
-
- /** Designates how "curvy" the carousel is from 0 to 1, where 0 is a straight line. */
- public static final float CURVE_FACTOR = 0.25f;
- /** A circular curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
- public static final TimeInterpolator CURVE_INTERPOLATOR
- = x -> (float) (1 - Math.sqrt(1 - Math.pow(x, 2)));
-
- /**
- * 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.
- */
- private static final float MAX_PAGE_SCRIM_ALPHA = 0.8f;
-
- private static final long SCALE_ICON_DURATION = 120;
-
- private static final Property<TaskView, Float> SCALE_ICON_PROPERTY =
- new Property<TaskView, Float>(Float.TYPE, "scale_icon") {
- @Override
- public Float get(TaskView taskView) {
- return taskView.mIconScale;
- }
-
- @Override
- public void set(TaskView taskView, Float iconScale) {
- taskView.setIconScale(iconScale);
- }
- };
-
- private Task mTask;
- private TaskThumbnailView mSnapshotView;
- private ImageView mIconView;
- private float mIconScale = 1f;
-
- public TaskView(Context context) {
- this(context, null);
- }
-
- public TaskView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- setOnClickListener((view) -> launchTask(true /* animate */));
- setOutlineProvider(new TaskOutlineProvider(getResources()));
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSnapshotView = findViewById(R.id.snapshot);
- mIconView = findViewById(R.id.icon);
- }
-
- /**
- * Updates this task view to the given {@param task}.
- */
- public void bind(Task task) {
- if (mTask != null) {
- mTask.removeCallback(this);
- }
- mTask = task;
- mSnapshotView.bind();
- task.addCallback(this);
- }
-
- public Task getTask() {
- return mTask;
- }
-
- public TaskThumbnailView getThumbnail() {
- return mSnapshotView;
- }
-
- public void launchTask(boolean animate) {
- launchTask(animate, null, null);
- }
-
- public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
- Handler resultCallbackHandler) {
- if (mTask != null) {
- final ActivityOptions opts;
- if (animate) {
- // Calculate the bounds of the thumbnail to animate from
- final Rect bounds = new Rect();
- final int[] pos = new int[2];
- mSnapshotView.getLocationInWindow(pos);
- bounds.set(pos[0], pos[1],
- pos[0] + mSnapshotView.getWidth(),
- pos[1] + mSnapshotView.getHeight());
- AppTransitionAnimationSpecsFuture animFuture =
- new AppTransitionAnimationSpecsFuture(getHandler()) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- ArrayList<AppTransitionAnimationSpecCompat> specs =
- new ArrayList<>();
- specs.add(new AppTransitionAnimationSpecCompat(mTask.key.id, null,
- bounds));
- return specs;
- }
- };
- opts = RecentsTransition.createAspectScaleAnimation(
- getContext(), getHandler(), true /* scaleUp */, animFuture, null);
- } else {
- opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
- }
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
- opts, resultCallback, resultCallbackHandler);
- }
- }
-
- @Override
- public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
- mSnapshotView.setThumbnail(task, thumbnailData);
- mIconView.setImageDrawable(task.icon);
- mIconView.setOnClickListener(icon -> TaskMenuView.showForTask(this));
- mIconView.setOnLongClickListener(icon -> TaskMenuView.showForTask(this));
- }
-
- @Override
- public void onTaskDataUnloaded() {
- mSnapshotView.setThumbnail(null, null);
- mIconView.setImageDrawable(null);
- mIconView.setOnLongClickListener(null);
- }
-
- @Override
- public void onTaskWindowingModeChanged() {
- // Do nothing
- }
-
- public void animateIconToScale(float scale) {
- ObjectAnimator.ofFloat(this, SCALE_ICON_PROPERTY, scale)
- .setDuration(SCALE_ICON_DURATION).start();
- }
-
- protected void setIconScale(float iconScale) {
- mIconScale = iconScale;
- if (mIconView != null) {
- mIconView.setScaleX(mIconScale);
- mIconView.setScaleY(mIconScale);
- }
- }
-
- @Override
- public int onPageScroll(ScrollState scrollState) {
- float curveInterpolation =
- CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
- float scale = 1 - curveInterpolation * CURVE_FACTOR;
- setScaleX(scale);
- setScaleY(scale);
-
- // Make sure the biggest card (i.e. the one in front) shows on top of the adjacent ones.
- setTranslationZ(scale);
-
- mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
-
- float translation =
- scrollState.distanceFromScreenCenter * curveInterpolation * CURVE_FACTOR;
- setTranslationX(translation);
-
- if (scrollState.lastScrollType == SCROLL_TYPE_WORKSPACE) {
- // Make sure that the task cards do not overlap with the workspace card
- float min = scrollState.halfPageWidth * (1 - scale);
- if (scrollState.isRtl) {
- setTranslationX(Math.min(translation, min) - scrollState.prevPageExtraWidth);
- } else {
- setTranslationX(Math.max(translation, -min) + scrollState.prevPageExtraWidth);
- }
- } else {
- setTranslationX(translation);
- }
- scrollState.prevPageExtraWidth = 0;
- return SCROLL_TYPE_TASK;
- }
-
-
- private static final class TaskOutlineProvider extends ViewOutlineProvider {
-
- private final int mMarginTop;
- private final float mRadius;
-
- TaskOutlineProvider(Resources res) {
- mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- mRadius = res.getDimension(R.dimen.task_corner_radius);
- }
-
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, mMarginTop, view.getWidth(),
- view.getHeight(), mRadius);
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
index 77480af..1290ec3 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -18,6 +18,7 @@
import android.annotation.TargetApi;
import android.os.Build;
import android.support.annotation.IntDef;
+import android.view.Choreographer;
import android.view.MotionEvent;
import java.lang.annotation.Retention;
@@ -28,37 +29,44 @@
@FunctionalInterface
public interface TouchConsumer extends Consumer<MotionEvent> {
- static boolean isInteractionQuick(@InteractionType int interactionType) {
- return interactionType == INTERACTION_QUICK_SCRUB ||
- interactionType == INTERACTION_QUICK_SWITCH;
- }
-
@IntDef(flag = true, value = {
INTERACTION_NORMAL,
- INTERACTION_QUICK_SWITCH,
INTERACTION_QUICK_SCRUB
})
@Retention(RetentionPolicy.SOURCE)
@interface InteractionType {}
int INTERACTION_NORMAL = 0;
- int INTERACTION_QUICK_SWITCH = 1;
- int INTERACTION_QUICK_SCRUB = 2;
+ int INTERACTION_QUICK_SCRUB = 1;
default void reset() { }
- default boolean shouldUseBackgroundConsumer() {
- return false;
- }
-
default void updateTouchTracking(@InteractionType int interactionType) { }
default void onQuickScrubEnd() { }
default void onQuickScrubProgress(float progress) { }
+ default void onQuickStep(float eventX, float eventY, long eventTime) { }
+
/**
* Called on the binder thread to allow the consumer to process the motion event before it is
* posted on a handler thread.
*/
default void preProcessMotionEvent(MotionEvent ev) { }
+
+ default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ return null;
+ }
+
+ default void deferInit() { }
+
+ default boolean deferNextEventToMainThread() {
+ return false;
+ }
+
+ default boolean forceToLauncherConsumer() {
+ return false;
+ }
+
+ default void onShowOverviewFromAltTab() {}
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index e80f97d..b610f4d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,34 +21,39 @@
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
+import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
-import android.content.ComponentName;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.graphics.PointF;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Choreographer;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.model.ModelPreload;
+import com.android.launcher3.R;
+import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.views.RecentsView;
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.NavigationBarCompat.HitTarget;
/**
* Service connected by system-UI for handling touch interaction.
@@ -56,6 +61,15 @@
@TargetApi(Build.VERSION_CODES.O)
public class TouchInteractionService extends Service {
+ private static final SparseArray<String> sMotionEventNames;
+
+ static {
+ sMotionEventNames = new SparseArray<>(3);
+ sMotionEventNames.put(ACTION_DOWN, "ACTION_DOWN");
+ sMotionEventNames.put(ACTION_UP, "ACTION_UP");
+ sMotionEventNames.put(ACTION_CANCEL, "ACTION_CANCEL");
+ }
+
public static final int EDGE_NAV_BAR = 1 << 8;
private static final String TAG = "TouchInteractionService";
@@ -68,61 +82,96 @@
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@Override
+ public void onPreMotionEvent(@HitTarget int downHitTarget) throws RemoteException {
+ TraceHelper.beginSection("SysUiBinder");
+ setupTouchConsumer(downHitTarget);
+ TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget);
+ }
+
+ @Override
public void onMotionEvent(MotionEvent ev) {
- onBinderMotionEvent(ev);
+ mEventQueue.queue(ev);
+
+ String name = sMotionEventNames.get(ev.getActionMasked());
+ if (name != null){
+ TraceHelper.partitionSection("SysUiBinder", name);
+ }
}
@Override
public void onBind(ISystemUiProxy iSystemUiProxy) {
mISystemUiProxy = iSystemUiProxy;
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
- }
-
- @Override
- public void onQuickSwitch() {
- mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SWITCH);
+ RemoteRunnable.executeSafely(() -> mISystemUiProxy.setRecentsOnboardingText(
+ getResources().getString(R.string.recents_swipe_up_onboarding)));
+ mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
}
@Override
public void onQuickScrubStart() {
- mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
- sQuickScrubEnabled = true;
- }
-
- @Override
- public void onQuickScrubEnd() {
- mCurrentConsumer.onQuickScrubEnd();
- sQuickScrubEnabled = false;
+ mEventQueue.onQuickScrubStart();
+ TraceHelper.partitionSection("SysUiBinder", "onQuickScrubStart");
}
@Override
public void onQuickScrubProgress(float progress) {
- mCurrentConsumer.onQuickScrubProgress(progress);
+ mEventQueue.onQuickScrubProgress(progress);
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ mEventQueue.onQuickScrubEnd();
+ TraceHelper.endSection("SysUiBinder", "onQuickScrubEnd");
+ }
+
+ @Override
+ public void onOverviewToggle() {
+ mOverviewCommandHelper.onOverviewToggle();
+ }
+
+ @Override
+ public void onOverviewShown(boolean triggeredFromAltTab) {
+ if (triggeredFromAltTab) {
+ setupTouchConsumer(HIT_TARGET_NONE);
+ mEventQueue.onOverviewShownFromAltTab();
+ } else {
+ mOverviewCommandHelper.onOverviewShown();
+ }
+ }
+
+ @Override
+ public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+ if (triggeredFromAltTab && !triggeredFromHomeKey) {
+ // onOverviewShownFromAltTab initiates quick scrub. Ending it here.
+ mEventQueue.onQuickScrubEnd();
+ }
+ }
+
+ @Override
+ public void onQuickStep(MotionEvent motionEvent) {
+ mEventQueue.onQuickStep(motionEvent);
+ TraceHelper.endSection("SysUiBinder", "onQuickStep");
+
}
};
private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
- private TouchConsumer mCurrentConsumer = mNoOpTouchConsumer;
private static boolean sConnected = false;
- private static boolean sQuickScrubEnabled = false;
public static boolean isConnected() {
return sConnected;
}
- public static boolean isQuickScrubEnabled() {
- return sQuickScrubEnabled;
- }
-
private ActivityManagerWrapper mAM;
- private RunningTaskInfo mRunningTask;
private RecentsModel mRecentsModel;
- private Intent mHomeIntent;
- private ComponentName mLauncher;
private MotionEventQueue mEventQueue;
private MainThreadExecutor mMainThreadExecutor;
private ISystemUiProxy mISystemUiProxy;
+ private OverviewCommandHelper mOverviewCommandHelper;
+ private OverviewInteractionState mOverviewInteractionState;
+
+ private Choreographer mMainThreadChoreographer;
private Choreographer mBackgroundThreadChoreographer;
@Override
@@ -131,27 +180,21 @@
mAM = ActivityManagerWrapper.getInstance();
mRecentsModel = RecentsModel.getInstance(this);
mMainThreadExecutor = new MainThreadExecutor();
+ mOverviewCommandHelper = new OverviewCommandHelper(this);
+ mMainThreadChoreographer = Choreographer.getInstance();
+ mEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
+ mOverviewInteractionState = OverviewInteractionState.getInstance(this);
- mHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(getPackageName())
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0);
- mLauncher = new ComponentName(getPackageName(), info.activityInfo.name);
- // Clear the packageName as system can fail to dedupe it b/64108432
- mHomeIntent.setComponent(mLauncher).setPackage(null);
-
- mEventQueue = new MotionEventQueue(Choreographer.getInstance(), mNoOpTouchConsumer);
sConnected = true;
- new ModelPreload().start(this);
+ // Temporarily disable model preload
+ // new ModelPreload().start(this);
initBackgroundChoreographer();
}
@Override
public void onDestroy() {
sConnected = false;
- sQuickScrubEnabled = false;
super.onDestroy();
}
@@ -161,89 +204,87 @@
return mMyBinder;
}
- private void onBinderMotionEvent(MotionEvent ev) {
- if (ev.getActionMasked() == ACTION_DOWN) {
- mRunningTask = mAM.getRunningTask();
-
- mCurrentConsumer.reset();
- if (mRunningTask == null) {
- mCurrentConsumer = mNoOpTouchConsumer;
- } else if (mRunningTask.topActivity.equals(mLauncher)) {
- mCurrentConsumer = getLauncherConsumer();
- } else {
- mCurrentConsumer = getOtherActivityConsumer();
- }
-
- mEventQueue.setConsumer(mCurrentConsumer);
- mEventQueue.setInterimChoreographer(mCurrentConsumer.shouldUseBackgroundConsumer()
- ? mBackgroundThreadChoreographer : null);
+ private void setupTouchConsumer(@HitTarget int downHitTarget) {
+ mEventQueue.reset();
+ TouchConsumer oldConsumer = mEventQueue.getConsumer();
+ if (oldConsumer.deferNextEventToMainThread()) {
+ mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
+ new DeferredTouchConsumer((v) -> getCurrentTouchConsumer(downHitTarget,
+ oldConsumer.forceToLauncherConsumer(), v)));
+ mEventQueue.deferInit();
+ } else {
+ mEventQueue = new MotionEventQueue(
+ mMainThreadChoreographer, getCurrentTouchConsumer(downHitTarget, false, null));
}
- mCurrentConsumer.preProcessMotionEvent(ev);
- mEventQueue.queue(ev);
}
- private TouchConsumer getOtherActivityConsumer() {
- TouchConsumer consumer = new OtherActivityTouchConsumer(this, mRunningTask, mRecentsModel,
- mHomeIntent, mISystemUiProxy, mMainThreadExecutor) {
+ private TouchConsumer getCurrentTouchConsumer(
+ @HitTarget int downHitTarget, boolean forceToLauncher, VelocityTracker tracker) {
+ RunningTaskInfo runningTaskInfo = mAM.getRunningTask();
- @Override
- public void switchToMainConsumer() {
- if (mCurrentConsumer == this) {
- mEventQueue.setInterimChoreographer(null);
- }
+ if (runningTaskInfo == null && !forceToLauncher) {
+ return mNoOpTouchConsumer;
+ } else if (forceToLauncher ||
+ runningTaskInfo.topActivity.equals(mOverviewCommandHelper.launcher)) {
+ return getLauncherConsumer();
+ } else {
+ if (tracker == null) {
+ tracker = VelocityTracker.obtain();
}
-
- @Override
- public void onTouchTrackingComplete() {
- if (mCurrentConsumer == this) {
- mCurrentConsumer = mNoOpTouchConsumer;
- mEventQueue.setConsumer(mCurrentConsumer);
- }
- }
- };
- return consumer;
+ return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
+ mOverviewCommandHelper.homeIntent,
+ mOverviewCommandHelper.getActivityControlHelper(), mMainThreadExecutor,
+ mBackgroundThreadChoreographer, downHitTarget, tracker);
+ }
}
private TouchConsumer getLauncherConsumer() {
-
Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
if (launcher == null) {
return mNoOpTouchConsumer;
}
-
View target = launcher.getDragLayer();
- if (!target.getWindowId().isFocused()) {
- return mNoOpTouchConsumer;
- }
- return new LauncherTouchConsumer(target);
+ return new LauncherTouchConsumer(launcher, target);
}
- private class LauncherTouchConsumer implements TouchConsumer {
+ private static class LauncherTouchConsumer implements TouchConsumer {
+ private final Launcher mLauncher;
private final View mTarget;
private final int[] mLocationOnScreen = new int[2];
private final PointF mDownPos = new PointF();
private final int mTouchSlop;
+ private final QuickScrubController mQuickScrubController;
private boolean mTrackingStarted = false;
+ private boolean mInvalidated = false;
+ private boolean mHadWindowFocusOnDown;
- LauncherTouchConsumer(View target) {
+ LauncherTouchConsumer(Launcher launcher, View target) {
+ mLauncher = launcher;
mTarget = target;
mTouchSlop = ViewConfiguration.get(mTarget.getContext()).getScaledTouchSlop();
+
+ mQuickScrubController = mLauncher.<RecentsView>getOverviewPanel()
+ .getQuickScrubController();
}
@Override
public void accept(MotionEvent ev) {
+ if (mInvalidated) {
+ return;
+ }
int action = ev.getActionMasked();
if (action == ACTION_DOWN) {
mTrackingStarted = false;
mDownPos.set(ev.getX(), ev.getY());
- } else if (!mTrackingStarted) {
+ mHadWindowFocusOnDown = mTarget.hasWindowFocus();
+ } else if (!mTrackingStarted && mHadWindowFocusOnDown) {
switch (action) {
case ACTION_POINTER_UP:
case ACTION_POINTER_DOWN:
if (!mTrackingStarted) {
- mEventQueue.setConsumer(mNoOpTouchConsumer);
+ mInvalidated = true;
}
break;
case ACTION_MOVE: {
@@ -267,7 +308,7 @@
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
- mEventQueue.setConsumer(mNoOpTouchConsumer);
+ mInvalidated = true;
}
}
@@ -279,6 +320,42 @@
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
}
+
+ @Override
+ public void updateTouchTracking(int interactionType) {
+ if (mInvalidated) {
+ return;
+ }
+ if (interactionType == INTERACTION_QUICK_SCRUB) {
+ Runnable action = () -> {
+ LauncherState fromState = mLauncher.getStateManager().getState();
+ mLauncher.getStateManager().goToState(FAST_OVERVIEW, true);
+ mQuickScrubController.onQuickScrubStart(fromState == NORMAL);
+ };
+
+ if (mLauncher.getWorkspace().runOnOverlayHidden(action)) {
+ // Hide the minus one overlay so launcher can get window focus.
+ mLauncher.onQuickstepGestureStarted(true);
+ }
+ }
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ if (mInvalidated) {
+ return;
+ }
+ mQuickScrubController.onQuickScrubEnd();
+ }
+
+ @Override
+ public void onQuickScrubProgress(float progress) {
+ if (mInvalidated) {
+ return;
+ }
+ mQuickScrubController.onQuickScrubProgress(progress);
+ }
+
}
private void initBackgroundChoreographer() {
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index fbb6d74..f6cf85a 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -15,82 +15,145 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
-import static com.android.quickstep.TouchConsumer.isInteractionQuick;
+import static com.android.systemui.shared.recents.utilities.Utilities
+ .postAtFrontOfQueueAsynchronously;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.RectEvaluator;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
+import android.os.Handler;
import android.os.Looper;
+import android.os.SystemClock;
import android.support.annotation.UiThread;
import android.support.annotation.WorkerThread;
+import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver.OnDrawListener;
+import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Hotseat;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.logging.UserEventDispatcher;
+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.TraceHelper;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.ActivityControlHelper.LayoutListener;
import com.android.quickstep.TouchConsumer.InteractionType;
+import com.android.quickstep.util.SysuiEventLogger;
+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.recents.utilities.RectFEvaluator;
+import com.android.systemui.shared.system.InputConsumerController;
+import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.util.StringJoiner;
+
@TargetApi(Build.VERSION_CODES.O)
-public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
+public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> {
+ private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
+ private static final boolean DEBUG_STATES = false;
// Launcher UI related states
- private static final int STATE_LAUNCHER_READY = 1 << 0;
- private static final int STATE_LAUNCHER_DRAWN = 1 << 1;
- private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 2;
+ private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
+ private static final int STATE_LAUNCHER_STARTED = 1 << 1;
+ private static final int STATE_LAUNCHER_DRAWN = 1 << 2;
+ private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 3;
// Internal initialization states
- private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 3;
+ private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
// Interaction finish states
- private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 4;
- private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 5;
+ private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
+ private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
+
+ private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
+ private static final int STATE_GESTURE_STARTED = 1 << 8;
+ private static final int STATE_GESTURE_CANCELLED = 1 << 9;
+
+ // States for quick switch/scrub
+ private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 10;
+ private static final int STATE_QUICK_SCRUB_START = 1 << 11;
+ private static final int STATE_QUICK_SCRUB_END = 1 << 12;
private static final int LAUNCHER_UI_STATES =
- STATE_LAUNCHER_READY | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE;
+ STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
+ | STATE_LAUNCHER_STARTED;
+
+ // For debugging, keep in sync with above states
+ private static final String[] STATES = new String[] {
+ "STATE_LAUNCHER_PRESENT",
+ "STATE_LAUNCHER_STARTED",
+ "STATE_LAUNCHER_DRAWN",
+ "STATE_ACTIVITY_MULTIPLIER_COMPLETE",
+ "STATE_APP_CONTROLLER_RECEIVED",
+ "STATE_SCALED_CONTROLLER_RECENTS",
+ "STATE_SCALED_CONTROLLER_APP",
+ "STATE_HANDLER_INVALIDATED",
+ "STATE_GESTURE_STARTED",
+ "STATE_GESTURE_CANCELLED",
+ "STATE_SWITCH_TO_SCREENSHOT_COMPLETE",
+ "STATE_QUICK_SWITCH",
+ "STATE_QUICK_SCRUB_START",
+ "STATE_QUICK_SCRUB_END"
+ };
private static final long MAX_SWIPE_DURATION = 200;
private static final long MIN_SWIPE_DURATION = 80;
- private static final int QUICK_SWITCH_SNAP_DURATION = 120;
private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
- private final Rect mStableInsets = new Rect();
- private final Rect mSourceRect = new Rect();
- private final Rect mTargetRect = new Rect();
- private final Rect mCurrentRect = new Rect();
+ // The bounds of the source app in device coordinates
+ private final Rect mSourceStackBounds = new Rect();
+ // The insets of the source app
+ private final Rect mSourceInsets = new Rect();
+ // The source app bounds with the source insets applied, in the source app window coordinates
+ private final RectF mSourceRect = new RectF();
+ // The bounds of the task view in launcher window coordinates
+ private final RectF mTargetRect = new RectF();
+ // Doesn't change after initialized, used as an anchor when changing mTargetRect
+ private final RectF mInitialTargetRect = new RectF();
+ // The insets to be used for clipping the app window, which can be larger than mSourceInsets
+ // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
+ // app window coordinates.
+ private final RectF mSourceWindowClipInsets = new RectF();
+
+ // The bounds of launcher (not including insets) in device coordinates
+ private final Rect mHomeStackBounds = new Rect();
+ // The clip rect in source app window coordinates
private final Rect mClipRect = new Rect();
- private final RectEvaluator mRectEvaluator = new RectEvaluator(mCurrentRect);
+ private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
+ protected Runnable mGestureEndCallback;
+ protected boolean mIsGoingToHome;
private DeviceProfile mDp;
private int mTransitionDragLength;
@@ -104,12 +167,14 @@
private final Context mContext;
private final int mRunningTaskId;
+ private final ActivityControlHelper<T> mActivityControlHelper;
+ private final ActivityInitListener mActivityInitListener;
private MultiStateCallback mStateCallback;
private AnimatorPlaybackController mLauncherTransitionController;
- private Launcher mLauncher;
- private LauncherLayoutListener mLauncherLayoutListener;
+ private T mActivity;
+ private LayoutListener mLayoutListener;
private RecentsView mRecentsView;
private QuickScrubController mQuickScrubController;
@@ -119,62 +184,131 @@
private float mCurrentDisplacement;
private boolean mGestureStarted;
+ private int mLogAction = Touch.SWIPE;
private @InteractionType int mInteractionType = INTERACTION_NORMAL;
- private boolean mStartedQuickScrubFromHome;
+
+ private InputConsumerController mInputConsumer =
+ InputConsumerController.getRecentsAnimationInputConsumer();
private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
private Matrix mTmpMatrix = new Matrix();
+ private final long mTouchTimeMs;
+ private long mLauncherFrameDrawnTime;
- WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context) {
+ // Only used with the recents activity, when the screenshot should be fetched at the beginning
+ // of the animation and not at the end when the activity is already paused
+ private boolean mSkipScreenshotAtEndOfTransition;
+
+ WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs,
+ ActivityControlHelper<T> controller) {
mContext = context;
mRunningTaskId = runningTaskInfo.id;
+ mTouchTimeMs = touchTimeMs;
+ mActivityControlHelper = controller;
+ mActivityInitListener = mActivityControlHelper
+ .createActivityInitListener(this::onActivityInit);
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-
- DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
- // TODO: If in multi window mode, dp = dp.getMultiWindowProfile()
- dp = dp.copy(mContext);
- // TODO: Use different insets for multi-window mode
- dp.updateInsets(mStableInsets);
-
- initTransitionEndpoints(dp);
+ // Register the input consumer on the UI thread, to ensure that it runs after any pending
+ // unregister calls
+ mMainExecutor.execute(mInputConsumer::registerInputConsumer);
initStateCallbacks();
}
private void initStateCallbacks() {
- mStateCallback = new MultiStateCallback();
- mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_APP | STATE_APP_CONTROLLER_RECEIVED,
- this::resumeLastTask);
- mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_RECENTS
- | STATE_ACTIVITY_MULTIPLIER_COMPLETE
- | STATE_APP_CONTROLLER_RECEIVED,
- this::switchToScreenshot);
- mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_RECENTS
- | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
- this::animateFirstTaskIcon);
+ mStateCallback = new MultiStateCallback() {
+ @Override
+ public void setState(int stateFlag) {
+ debugNewState(stateFlag);
+ super.setState(stateFlag);
+ }
+ };
- mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_APP,
- this::reset);
- mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_RECENTS,
- this::reset);
- mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_LAUNCHER_DRAWN,
+ mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
+ this::initializeLauncherAnimationController);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
this::launcherFrameDrawn);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
+ this::notifyGestureStarted);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
+ | STATE_GESTURE_CANCELLED,
+ this::resetStateForAnimationCancel);
+
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_SCALED_CONTROLLER_APP,
+ this::resumeLastTask);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_ACTIVITY_MULTIPLIER_COMPLETE
+ | STATE_SCALED_CONTROLLER_RECENTS,
+ this::switchToScreenshot);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_ACTIVITY_MULTIPLIER_COMPLETE
+ | STATE_SCALED_CONTROLLER_RECENTS
+ | STATE_SWITCH_TO_SCREENSHOT_COMPLETE,
+ this::setupLauncherUiAfterSwipeUpAnimation);
+
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
+ this::reset);
+
+ mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ this::invalidateHandlerWithLauncher);
+
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_SCRUB_START,
+ this::onQuickScrubStart);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_SCRUB_START
+ | STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SWITCH_TO_SCREENSHOT_COMPLETE
+ | STATE_QUICK_SCRUB_END, this::switchToFinalAppAfterQuickScrub);
}
private void setStateOnUiThread(int stateFlag) {
- mMainExecutor.execute(() -> mStateCallback.setState(stateFlag));
+ Handler handler = mMainExecutor.getHandler();
+ if (Looper.myLooper() == handler.getLooper()) {
+ mStateCallback.setState(stateFlag);
+ } else {
+ postAtFrontOfQueueAsynchronously(handler, () -> mStateCallback.setState(stateFlag));
+ }
}
private void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
- RecentsView.getPageRect(dp, mContext, mTargetRect);
- mSourceRect.set(0, 0, dp.widthPx - mStableInsets.left - mStableInsets.right,
- dp.heightPx - mStableInsets.top - mStableInsets.bottom);
+ mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
+ mSourceStackBounds.width() - mSourceInsets.right,
+ mSourceStackBounds.height() - mSourceInsets.bottom);
- mTransitionDragLength = dp.hotseatBarSizePx + (dp.isVerticalBarLayout()
- ? (dp.hotseatBarSidePaddingPx + (dp.isSeascape() ? mStableInsets.left : mStableInsets.right))
- : mStableInsets.bottom);
+ Rect tempRect = new Rect();
+ mTransitionDragLength = mActivityControlHelper
+ .getSwipeUpDestinationAndLength(dp, mContext, tempRect);
+
+ mTargetRect.set(tempRect);
+ mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
+ mHomeStackBounds.top - mSourceStackBounds.top);
+ mInitialTargetRect.set(mTargetRect);
+
+ // Calculate the clip based on the target rect (since the content insets and the
+ // launcher insets may differ, so the aspect ratio of the target rect can differ
+ // from the source rect. The difference between the target rect (scaled to the
+ // source rect) is the amount to clip on each edge.
+ RectF scaledTargetRect = new RectF(mTargetRect);
+ Utilities.scaleRectFAboutCenter(scaledTargetRect,
+ mSourceRect.width() / mTargetRect.width());
+ scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
+ mSourceWindowClipInsets.set(
+ Math.max(scaledTargetRect.left, 0),
+ Math.max(scaledTargetRect.top, 0),
+ Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
+ Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
+ mSourceRect.set(scaledTargetRect);
+ }
+
+ public int getTransitionLength() {
+ return mTransitionDragLength;
+ }
+
+ public RectF getTargetRect(Point outWindowSize) {
+ outWindowSize.set(mDp.widthPx, mDp.heightPx);
+ return mInitialTargetRect;
}
private long getFadeInDuration() {
@@ -189,44 +323,63 @@
}
}
- @Override
- protected boolean init(final Launcher launcher, boolean alreadyOnHome) {
- if (launcher == mLauncher) {
+ public void initWhenReady() {
+ mActivityInitListener.register();
+ }
+
+ private boolean onActivityInit(final T activity, Boolean alreadyOnHome) {
+ if (mActivity == activity) {
return true;
}
- if (mLauncher != null) {
+ if (mActivity != null) {
// The launcher may have been recreated as a result of device rotation.
int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
initStateCallbacks();
mStateCallback.setState(oldState);
- mLauncherLayoutListener.setHandler(null);
+ mLayoutListener.setHandler(null);
}
- mLauncher = launcher;
-
- AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
mWasLauncherAlreadyVisible = alreadyOnHome;
+ mActivity = activity;
+ // Override the visibility of the activity until the gesture actually starts and we swipe
+ // up, or until we transition home and the home animation is composed
+ mActivity.setForceInvisible(true);
- mRecentsView = mLauncher.getOverviewPanel();
- mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
+ mRecentsView = activity.getOverviewPanel();
+ mQuickScrubController = mRecentsView.getQuickScrubController();
+ mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);
- final int state;
+ mStateCallback.setState(STATE_LAUNCHER_PRESENT);
+ if (alreadyOnHome) {
+ onLauncherStart(activity);
+ } else {
+ activity.setOnStartCallback(this::onLauncherStart);
+ }
+ return true;
+ }
+
+ private void onLauncherStart(final T activity) {
+ if (mActivity != activity) {
+ return;
+ }
+ if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) {
+ return;
+ }
+
+ mStateCallback.setState(STATE_LAUNCHER_STARTED);
+ mActivityControlHelper.prepareRecentsUI(mActivity, mWasLauncherAlreadyVisible);
+ AbstractFloatingView.closeAllOpenViews(activity, mWasLauncherAlreadyVisible);
+
if (mWasLauncherAlreadyVisible) {
- DeviceProfile dp = mLauncher.getDeviceProfile();
- long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
- mLauncherTransitionController = launcher.getStateManager()
- .createAnimationToNewWorkspace(OVERVIEW, accuracy);
+ mLauncherTransitionController = mActivityControlHelper
+ .createControllerForVisibleActivity(activity);
mLauncherTransitionController.dispatchOnStart();
mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
- state = STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN
- | STATE_LAUNCHER_READY;
+ mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
} else {
TraceHelper.beginSection("WTS-init");
- launcher.getStateManager().goToState(OVERVIEW, false);
- TraceHelper.partitionSection("WTS-init", "State changed");
-
// TODO: Implement a better animation for fading in
- View rootView = launcher.getRootView();
+ View rootView = activity.getRootView();
rootView.setAlpha(0);
rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
@@ -235,28 +388,18 @@
TraceHelper.endSection("WTS-init", "Launcher frame is drawn");
rootView.post(() ->
rootView.getViewTreeObserver().removeOnDrawListener(this));
- if (launcher != mLauncher) {
+ if (activity != mActivity) {
return;
}
mStateCallback.setState(STATE_LAUNCHER_DRAWN);
}
});
- state = STATE_LAUNCHER_READY;
}
mRecentsView.showTask(mRunningTaskId);
- mLauncher.getDragLayer().addView(mLauncherLayoutListener);
-
- // Optimization
- // We are using the internal device profile as launcher may not have got the insets yet.
- if (!mDp.isVerticalBarLayout()) {
- // All-apps search box is visible in vertical bar layout.
- mLauncher.getAppsView().setVisibility(View.GONE);
- }
-
- mStateCallback.setState(state);
- return true;
+ mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
+ mLayoutListener.open();
}
public void setLauncherOnDrawCallback(Runnable callback) {
@@ -264,7 +407,7 @@
}
private void launcherFrameDrawn() {
- View rootView = mLauncher.getRootView();
+ View rootView = mActivity.getRootView();
if (rootView.getAlpha() < 1) {
if (mGestureStarted) {
final MultiStateCallback callback = mStateCallback;
@@ -276,39 +419,39 @@
mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
}
}
- mLauncherLayoutListener.setHandler(this);
- onLauncherLayoutChanged();
-
if (mLauncherDrawnCallback != null) {
mLauncherDrawnCallback.run();
}
+ mLauncherFrameDrawnTime = SystemClock.uptimeMillis();
+ }
+
+ private void initializeLauncherAnimationController() {
+ mLayoutListener.setHandler(this);
+ onLauncherLayoutChanged();
+
+ final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs;
+ SysuiEventLogger.writeDummyRecentsTransition(transitionDelay);
+
+ if (LatencyTrackerCompat.isEnabled(mContext)) {
+ LatencyTrackerCompat.logToggleRecents((int) transitionDelay);
+ }
}
public void updateInteractionType(@InteractionType int interactionType) {
- Preconditions.assertUIThread();
if (mInteractionType != INTERACTION_NORMAL) {
throw new IllegalArgumentException(
"Can't change interaction type from " + mInteractionType);
}
- if (!isInteractionQuick(interactionType)) {
+ if (interactionType != INTERACTION_QUICK_SCRUB) {
throw new IllegalArgumentException(
"Can't change interaction type to " + interactionType);
}
mInteractionType = interactionType;
- if (mLauncher != null) {
- updateUiForQuickScrub();
- }
- }
+ setStateOnUiThread(STATE_QUICK_SCRUB_START);
- private void updateUiForQuickScrub() {
- mStartedQuickScrubFromHome = mWasLauncherAlreadyVisible;
- mQuickScrubController = mRecentsView.getQuickScrubController();
- mQuickScrubController.onQuickScrubStart(mStartedQuickScrubFromHome);
- animateToProgress(1f, MAX_SWIPE_DURATION);
- if (mStartedQuickScrubFromHome) {
- mLauncherLayoutListener.setVisibility(View.INVISIBLE);
- }
+ // Start the window animation without waiting for launcher.
+ animateToProgress(1f, QUICK_SCRUB_START_DURATION);
}
@WorkerThread
@@ -321,77 +464,55 @@
}
/**
- * Called by {@link #mLauncherLayoutListener} when launcher layout changes
+ * Called by {@link #mLayoutListener} when launcher layout changes
*/
public void onLauncherLayoutChanged() {
- Hotseat hotseat = mLauncher.getHotseat();
-
- WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
- initTransitionEndpoints(mLauncher.getDeviceProfile());
+ initTransitionEndpoints(mActivity.getDeviceProfile());
if (!mWasLauncherAlreadyVisible) {
- AnimatorSet anim = new AnimatorSet();
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- mLauncher.getAllAppsController().setProgress(1);
- ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(mLauncher.getAllAppsController(),
- AllAppsTransitionController.ALL_APPS_PROGRESS,
- 1, OVERVIEW.getVerticalProgress(mLauncher));
- shiftAnim.setInterpolator(LINEAR);
- anim.play(shiftAnim);
-
- hotseat.setAlpha(0);
- ObjectAnimator fadeAnim = ObjectAnimator.ofFloat(hotseat, View.ALPHA, 1);
- fadeAnim.setInterpolator(LINEAR);
- anim.play(fadeAnim);
- } else {
- hotseat.setTranslationY(mTransitionDragLength);
- ObjectAnimator hotseatAnim = ObjectAnimator.ofFloat(hotseat, View.TRANSLATION_Y, 0);
- hotseatAnim.setInterpolator(LINEAR);
- anim.play(hotseatAnim);
-
- View scrim = mLauncher.findViewById(R.id.all_apps_scrim);
- scrim.setTranslationY(mTransitionDragLength);
- ObjectAnimator scrimAnim = ObjectAnimator.ofFloat(scrim, View.TRANSLATION_Y, 0);
- scrimAnim.setInterpolator(LINEAR);
- anim.play(scrimAnim);
- }
-
- // TODO: Link this animation to state animation, so that it is cancelled
- // automatically on state change
- anim.setDuration(mTransitionDragLength * 2);
- mLauncherTransitionController =
- AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
+ mLauncherTransitionController = mActivityControlHelper
+ .createControllerForHiddenActivity(mActivity, mTransitionDragLength);
mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
}
}
@WorkerThread
private void updateFinalShift() {
- if (mStartedQuickScrubFromHome) {
- return;
- }
-
float shift = mCurrentShift.value;
synchronized (mRecentsAnimationWrapper) {
if (mRecentsAnimationWrapper.controller != null) {
- mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
- float scale = (float) mCurrentRect.width() / mSourceRect.width();
+ RectF currentRect;
+ synchronized (mTargetRect) {
+ Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
+ ? ACCEL_2 : LINEAR;
+ float interpolated = interpolator.getInterpolation(shift);
+ currentRect = mRectFEvaluator.evaluate(interpolated, mSourceRect, mTargetRect);
+ // Stay lined up with the center of the target, since it moves for quick scrub.
+ currentRect.offset(mTargetRect.centerX() - currentRect.centerX(), 0);
+ }
- mClipRect.left = mSourceRect.left;
- mClipRect.top = (int) (mStableInsets.top * shift);
- mClipRect.bottom = (int) (mDp.heightPx - (mStableInsets.bottom * shift));
- mClipRect.right = mSourceRect.right;
+ mClipRect.left = (int) (mSourceWindowClipInsets.left * shift);
+ mClipRect.top = (int) (mSourceWindowClipInsets.top * shift);
+ mClipRect.right = (int)
+ (mSourceStackBounds.width() - (mSourceWindowClipInsets.right * shift));
+ mClipRect.bottom = (int)
+ (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * shift));
- mTmpMatrix.setScale(scale, scale, 0, 0);
- mTmpMatrix.postTranslate(mCurrentRect.left - mStableInsets.left * scale * shift,
- mCurrentRect.top - mStableInsets.top * scale * shift);
+ mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
+
TransactionCompat transaction = new TransactionCompat();
for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
if (app.mode == MODE_CLOSING) {
+ mTmpMatrix.postTranslate(app.position.x, app.position.y);
transaction.setMatrix(app.leash, mTmpMatrix)
- .setWindowCrop(app.leash, mClipRect)
- .show(app.leash);
+ .setWindowCrop(app.leash, mClipRect);
+
+ if (app.isNotInRecents) {
+ transaction.setAlpha(app.leash, 1 - shift);
+ }
+
+ transaction.show(app.leash);
}
}
transaction.apply();
@@ -399,23 +520,108 @@
}
if (mLauncherTransitionController != null) {
- if (Looper.getMainLooper() == Looper.myLooper()) {
+ Runnable runOnUi = () -> {
+ if (mLauncherTransitionController == null) {
+ return;
+ }
mLauncherTransitionController.setPlayFraction(shift);
+
+ // Make sure the window follows the first task if it moves, e.g. during quick scrub.
+ View firstTask = mRecentsView.getPageAt(0);
+ // The first task may be null if we are swiping up from a task that does not
+ // appear in the list (ie. the assistant)
+ if (firstTask != null) {
+ int scrollForFirstTask = mRecentsView.getScrollForPage(0);
+ int offsetFromFirstTask = (scrollForFirstTask - mRecentsView.getScrollX());
+ synchronized (mTargetRect) {
+ mTargetRect.set(mInitialTargetRect);
+ Utilities.scaleRectFAboutCenter(mTargetRect, firstTask.getScaleX());
+ float offsetX = offsetFromFirstTask + firstTask.getTranslationX();
+ float offsetY = mRecentsView.getTranslationY();
+ mTargetRect.offset(offsetX, offsetY);
+ }
+ }
+ if (mRecentsAnimationWrapper.controller != null) {
+ // TODO: This logic is spartanic!
+ boolean passedThreshold = shift > 0.12f;
+ mRecentsAnimationWrapper.setAnimationTargetsBehindSystemBars(!passedThreshold);
+ mRecentsAnimationWrapper.setSplitScreenMinimizedForTransaction(passedThreshold);
+ }
+ };
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ runOnUi.run();
} else {
// The fling operation completed even before the launcher was drawn
- mMainExecutor.execute(() -> mLauncherTransitionController.setPlayFraction(shift));
+ mMainExecutor.execute(runOnUi);
}
}
}
- public void setRecentsAnimation(RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps) {
+ public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) {
+ if (apps != null) {
+ // Use the top closing app to determine the insets for the animation
+ for (RemoteAnimationTargetCompat target : apps) {
+ if (target.mode == MODE_CLOSING) {
+ DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+ if (minimizedHomeBounds != null) {
+ mHomeStackBounds.set(minimizedHomeBounds);
+ dp = dp.getMultiWindowProfile(mContext,
+ new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
+ dp.updateInsets(homeContentInsets);
+ } else {
+ mHomeStackBounds.set(new Rect(0, 0, dp.widthPx, dp.heightPx));
+ // TODO: Workaround for an existing issue where the home content insets are
+ // not valid immediately after rotation, just use the stable insets for now
+ Rect insets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(insets);
+ dp.updateInsets(insets);
+ }
+
+ // Initialize the start and end animation bounds
+ // TODO: Remove once platform is updated
+ try {
+ mSourceInsets.set(target.getContentInsets());
+ } catch (Error e) {
+ // TODO: Remove once platform is updated, use stable insets as fallback
+ WindowManagerWrapper.getInstance().getStableInsets(mSourceInsets);
+ }
+ mSourceStackBounds.set(target.sourceContainerBounds);
+
+ initTransitionEndpoints(dp);
+ }
+ }
+ }
mRecentsAnimationWrapper.setController(controller, apps);
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
+ public void onRecentsAnimationCanceled() {
+ mRecentsAnimationWrapper.setController(null, null);
+ mActivityInitListener.unregister();
+ setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+ }
+
public void onGestureStarted() {
+ notifyGestureStarted();
+ setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
+ mRecentsAnimationWrapper.enableInputConsumer();
+ }
+
+ /**
+ * Notifies the launcher that the swipe gesture has started. This can be called multiple times
+ * on both background and UI threads
+ */
+ private void notifyGestureStarted() {
+ final T curActivity = mActivity;
+ if (curActivity != null) {
+ // Once the gesture starts, we can no longer transition home through the button, so
+ // reset the force override of the activity visibility
+ mActivity.setForceInvisible(false);
+ mActivityControlHelper.onQuickstepGestureStarted(
+ curActivity, mWasLauncherAlreadyVisible);
+ }
}
@WorkerThread
@@ -428,6 +634,7 @@
final float endShift;
if (!isFling) {
endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
+ mLogAction = Touch.SWIPE;
} else {
endShift = endVelocity < 0 ? 1 : 0;
float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity);
@@ -439,20 +646,38 @@
// derivative of the scroll interpolator at zero, ie. 5.
duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
}
+ mLogAction = Touch.FLING;
}
animateToProgress(endShift, duration);
}
+ private void doLogGesture(boolean toLauncher) {
+ final int direction;
+ if (mDp.isVerticalBarLayout()) {
+ direction = (mDp.isSeascape() ^ toLauncher) ? Direction.LEFT : Direction.RIGHT;
+ } else {
+ direction = toLauncher ? Direction.UP : Direction.DOWN;
+ }
+
+ int dstContainerType = toLauncher ? ContainerType.TASKSWITCHER : ContainerType.APP;
+ UserEventDispatcher.newInstance(mContext, mDp).logStateChangeAction(
+ mLogAction, direction,
+ ContainerType.NAVBAR, ContainerType.APP,
+ dstContainerType,
+ 0);
+ }
+
/** Animates to the given progress, where 0 is the current app and 1 is overview. */
private void animateToProgress(float progress, long duration) {
+ mIsGoingToHome = Float.compare(progress, 1) == 0;
ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
anim.setInterpolator(Interpolators.SCROLL);
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
- setStateOnUiThread((Float.compare(mCurrentShift.value, 0) == 0)
- ? STATE_SCALED_SNAPSHOT_APP : STATE_SCALED_SNAPSHOT_RECENTS);
+ setStateOnUiThread(mIsGoingToHome ?
+ STATE_SCALED_CONTROLLER_RECENTS : STATE_SCALED_CONTROLLER_APP);
}
});
anim.start();
@@ -460,93 +685,158 @@
@UiThread
private void resumeLastTask() {
- mRecentsAnimationWrapper.finish(false /* toHome */);
+ mRecentsAnimationWrapper.finish(false /* toHome */, null);
+ doLogGesture(false /* toLauncher */);
}
public void reset() {
- mCurrentShift.cancelAnimation();
+ if (mInteractionType != INTERACTION_QUICK_SCRUB) {
+ // Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
+ // invalidated after the quick scrub ends
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ }
+ }
+
+ private void invalidateHandler() {
+ mCurrentShift.finishAnimation();
if (mGestureEndCallback != null) {
mGestureEndCallback.run();
}
- if (mLauncher != null) {
- // TODO: These should be done as part of ActivityOptions#OnAnimationStarted
- mLauncher.getStateManager().reapplyState();
- mLauncher.setOnResumeCallback(() -> mLauncherLayoutListener.close(false));
+ mActivityInitListener.unregister();
+ mInputConsumer.unregisterInputConsumer();
+ }
- if (mLauncherTransitionController != null) {
- mLauncherTransitionController.setPlayFraction(1);
- }
- }
- clearReference();
+ private void invalidateHandlerWithLauncher() {
+ mLauncherTransitionController = null;
+ mLayoutListener.finish();
+
+ mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
+ }
+
+ private void resetStateForAnimationCancel() {
+ boolean wasVisible = mWasLauncherAlreadyVisible || mGestureStarted;
+ mActivityControlHelper.onTransitionCancelled(mActivity, wasVisible);
}
public void layoutListenerClosed() {
- if (mWasLauncherAlreadyVisible) {
+ if (mWasLauncherAlreadyVisible && mLauncherTransitionController != null) {
mLauncherTransitionController.setPlayFraction(1);
}
}
private void switchToScreenshot() {
- mLauncherLayoutListener.close(false);
- View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
- if (currentRecentsPage instanceof TaskView) {
- ((TaskView) currentRecentsPage).animateIconToScale(1f);
- }
- if (mInteractionType == INTERACTION_QUICK_SWITCH) {
- for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) {
- TaskView taskView = (TaskView) mRecentsView.getPageAt(i);
- if (taskView.getTask().key.id != mRunningTaskId) {
- mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION);
- taskView.postDelayed(() -> {taskView.launchTask(true);},
- QUICK_SWITCH_SNAP_DURATION);
- break;
- }
- }
- } else if (mInteractionType == INTERACTION_QUICK_SCRUB) {
- if (mQuickScrubController != null) {
- mQuickScrubController.snapToPageForCurrentQuickScrubSection();
- }
- } else {
+ boolean finishTransitionPosted = false;
+ final Runnable finishTransitionRunnable = () -> {
synchronized (mRecentsAnimationWrapper) {
- if (mRecentsAnimationWrapper.controller != null) {
- TransactionCompat transaction = new TransactionCompat();
- for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
- if (app.mode == MODE_CLOSING) {
- // Update the screenshot of the task
- final ThumbnailData thumbnail =
- mRecentsAnimationWrapper.controller.screenshotTask(app.taskId);
- mRecentsView.updateThumbnail(app.taskId, thumbnail);
+ mRecentsAnimationWrapper.finish(true /* toHome */,
+ () -> setStateOnUiThread(STATE_SWITCH_TO_SCREENSHOT_COMPLETE));
+ }
+ };
+
+ synchronized (mRecentsAnimationWrapper) {
+ if (mRecentsAnimationWrapper.controller != null && !mSkipScreenshotAtEndOfTransition) {
+ TransactionCompat transaction = new TransactionCompat();
+ for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
+ if (app.mode == MODE_CLOSING) {
+ // Update the screenshot of the task
+ ThumbnailData thumbnail =
+ mRecentsAnimationWrapper.controller.screenshotTask(app.taskId);
+ TaskView taskView = mRecentsView.updateThumbnail(app.taskId, thumbnail);
+ if (taskView != null) {
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ mActivityControlHelper.executeOnNextDraw(mActivity, taskView,
+ finishTransitionRunnable);
+ finishTransitionPosted = true;
}
}
- transaction.apply();
}
+ transaction.apply();
}
- mRecentsAnimationWrapper.finish(true /* toHome */);
}
+ if (!finishTransitionPosted) {
+ // If we haven't posted the transition end runnable, run it now
+ finishTransitionRunnable.run();
+ }
+ doLogGesture(true /* toLauncher */);
}
- private void animateFirstTaskIcon() {
- View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
- if (currentRecentsPage instanceof TaskView) {
- ((TaskView) currentRecentsPage).animateIconToScale(1f);
- }
+ @UiThread
+ public void switchToScreenshotImmediate(ThumbnailData thumbnail) {
+ mRecentsView.updateThumbnail(mRunningTaskId, thumbnail);
+ mSkipScreenshotAtEndOfTransition = true;
}
- public void onQuickScrubEnd() {
- if (mQuickScrubController != null) {
- mQuickScrubController.onQuickScrubEnd();
- } else {
- // TODO:
+ private void setupLauncherUiAfterSwipeUpAnimation() {
+ if (mLauncherTransitionController != null) {
+ mLauncherTransitionController.getAnimationPlayer().end();
+ mLauncherTransitionController = null;
}
+ mActivityControlHelper.onSwipeUpComplete(mActivity);
+
+ // Animate the first icon.
+ mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
+
+ mRecentsView.setSwipeDownShouldLaunchApp(true);
+
+ reset();
+ }
+
+ private void onQuickScrubStart() {
+ mActivityControlHelper.onQuickInteractionStart(mActivity, mWasLauncherAlreadyVisible);
+ mQuickScrubController.onQuickScrubStart(false);
+ }
+
+ private void onFinishedTransitionToQuickScrub() {
+ mQuickScrubController.onFinishedTransitionToQuickScrub();
}
public void onQuickScrubProgress(float progress) {
- if (mQuickScrubController != null) {
- mQuickScrubController.onQuickScrubProgress(progress);
- } else {
- // TODO:
+ if (Looper.myLooper() != Looper.getMainLooper() || mQuickScrubController == null) {
+ // TODO: We can still get progress events while launcher is not ready on the worker
+ // thread. Keep track of last received progress and apply that progress when launcher
+ // is ready
+ return;
}
+ mQuickScrubController.onQuickScrubProgress(progress);
+ }
+
+ public void onQuickScrubEnd() {
+ setStateOnUiThread(STATE_QUICK_SCRUB_END);
+ }
+
+ private void switchToFinalAppAfterQuickScrub() {
+ mQuickScrubController.onQuickScrubEnd();
+
+ // Normally this is handled in reset(), but since we are still scrubbing after the
+ // transition into recents, we need to defer the handler invalidation for quick scrub until
+ // after the gesture ends
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ }
+
+ private void debugNewState(int stateFlag) {
+ if (!DEBUG_STATES) {
+ return;
+ }
+
+ int state = mStateCallback.getState();
+ StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
+ String stateFlagStr = "Unknown-" + stateFlag;
+ for (int i = 0; i < STATES.length; i++) {
+ if ((state & (i << i)) != 0) {
+ currentStateStr.add(STATES[i]);
+ }
+ if (stateFlag == (1 << i)) {
+ stateFlagStr = STATES[i] + " (" + stateFlag + ")";
+ }
+ }
+ Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
+ + currentStateStr);
+ }
+
+ public void setGestureEndCallback(Runnable gestureEndCallback) {
+ mGestureEndCallback = gestureEndCallback;
}
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
new file mode 100644
index 0000000..7d7f88f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -0,0 +1,85 @@
+/*
+ * 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.fallback;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.quickstep.RecentsActivity;
+import com.android.quickstep.views.RecentsView;
+
+public class FallbackRecentsView extends RecentsView<RecentsActivity> implements Insettable {
+
+ public FallbackRecentsView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setOverviewStateEnabled(true);
+ updateEmptyMessage();
+ }
+
+ @Override
+ protected void onAllTasksRemoved() {
+ mActivity.finish();
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ updateEmptyMessage();
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+ updateEmptyMessage();
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ Rect padding = getPadding(dp, getContext());
+ verticalCenter(padding, dp);
+ setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ }
+
+ private static void verticalCenter(Rect padding, DeviceProfile dp) {
+ Rect insets = dp.getInsets();
+ int totalSpace = (padding.top + padding.bottom - insets.top - insets.bottom) / 2;
+ padding.top = insets.top + totalSpace;
+ padding.bottom = insets.bottom + totalSpace;
+ }
+
+ public static void getCenterPageRect(DeviceProfile grid, Context context, Rect outRect) {
+ Rect targetPadding = getPadding(grid, context);
+ verticalCenter(targetPadding, grid);
+ getPageRect(grid, context, outRect, targetPadding);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ maybeDrawEmptyMessage(canvas);
+ super.draw(canvas);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
new file mode 100644
index 0000000..7aaa88c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -0,0 +1,65 @@
+/*
+ * 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.fallback;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsActivity;
+
+public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
+
+ private final RecentsActivity mActivity;
+
+ public RecentsRootView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mActivity = (RecentsActivity) BaseActivity.fromContext(context);
+ setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
+
+ public void setup() {
+ mControllers = new TouchController[] { new RecentsTaskController(mActivity) };
+ }
+
+ @TargetApi(23)
+ @Override
+ protected boolean fitSystemWindows(Rect insets) {
+ // Update device profile before notifying the children.
+ mActivity.getDeviceProfile().updateInsets(insets);
+ setInsets(insets);
+ return true; // I'll take it from here
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
+ // modifying child layout params.
+ if (!insets.equals(mInsets)) {
+ super.setInsets(insets);
+ }
+ setBackground(insets.top == 0 ? null
+ : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
new file mode 100644
index 0000000..9463cc9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -0,0 +1,31 @@
+/*
+ * 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.fallback;
+
+import com.android.launcher3.uioverrides.TaskViewTouchController;
+import com.android.quickstep.RecentsActivity;
+
+public class RecentsTaskController extends TaskViewTouchController<RecentsActivity> {
+
+ public RecentsTaskController(RecentsActivity activity) {
+ super(activity);
+ }
+
+ @Override
+ protected boolean isRecentsInteractive() {
+ return mActivity.hasWindowFocus();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java b/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java
new file mode 100644
index 0000000..d474ded
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/SysuiEventLogger.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+import android.metrics.LogMaker;
+import android.util.EventLog;
+
+/**
+ * Utility class for writing logs on behalf of systemUI
+ */
+public class SysuiEventLogger {
+
+ /** 524292 sysui_multi_action (content|4) */
+ public static final int SYSUI_MULTI_ACTION = 524292;
+
+ private static void write(LogMaker content) {
+ if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) {
+ content.setType(4/*MetricsEvent.TYPE_ACTION*/);
+ }
+ EventLog.writeEvent(SYSUI_MULTI_ACTION, content.serialize());
+ }
+
+ public static void writeDummyRecentsTransition(long transitionDelay) {
+ // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for
+ // "Recents" activity for app transition tests for the app-to-recents case.
+ final LogMaker builder = new LogMaker(761/*APP_TRANSITION*/);
+ builder.setPackageName("com.android.systemui");
+ builder.addTaggedData(871/*FIELD_CLASS_NAME*/,
+ "com.android.systemui.recents.RecentsActivity");
+ builder.addTaggedData(319/*APP_TRANSITION_DELAY_MS*/,
+ transitionDelay);
+ write(builder);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
similarity index 70%
rename from quickstep/src/com/android/quickstep/LauncherLayoutListener.java
rename to quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
index 40cd3e6..6b7143d 100644
--- a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep;
+package com.android.quickstep.views;
+
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.graphics.Rect;
import android.view.MotionEvent;
@@ -21,11 +23,14 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
+import com.android.quickstep.ActivityControlHelper.LayoutListener;
+import com.android.quickstep.WindowTransformSwipeHandler;
/**
* Floating view which shows the task snapshot allowing it to be dragged and placed.
*/
-public class LauncherLayoutListener extends AbstractFloatingView implements Insettable {
+public class LauncherLayoutListener extends AbstractFloatingView
+ implements Insettable, LayoutListener {
private final Launcher mLauncher;
private WindowTransformSwipeHandler mHandler;
@@ -36,18 +41,13 @@
setVisibility(INVISIBLE);
}
+ @Override
public void setHandler(WindowTransformSwipeHandler handler) {
mHandler = handler;
}
@Override
public void setInsets(Rect insets) {
- requestLayout();
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
if (mHandler != null) {
mHandler.onLauncherLayoutChanged();
}
@@ -60,11 +60,22 @@
@Override
protected void handleClose(boolean animate) {
- // We dont suupport animate.
- mLauncher.getDragLayer().removeView(this);
+ if (mIsOpen) {
+ mIsOpen = false;
+ // We don't support animate.
+ mLauncher.getDragLayer().removeView(this);
- if (mHandler != null) {
- mHandler.layoutListenerClosed();
+ if (mHandler != null) {
+ mHandler.layoutListenerClosed();
+ }
+ }
+ }
+
+ @Override
+ public void open() {
+ if (!mIsOpen) {
+ mLauncher.getDragLayer().addView(this);
+ mIsOpen = true;
}
}
@@ -82,4 +93,11 @@
protected boolean isOfType(int type) {
return (type & TYPE_QUICKSTEP_PREVIEW) != 0;
}
+
+ @Override
+ public void finish() {
+ setHandler(null);
+ close(false);
+ mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
new file mode 100644
index 0000000..6788827
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -0,0 +1,141 @@
+/*
+ * 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.views;
+
+import static com.android.launcher3.LauncherAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
+import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+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.util.FloatProperty;
+import android.view.View;
+import android.view.ViewDebug;
+
+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;
+
+/**
+ * {@link RecentsView} used in Launcher activity
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class LauncherRecentsView extends RecentsView<Launcher> implements Insettable {
+
+ public static final FloatProperty<LauncherRecentsView> TRANSLATION_Y_FACTOR =
+ new FloatProperty<LauncherRecentsView>("translationYFactor") {
+
+ @Override
+ public void setValue(LauncherRecentsView view, float v) {
+ view.setTranslationYFactor(v);
+ }
+
+ @Override
+ public Float get(LauncherRecentsView view) {
+ return view.mTranslationYFactor;
+ }
+ };
+
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private float mTranslationYFactor;
+
+ private Rect mPagePadding = new Rect();
+
+ public LauncherRecentsView(Context context) {
+ this(context, null);
+ }
+
+ public LauncherRecentsView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setContentAlpha(0);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ DeviceProfile dp = mActivity.getDeviceProfile();
+ Rect padding = getPadding(dp, getContext());
+ setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ mPagePadding.set(padding);
+ mPagePadding.top += getResources().getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ }
+
+ @Override
+ protected void onAllTasksRemoved() {
+ mActivity.getStateManager().goToState(NORMAL);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ setTranslationYFactor(mTranslationYFactor);
+ }
+
+ public void setTranslationYFactor(float translationFactor) {
+ mTranslationYFactor = translationFactor;
+ setTranslationY(mTranslationYFactor * (mPagePadding.bottom - mPagePadding.top));
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ maybeDrawEmptyMessage(canvas);
+ super.draw(canvas);
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ updateEmptyMessage();
+ }
+
+ @Override
+ protected void onTaskStackUpdated() {
+ // Lazily update the empty message only when the task stack is reapplied
+ updateEmptyMessage();
+ }
+
+ /**
+ * Animates adjacent tasks and translate hotseat off screen as well.
+ */
+ @Override
+ public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
+ AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
+
+ float allAppsProgressOffscreen = ALL_APPS_PROGRESS_OFF_SCREEN;
+ LauncherState state = mActivity.getStateManager().getState();
+ if ((state.getVisibleElements(mActivity) & ALL_APPS_HEADER_EXTRA) != 0) {
+ float maxShiftRange = mActivity.getDeviceProfile().heightPx;
+ float currShiftRange = mActivity.getAllAppsController().getShiftRange();
+ allAppsProgressOffscreen = 1f + (maxShiftRange - currShiftRange) / maxShiftRange;
+ }
+ anim.play(ObjectAnimator.ofFloat(
+ mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
+ return anim;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java b/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java
new file mode 100644
index 0000000..5e9cd6e
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/QuickstepDragIndicator.java
@@ -0,0 +1,70 @@
+/*
+ * 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.views;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.views.LauncherDragIndicator;
+
+public class QuickstepDragIndicator extends LauncherDragIndicator {
+
+ public QuickstepDragIndicator(Context context) {
+ super(context);
+ }
+
+ public QuickstepDragIndicator(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public QuickstepDragIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ private boolean isInOverview() {
+ return mLauncher.isInState(OVERVIEW);
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ info.setContentDescription(getContext().getString(R.string.all_apps_button_label));
+ }
+
+ @Override
+ protected void initCustomActions(AccessibilityNodeInfo info) {
+ if (!isInOverview()) {
+ super.initCustomActions(info);
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ mLauncher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.ALL_APPS_BUTTON,
+ isInOverview() ? ContainerType.TASKSWITCHER : ContainerType.WORKSPACE);
+ mLauncher.getStateManager().goToState(ALL_APPS);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
new file mode 100644
index 0000000..d95619c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -0,0 +1,1074 @@
+/*
+ * 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.quickstep.views;
+
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+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 android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+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.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.ArraySet;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.SparseBooleanArray;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.accessibility.AccessibilityEvent;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.Themes;
+import com.android.quickstep.QuickScrubController;
+import com.android.quickstep.RecentsAnimationInterpolator;
+import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.RecentsModel;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.TaskStack;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.ArrayList;
+
+/**
+ * A list of recent tasks.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public abstract class RecentsView<T extends BaseActivity>
+ extends PagedView implements OnSharedPreferenceChangeListener {
+
+ public static final FloatProperty<RecentsView> CONTENT_ALPHA =
+ new FloatProperty<RecentsView>("contentAlpha") {
+ @Override
+ public void setValue(RecentsView recentsView, float v) {
+ recentsView.setContentAlpha(v);
+ }
+
+ @Override
+ public Float get(RecentsView recentsView) {
+ return recentsView.mContentAlpha;
+ }
+ };
+
+
+
+ public static final FloatProperty<RecentsView> ADJACENT_SCALE =
+ new FloatProperty<RecentsView>("adjacentScale") {
+ @Override
+ public void setValue(RecentsView recentsView, float v) {
+ recentsView.setAdjacentScale(v);
+ }
+
+ @Override
+ public Float get(RecentsView recentsView) {
+ return recentsView.mAdjacentScale;
+ }
+ };
+ private static final String PREF_FLIP_RECENTS = "pref_flip_recents";
+ private static final int DISMISS_TASK_DURATION = 300;
+
+ private static final Rect sTempStableInsets = new Rect();
+
+ protected final T mActivity;
+ private final QuickScrubController mQuickScrubController;
+ private final float mFastFlingVelocity;
+ private final RecentsModel mModel;
+
+ private final ScrollState mScrollState = new ScrollState();
+ // Keeps track of the previously known visible tasks for purposes of loading/unloading task data
+ private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
+
+ /**
+ * TODO: Call reloadIdNeeded in onTaskStackChanged.
+ */
+ private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
+ @Override
+ public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
+ for (int i = 0; i < getChildCount(); i++) {
+ final TaskView taskView = (TaskView) getChildAt(i);
+ if (taskView.getTask().key.id == taskId) {
+ taskView.getThumbnail().setThumbnail(taskView.getTask(), snapshot);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
+ // Check this is for the right user
+ if (!checkCurrentUserId(userId, false /* debug */)) {
+ return;
+ }
+
+ // Remove the task immediately from the task list
+ TaskView taskView = getTaskView(taskId);
+ if (taskView != null) {
+ removeView(taskView);
+ }
+ }
+
+ @Override
+ public void onActivityUnpinned() {
+ // TODO: Re-enable layout transitions for addition of the unpinned task
+ reloadIfNeeded();
+ }
+ };
+
+ private int mLoadPlanId = -1;
+
+ // Only valid until the launcher state changes to NORMAL
+ private int mRunningTaskId = -1;
+ private Task mTmpRunningTask;
+
+ private boolean mFirstTaskIconScaledDown = false;
+
+ private boolean mOverviewStateEnabled;
+ private boolean mTaskStackListenerRegistered;
+ private Runnable mNextPageSwitchRunnable;
+ private boolean mSwipeDownShouldLaunchApp;
+
+ private PendingAnimation mPendingAnimation;
+
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private float mContentAlpha = 1;
+ @ViewDebug.ExportedProperty(category = "launcher")
+ private float mAdjacentScale = 1;
+
+ // Keeps track of task views whose visual state should not be reset
+ private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>();
+
+ // Variables for empty state
+ private final Drawable mEmptyIcon;
+ private final CharSequence mEmptyMessage;
+ private final TextPaint mEmptyMessagePaint;
+ private final Point mLastMeasureSize = new Point();
+ private final int mEmptyMessagePadding;
+ private boolean mShowEmptyMessage;
+ private Layout mEmptyTextLayout;
+
+ public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
+ enableFreeScroll(true);
+ setClipToOutline(true);
+
+ mFastFlingVelocity = getResources()
+ .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
+ mActivity = (T) BaseActivity.fromContext(context);
+ mQuickScrubController = new QuickScrubController(mActivity, this);
+ mModel = RecentsModel.getInstance(context);
+
+ onSharedPreferenceChanged(Utilities.getPrefs(context), PREF_FLIP_RECENTS);
+
+ mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
+ mEmptyIcon.setCallback(this);
+ mEmptyMessage = context.getText(R.string.recents_empty_message);
+ mEmptyMessagePaint = new TextPaint();
+ mEmptyMessagePaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+ mEmptyMessagePaint.setTextSize(getResources()
+ .getDimension(R.dimen.recents_empty_message_text_size));
+ mEmptyMessagePadding = getResources()
+ .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ if (s.equals(PREF_FLIP_RECENTS)) {
+ mIsRtl = Utilities.isRtl(getResources());
+ if (sharedPreferences.getBoolean(PREF_FLIP_RECENTS, false)) {
+ mIsRtl = !mIsRtl;
+ }
+ setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
+ }
+ }
+
+ public boolean isRtl() {
+ return mIsRtl;
+ }
+
+ public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
+ for (int i = 0; i < getChildCount(); i++) {
+ final TaskView taskView = (TaskView) getChildAt(i);
+ if (taskView.getTask().key.id == taskId) {
+ taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
+ taskView.setAlpha(1);
+ return taskView;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ updateTaskStackListenerState();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ updateTaskStackListenerState();
+ Utilities.getPrefs(getContext()).registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ updateTaskStackListenerState();
+ Utilities.getPrefs(getContext()).unregisterOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
+
+ // Clear the task data for the removed child if it was visible
+ Task task = ((TaskView) child).getTask();
+ if (mHasVisibleTaskData.get(task.key.id)) {
+ mHasVisibleTaskData.delete(task.key.id);
+ RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+ loader.unloadTaskData(task);
+ loader.getHighResThumbnailLoader().onTaskInvisible(task);
+ }
+ }
+
+ 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;
+ }
+
+ public TaskView getTaskView(int taskId) {
+ for (int i = 0; i < getChildCount(); i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ if (tv.getTask().key.id == taskId) {
+ return tv;
+ }
+ }
+ return null;
+ }
+
+ public void setOverviewStateEnabled(boolean enabled) {
+ mOverviewStateEnabled = enabled;
+ updateTaskStackListenerState();
+ }
+
+ public void setNextPageSwitchRunnable(Runnable r) {
+ mNextPageSwitchRunnable = r;
+ }
+
+ @Override
+ protected void onPageEndTransition() {
+ super.onPageEndTransition();
+ if (mNextPageSwitchRunnable != null) {
+ mNextPageSwitchRunnable.run();
+ mNextPageSwitchRunnable = null;
+ }
+ if (getNextPage() > 0) {
+ setSwipeDownShouldLaunchApp(true);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ super.onTouchEvent(ev);
+ // Do not let touch escape to siblings below this view.
+ return true;
+ }
+
+ private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
+ if (mPendingAnimation != null) {
+ mPendingAnimation.addEndListener((b) -> applyLoadPlan(loadPlan));
+ return;
+ }
+ TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
+ if (stack == null) {
+ removeAllViews();
+ onTaskStackUpdated();
+ return;
+ }
+
+ int oldChildCount = getChildCount();
+
+ // Ensure there are as many views as there are tasks in the stack (adding and trimming as
+ // necessary)
+ final LayoutInflater inflater = LayoutInflater.from(getContext());
+ final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
+
+ final int requiredChildCount = tasks.size();
+ for (int i = getChildCount(); i < requiredChildCount; i++) {
+ final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
+ addView(taskView);
+ }
+ while (getChildCount() > requiredChildCount) {
+ final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
+ removeView(taskView);
+ }
+
+ // Unload existing visible task data
+ unloadVisibleTaskData();
+
+ // Rebind and reset all task views
+ for (int i = requiredChildCount - 1; i >= 0; i--) {
+ final int pageIndex = requiredChildCount - i - 1;
+ final Task task = tasks.get(i);
+ final TaskView taskView = (TaskView) getChildAt(pageIndex);
+ taskView.bind(task);
+ }
+ resetTaskVisuals();
+ applyIconScale(false /* animate */);
+
+ if (oldChildCount != getChildCount()) {
+ mQuickScrubController.snapToNextTaskIfAvailable();
+ }
+ onTaskStackUpdated();
+ }
+
+ protected void onTaskStackUpdated() { }
+
+ public void resetTaskVisuals() {
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ TaskView taskView = (TaskView) getChildAt(i);
+ if (!mIgnoreResetTaskViews.contains(taskView)) {
+ taskView.resetVisualProperties();
+ }
+ }
+
+ updateCurveProperties();
+ // Update the set of visible task's data
+ loadVisibleTaskData();
+ }
+
+ private void updateTaskStackListenerState() {
+ boolean registerStackListener = mOverviewStateEnabled && isAttachedToWindow()
+ && getWindowVisibility() == VISIBLE;
+ if (registerStackListener != mTaskStackListenerRegistered) {
+ if (registerStackListener) {
+ ActivityManagerWrapper.getInstance()
+ .registerTaskStackListener(mTaskStackListener);
+ reloadIfNeeded();
+ } else {
+ ActivityManagerWrapper.getInstance()
+ .unregisterTaskStackListener(mTaskStackListener);
+ }
+ mTaskStackListenerRegistered = registerStackListener;
+ }
+ }
+
+ protected static Rect getPadding(DeviceProfile profile, Context context) {
+ WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets);
+ Rect padding = new Rect(profile.workspacePadding);
+
+ float taskWidth = profile.widthPx - sTempStableInsets.left - sTempStableInsets.right;
+ float taskHeight = profile.heightPx - sTempStableInsets.top - sTempStableInsets.bottom;
+
+ float overviewHeight, overviewWidth;
+ if (profile.isVerticalBarLayout()) {
+ float maxPadding = Math.max(padding.left, padding.right);
+
+ // Use the same padding on both sides for symmetry.
+ float availableWidth = taskWidth - 2 * maxPadding;
+ float availableHeight = profile.availableHeightPx - padding.top - padding.bottom
+ - sTempStableInsets.top;
+ float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+ overviewHeight = taskHeight * scaledRatio;
+ overviewWidth = taskWidth * scaledRatio;
+
+ } else {
+ overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
+ - sTempStableInsets.top;
+ overviewWidth = taskWidth * overviewHeight / taskHeight;
+ }
+
+ padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top
+ - Math.round(overviewHeight);
+ padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
+
+ // If the height ratio is larger than the width ratio, the screenshot will get cropped
+ // at the bottom when swiping up. In this case, increase the top/bottom padding to make it
+ // the same aspect ratio.
+ Rect pageRect = new Rect();
+ getPageRect(profile, context, pageRect, padding);
+ float widthRatio = (float) pageRect.width() / taskWidth;
+ float heightRatio = (float) pageRect.height() / taskHeight;
+ if (heightRatio > widthRatio) {
+ float additionalVerticalPadding = pageRect.height() - widthRatio * taskHeight;
+ additionalVerticalPadding = Math.round(additionalVerticalPadding);
+ padding.top += additionalVerticalPadding / 2;
+ padding.bottom += additionalVerticalPadding / 2;
+ }
+
+ return padding;
+ }
+
+ public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
+ Rect targetPadding = getPadding(grid, context);
+ getPageRect(grid, context, outRect, targetPadding);
+ }
+
+ protected static void getPageRect(DeviceProfile grid, Context context, Rect outRect,
+ Rect targetPadding) {
+ Rect insets = grid.getInsets();
+ outRect.set(
+ targetPadding.left + insets.left,
+ targetPadding.top + insets.top,
+ grid.widthPx - targetPadding.right - insets.right,
+ grid.heightPx - targetPadding.bottom - insets.bottom);
+ outRect.top += context.getResources()
+ .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ }
+
+ @Override
+ protected boolean computeScrollHelper() {
+ boolean scrolling = super.computeScrollHelper();
+ boolean isFlingingFast = false;
+ updateCurveProperties();
+ if (scrolling || (mTouchState == TOUCH_STATE_SCROLLING)) {
+ if (scrolling) {
+ // Check if we are flinging quickly to disable high res thumbnail loading
+ isFlingingFast = mScroller.getCurrVelocity() > mFastFlingVelocity;
+ }
+
+ // After scrolling, update the visible task's data
+ loadVisibleTaskData();
+ }
+
+ // Update the high res thumbnail loader
+ RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+ loader.getHighResThumbnailLoader().setFlingingFast(isFlingingFast);
+ return scrolling;
+ }
+
+ /**
+ * Scales and adjusts translation of adjacent pages as if on a curved carousel.
+ */
+ public void updateCurveProperties() {
+ if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
+ return;
+ }
+ final int halfPageWidth = getNormalChildWidth() / 2;
+ final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth;
+ final int halfScreenWidth = getMeasuredWidth() / 2;
+ final int pageSpacing = mPageSpacing;
+
+ final int pageCount = getPageCount();
+ for (int i = 0; i < pageCount; i++) {
+ View page = getPageAt(i);
+ float pageCenter = page.getLeft() + page.getTranslationX() + halfPageWidth;
+ float distanceFromScreenCenter = screenCenter - pageCenter;
+ float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
+ mScrollState.linearInterpolation = Math.min(1,
+ Math.abs(distanceFromScreenCenter) / distanceToReachEdge);
+ ((PageCallbacks) page).onPageScroll(mScrollState);
+ }
+ }
+
+ /**
+ * Iterates through all thet asks, and loads the associated task data for newly visible tasks,
+ * and unloads the associated task data for tasks that are no longer visible.
+ */
+ public void loadVisibleTaskData() {
+ RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+ int centerPageIndex = getPageNearestToCenterOfScreen();
+ int lower = Math.max(0, centerPageIndex - 2);
+ int upper = Math.min(centerPageIndex + 2, getChildCount() - 1);
+ int numChildren = getChildCount();
+
+ // Update the task data for the in/visible children
+ for (int i = 0; i < numChildren; i++) {
+ TaskView taskView = (TaskView) getChildAt(i);
+ Task task = taskView.getTask();
+ boolean visible = lower <= i && i <= upper;
+ if (visible) {
+ if (task == mTmpRunningTask) {
+ // Skip loading if this is the task that we are animating into
+ continue;
+ }
+ if (!mHasVisibleTaskData.get(task.key.id)) {
+ loader.loadTaskData(task);
+ loader.getHighResThumbnailLoader().onTaskVisible(task);
+ }
+ mHasVisibleTaskData.put(task.key.id, visible);
+ } else {
+ if (mHasVisibleTaskData.get(task.key.id)) {
+ loader.unloadTaskData(task);
+ loader.getHighResThumbnailLoader().onTaskInvisible(task);
+ }
+ mHasVisibleTaskData.delete(task.key.id);
+ }
+ }
+ }
+
+ /**
+ * Unloads any associated data from the currently visible tasks
+ */
+ private void unloadVisibleTaskData() {
+ RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
+ for (int i = 0; i < mHasVisibleTaskData.size(); i++) {
+ if (mHasVisibleTaskData.valueAt(i)) {
+ TaskView taskView = getTaskView(mHasVisibleTaskData.keyAt(i));
+ Task task = taskView.getTask();
+ loader.unloadTaskData(task);
+ loader.getHighResThumbnailLoader().onTaskInvisible(task);
+ }
+ }
+ mHasVisibleTaskData.clear();
+ }
+
+
+ protected abstract void onAllTasksRemoved();
+
+ public void reset() {
+ unloadVisibleTaskData();
+ mRunningTaskId = -1;
+ setCurrentPage(0);
+ }
+
+ /**
+ * Reloads the view if anything in recents changed.
+ */
+ public void reloadIfNeeded() {
+ if (!mModel.isLoadPlanValid(mLoadPlanId)) {
+ mLoadPlanId = mModel.loadTasks(mRunningTaskId, this::applyLoadPlan);
+ }
+ }
+
+ /**
+ * Ensures that the first task in the view represents {@param task} and reloads the view
+ * if needed. This allows the swipe-up gesture to assume that the first tile always
+ * corresponds to the correct task.
+ * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
+ * is called.
+ * Also scrolls the view to this task
+ */
+ public void showTask(int runningTaskId) {
+ if (getChildCount() == 0) {
+ // Add an empty view for now until the task plan is loaded and applied
+ final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
+ .inflate(R.layout.task, this, false);
+ addView(taskView);
+
+ // The temporary running task is only used for the duration between the start of the
+ // gesture and the task list is loaded and applied
+ mTmpRunningTask = new Task(new Task.TaskKey(runningTaskId, 0, new Intent(), 0, 0), null,
+ null, "", "", 0, 0, false, true, false, false,
+ new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false);
+ taskView.bind(mTmpRunningTask);
+ }
+
+ mRunningTaskId = runningTaskId;
+ setCurrentPage(0);
+
+ // Load the tasks (if the loading is already
+ mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
+
+ // Hide the task that we are animating into, ignore if there is no associated task (ie. the
+ // assistant)
+ if (getPageAt(mCurrentPage) != null) {
+ getPageAt(mCurrentPage).setAlpha(0);
+ }
+ }
+
+ public void showNextTask() {
+ TaskView runningTaskView = getTaskView(mRunningTaskId);
+ if (runningTaskView == null) {
+ // Launch the first task
+ if (getChildCount() > 0) {
+ ((TaskView) getChildAt(0)).launchTask(true /* animate */);
+ }
+ } else {
+ // Get the next launch task
+ int runningTaskIndex = indexOfChild(runningTaskView);
+ int nextTaskIndex = Math.max(0, Math.min(getChildCount() - 1, runningTaskIndex + 1));
+ if (nextTaskIndex < getChildCount()) {
+ ((TaskView) getChildAt(nextTaskIndex)).launchTask(true /* animate */);
+ }
+ }
+ }
+
+ public QuickScrubController getQuickScrubController() {
+ return mQuickScrubController;
+ }
+
+ public void setFirstTaskIconScaledDown(boolean isScaledDown, boolean animate) {
+ if (mFirstTaskIconScaledDown == isScaledDown) {
+ return;
+ }
+ mFirstTaskIconScaledDown = isScaledDown;
+ applyIconScale(animate);
+ }
+
+ private void applyIconScale(boolean animate) {
+ float scale = mFirstTaskIconScaledDown ? 0 : 1;
+ TaskView firstTask = (TaskView) getChildAt(0);
+ if (firstTask != null) {
+ if (animate) {
+ firstTask.animateIconToScale(scale);
+ } else {
+ firstTask.setIconScale(scale);
+ }
+ }
+ }
+
+ public void setSwipeDownShouldLaunchApp(boolean swipeDownShouldLaunchApp) {
+ mSwipeDownShouldLaunchApp = swipeDownShouldLaunchApp;
+ }
+
+ public boolean shouldSwipeDownLaunchApp() {
+ return mSwipeDownShouldLaunchApp;
+ }
+
+ public interface PageCallbacks {
+
+ /**
+ * Updates the page UI based on scroll params.
+ */
+ default void onPageScroll(ScrollState scrollState) {};
+ }
+
+ public static class ScrollState {
+
+ /**
+ * The progress from 0 to 1, where 0 is the center
+ * of the screen and 1 is the edge of the screen.
+ */
+ public float linearInterpolation;
+ }
+
+ public void addIgnoreResetTask(TaskView taskView) {
+ mIgnoreResetTaskViews.add(taskView);
+ }
+
+ public void removeIgnoreResetTask(TaskView taskView) {
+ mIgnoreResetTaskViews.remove(taskView);
+ }
+
+ public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+ boolean removeTask, long duration) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ throw new IllegalStateException("Another pending animation is still running");
+ }
+ AnimatorSet anim = new AnimatorSet();
+ PendingAnimation pendingAnimation = new PendingAnimation(anim);
+
+ int count = getChildCount();
+ if (count == 0) {
+ return pendingAnimation;
+ }
+
+ int[] oldScroll = new int[count];
+ getPageScrolls(oldScroll, false, SIMPLE_SCROLL_LOGIC);
+
+ int[] newScroll = new int[count];
+ getPageScrolls(newScroll, false, (v) -> v.getVisibility() != GONE && v != taskView);
+
+ int maxScrollDiff = 0;
+ int lastPage = mIsRtl ? 0 : count - 1;
+ if (getChildAt(lastPage) == taskView) {
+ if (count > 1) {
+ int secondLastPage = mIsRtl ? 1 : count - 2;
+ maxScrollDiff = oldScroll[lastPage] - newScroll[secondLastPage];
+ }
+ }
+
+ boolean needsCurveUpdates = false;
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ if (child == taskView) {
+ if (animateTaskView) {
+ addAnim(ObjectAnimator.ofFloat(taskView, ALPHA, 0), duration, ACCEL_2, anim);
+ addAnim(ObjectAnimator.ofFloat(taskView, TRANSLATION_Y, -taskView.getHeight()),
+ duration, LINEAR, anim);
+ }
+ } else {
+ int scrollDiff = newScroll[i] - oldScroll[i] + maxScrollDiff;
+ if (scrollDiff != 0) {
+ addAnim(ObjectAnimator.ofFloat(child, TRANSLATION_X, scrollDiff),
+ duration, ACCEL, anim);
+ needsCurveUpdates = true;
+ }
+ }
+ }
+
+ if (needsCurveUpdates) {
+ ValueAnimator va = ValueAnimator.ofFloat(0, 1);
+ va.addUpdateListener((a) -> updateCurveProperties());
+ anim.play(va);
+ }
+
+ // Add a tiny bit of translation Z, so that it draws on top of other views
+ if (animateTaskView) {
+ taskView.setTranslationZ(0.1f);
+ }
+
+ mPendingAnimation = pendingAnimation;
+ mPendingAnimation.addEndListener((isSuccess) -> {
+ if (isSuccess) {
+ if (removeTask) {
+ ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+ }
+ removeView(taskView);
+ if (getChildCount() == 0) {
+ onAllTasksRemoved();
+ }
+ }
+ resetTaskVisuals();
+ mPendingAnimation = null;
+ });
+ return pendingAnimation;
+ }
+
+ private static void addAnim(ObjectAnimator anim, long duration,
+ TimeInterpolator interpolator, AnimatorSet set) {
+ anim.setDuration(duration).setInterpolator(interpolator);
+ set.play(anim);
+ }
+
+ private void snapToPageRelative(int delta) {
+ if (getPageCount() == 0) {
+ return;
+ }
+ snapToPage((getNextPage() + getPageCount() + delta) % getPageCount());
+ }
+
+ @Override
+ public void onVisibilityAggregated(boolean isVisible) {
+ super.onVisibilityAggregated(isVisible);
+ if (isVisible && !isFocused()) {
+ // Having focus, even in touch mode, keeps us from losing [Alt+]Tab by preventing
+ // switching to keyboard mode.
+ requestFocus();
+ }
+ }
+
+ public void dismissTask(TaskView taskView, boolean animateTaskView, boolean removeTask) {
+ PendingAnimation pendingAnim = createTaskDismissAnimation(taskView, animateTaskView,
+ removeTask, DISMISS_TASK_DURATION);
+ AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(
+ pendingAnim.anim, DISMISS_TASK_DURATION);
+ controller.dispatchOnStart();
+ controller.setEndAction(() -> pendingAnim.finish(true));
+ controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
+ controller.start();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_TAB:
+ snapToPageRelative(event.isShiftPressed() ? -1 : 1);
+ return true;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ snapToPageRelative(mIsRtl ? -1 : 1);
+ return true;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ snapToPageRelative(mIsRtl ? 1 : -1);
+ return true;
+ case KeyEvent.KEYCODE_DEL:
+ case KeyEvent.KEYCODE_FORWARD_DEL:
+ dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/,
+ true /*removeTask*/);
+ return true;
+ case KeyEvent.KEYCODE_NUMPAD_DOT:
+ if (event.isAltPressed()) {
+ // Numpad DEL pressed while holding Alt.
+ dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/,
+ true /*removeTask*/);
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ public void snapToTaskAfterNext() {
+ snapToPageRelative(1);
+ }
+
+ public void launchNextTask() {
+ final TaskView nextTask = (TaskView) getChildAt(getNextPage());
+ nextTask.launchTask(true);
+ }
+
+ public void setContentAlpha(float alpha) {
+ if (mContentAlpha == alpha) {
+ return;
+ }
+
+ mContentAlpha = alpha;
+ for (int i = getChildCount() - 1; i >= 0; i--) {
+ getChildAt(i).setAlpha(alpha);
+ }
+
+ int alphaInt = Math.round(alpha * 255);
+ mEmptyMessagePaint.setAlpha(alphaInt);
+ mEmptyIcon.setAlpha(alphaInt);
+
+ setVisibility(alpha > 0 ? VISIBLE : GONE);
+ }
+
+ public void setAdjacentScale(float adjacentScale) {
+ if (mAdjacentScale == adjacentScale) {
+ return;
+ }
+ mAdjacentScale = adjacentScale;
+ TaskView currTask = getPageAt(mCurrentPage);
+ if (currTask == null) {
+ return;
+ }
+ currTask.setScaleX(mAdjacentScale);
+ currTask.setScaleY(mAdjacentScale);
+
+ if (mCurrentPage - 1 >= 0) {
+ TaskView adjacentTask = getPageAt(mCurrentPage - 1);
+ float[] scaleAndTranslation = getAdjacentScaleAndTranslation(currTask, adjacentTask,
+ mAdjacentScale, 0);
+ adjacentTask.setScaleX(scaleAndTranslation[0]);
+ adjacentTask.setScaleY(scaleAndTranslation[0]);
+ adjacentTask.setTranslationX(-scaleAndTranslation[1]);
+ adjacentTask.setTranslationY(scaleAndTranslation[2]);
+ }
+ if (mCurrentPage + 1 < getChildCount()) {
+ TaskView adjacentTask = getPageAt(mCurrentPage + 1);
+ float[] scaleAndTranslation = getAdjacentScaleAndTranslation(currTask, adjacentTask,
+ mAdjacentScale, 0);
+ adjacentTask.setScaleX(scaleAndTranslation[0]);
+ adjacentTask.setScaleY(scaleAndTranslation[0]);
+ adjacentTask.setTranslationX(scaleAndTranslation[1]);
+ adjacentTask.setTranslationY(scaleAndTranslation[2]);
+ }
+ }
+
+ private float[] getAdjacentScaleAndTranslation(TaskView currTask, TaskView adjacentTask,
+ float currTaskToScale, float currTaskToTranslationY) {
+ float displacement = currTask.getWidth() * (currTaskToScale - currTask.getCurveScale());
+ return new float[] {
+ currTaskToScale * adjacentTask.getCurveScale(),
+ mIsRtl ? -displacement : displacement,
+ currTaskToTranslationY
+ };
+ }
+
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ child.setAlpha(mContentAlpha);
+ setAdjacentScale(mAdjacentScale);
+ }
+
+ @Override
+ public TaskView getPageAt(int index) {
+ return (TaskView) getChildAt(index);
+ }
+
+ public void updateEmptyMessage() {
+ boolean isEmpty = getChildCount() == 0;
+ boolean hasSizeChanged = mLastMeasureSize.x != getWidth()
+ || mLastMeasureSize.y != getHeight();
+ if (isEmpty == mShowEmptyMessage && !hasSizeChanged) {
+ return;
+ }
+ setContentDescription(isEmpty ? mEmptyMessage : "");
+ mShowEmptyMessage = isEmpty;
+ updateEmptyStateUi(hasSizeChanged);
+ invalidate();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateEmptyStateUi(changed);
+ }
+
+ private void updateEmptyStateUi(boolean sizeChanged) {
+ boolean hasValidSize = getWidth() > 0 && getHeight() > 0;
+ if (sizeChanged && hasValidSize) {
+ mEmptyTextLayout = null;
+ }
+
+ if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
+ mLastMeasureSize.set(getWidth(), getHeight());
+ int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
+ mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
+ mEmptyMessagePaint, availableWidth)
+ .setAlignment(Layout.Alignment.ALIGN_CENTER)
+ .build();
+ int totalHeight = mEmptyTextLayout.getHeight()
+ + mEmptyMessagePadding + mEmptyIcon.getIntrinsicHeight();
+
+ int top = (mLastMeasureSize.y - totalHeight) / 2;
+ int left = (mLastMeasureSize.x - mEmptyIcon.getIntrinsicWidth()) / 2;
+ mEmptyIcon.setBounds(left, top, left + mEmptyIcon.getIntrinsicWidth(),
+ top + mEmptyIcon.getIntrinsicHeight());
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return super.verifyDrawable(who) || (mShowEmptyMessage && who == mEmptyIcon);
+ }
+
+ protected void maybeDrawEmptyMessage(Canvas canvas) {
+ if (mShowEmptyMessage && mEmptyTextLayout != null) {
+ mEmptyIcon.draw(canvas);
+ canvas.save();
+ canvas.translate(mEmptyMessagePadding,
+ mEmptyIcon.getBounds().bottom + mEmptyMessagePadding);
+ mEmptyTextLayout.draw(canvas);
+ canvas.restore();
+ }
+ }
+
+ /**
+ * Animate adjacent tasks off screen while scaling up.
+ *
+ * If launching one of the adjacent tasks, parallax the center task and other adjacent task
+ * to the right.
+ */
+ public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
+ AnimatorSet anim = new AnimatorSet();
+
+ int taskIndex = indexOfChild(tv);
+ int centerTaskIndex = getCurrentPage();
+ boolean launchingCenterTask = taskIndex == centerTaskIndex;
+
+ TaskWindowBounds endInterpolation = tv.getRecentsInterpolator().interpolate(1);
+ float toScale = endInterpolation.taskScale;
+ float toTranslationY = endInterpolation.taskY;
+
+ if (launchingCenterTask) {
+ TaskView centerTask = getPageAt(centerTaskIndex);
+ if (taskIndex - 1 >= 0) {
+ TaskView adjacentTask = getPageAt(taskIndex - 1);
+ float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
+ adjacentTask, toScale, toTranslationY);
+ scaleAndTranslation[1] = -scaleAndTranslation[1];
+ anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
+ }
+ if (taskIndex + 1 < getPageCount()) {
+ TaskView adjacentTask = getPageAt(taskIndex + 1);
+ float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask,
+ adjacentTask, toScale, toTranslationY);
+ anim.play(createAnimForChild(adjacentTask, scaleAndTranslation));
+ }
+ } 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));
+
+ int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - taskIndex);
+ if (otherAdjacentTaskIndex >= 0 && otherAdjacentTaskIndex < getPageCount()) {
+ anim.play(ObjectAnimator.ofPropertyValuesHolder(getPageAt(otherAdjacentTaskIndex),
+ new PropertyListBuilder()
+ .translationX(mIsRtl ? -displacementX : displacementX)
+ .scale(1)
+ .build()));
+ }
+ }
+ return anim;
+ }
+
+ private ObjectAnimator createAnimForChild(View child, float[] toScaleAndTranslation) {
+ return ObjectAnimator.ofPropertyValuesHolder(child,
+ new PropertyListBuilder()
+ .scale(child.getScaleX() * toScaleAndTranslation[0])
+ .translationX(toScaleAndTranslation[1])
+ .translationY(toScaleAndTranslation[2])
+ .build());
+ }
+
+ public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+ throw new IllegalStateException("Another pending animation is still running");
+ }
+ AnimatorSet anim = createAdjacentPageAnimForTaskLaunch(tv);
+
+ int count = getChildCount();
+ if (count == 0) {
+ return new PendingAnimation(anim);
+ }
+
+ final RecentsAnimationInterpolator recentsInterpolator = tv.getRecentsInterpolator();
+ ValueAnimator targetViewAnim = ValueAnimator.ofFloat(0, 1);
+ targetViewAnim.addUpdateListener((animation) -> {
+ float percent = animation.getAnimatedFraction();
+ TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
+ tv.setScaleX(tw.taskScale);
+ tv.setScaleY(tw.taskScale);
+ tv.setTranslationX(tw.taskX);
+ tv.setTranslationY(tw.taskY);
+ });
+ anim.play(targetViewAnim);
+ anim.setDuration(duration);
+
+ mPendingAnimation = new PendingAnimation(anim);
+ mPendingAnimation.addEndListener((isSuccess) -> {
+ if (isSuccess) {
+ tv.launchTask(false);
+ } else {
+ resetTaskVisuals();
+ }
+ mPendingAnimation = null;
+ });
+ return mPendingAnimation;
+ }
+
+ @Override
+ protected void notifyPageSwitchListener(int prevPage) {
+ super.notifyPageSwitchListener(prevPage);
+ getChildAt(mCurrentPage).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+
+ @Override
+ protected String getCurrentPageDescription() {
+ return "";
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskMenuView.java b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
similarity index 85%
rename from quickstep/src/com/android/quickstep/TaskMenuView.java
rename to quickstep/src/com/android/quickstep/views/TaskMenuView.java
index 52b2400..dd90c88 100644
--- a/quickstep/src/com/android/quickstep/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.quickstep;
+package com.android.quickstep.views;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -32,14 +32,16 @@
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.TaskSystemShortcut;
+import com.android.quickstep.TaskUtils;
/**
* Contains options for a recent task when long-pressing its icon.
@@ -49,7 +51,7 @@
private static final Rect sTempRect = new Rect();
/** Note that these will be shown in order from top to bottom, if available for the task. */
- private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
+ public static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
new TaskSystemShortcut.AppInfo(),
new TaskSystemShortcut.SplitScreen(),
new TaskSystemShortcut.Pin(),
@@ -58,7 +60,7 @@
private static final long OPEN_CLOSE_DURATION = 220;
- private Launcher mLauncher;
+ private BaseDraggingActivity mActivity;
private TextView mTaskIconAndName;
private AnimatorSet mOpenCloseAnimator;
private TaskView mTaskView;
@@ -70,7 +72,7 @@
public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
+ mActivity = BaseDraggingActivity.fromContext(context);
setClipToOutline(true);
setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -90,7 +92,7 @@
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- DragLayer dl = mLauncher.getDragLayer();
+ BaseDragLayer dl = mActivity.getDragLayer();
if (!dl.isEventOverView(this, ev)) {
// TODO: log this once we have a new container type for it?
close(true);
@@ -120,9 +122,9 @@
}
public static boolean showForTask(TaskView taskView) {
- Launcher launcher = Launcher.getLauncher(taskView.getContext());
- final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate(
- R.layout.task_menu, launcher.getDragLayer(), false);
+ BaseDraggingActivity activity = BaseDraggingActivity.fromContext(taskView.getContext());
+ final TaskMenuView taskMenuView = (TaskMenuView) activity.getLayoutInflater().inflate(
+ R.layout.task_menu, activity.getDragLayer(), false);
return taskMenuView.populateAndShowForTask(taskView);
}
@@ -130,7 +132,7 @@
if (isAttachedToWindow()) {
return false;
}
- mLauncher.getDragLayer().addView(this);
+ mActivity.getDragLayer().addView(this);
mTaskView = taskView;
addMenuOptions(mTaskView);
orientAroundTaskView(mTaskView);
@@ -143,10 +145,11 @@
int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
icon.setBounds(0, 0, iconSize, iconSize);
mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
- mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, taskView.getTask()));
+ mTaskIconAndName.setText(TaskUtils.getTitle(getContext(), taskView.getTask()));
+ mTaskIconAndName.setOnClickListener(v -> close(true));
for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
- OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, taskView);
+ OnClickListener onClickListener = menuOption.getOnClickListener(mActivity, taskView);
if (onClickListener != null) {
addMenuOption(menuOption, onClickListener);
}
@@ -154,7 +157,7 @@
}
private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
- DeepShortcutView menuOptionView = (DeepShortcutView) mLauncher.getLayoutInflater().inflate(
+ DeepShortcutView menuOptionView = (DeepShortcutView) mActivity.getLayoutInflater().inflate(
R.layout.system_shortcut, this, false);
menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId);
menuOptionView.getBubbleText().setText(menuOption.labelResId);
@@ -164,8 +167,8 @@
private void orientAroundTaskView(TaskView taskView) {
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
- Rect insets = mLauncher.getDragLayer().getInsets();
+ mActivity.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+ Rect insets = mActivity.getDragLayer().getInsets();
int x = sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left;
setX(Utilities.isRtl(getResources()) ? -x : x);
setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top);
@@ -208,7 +211,7 @@
private void closeComplete() {
mIsOpen = false;
- mLauncher.getDragLayer().removeView(this);
+ mActivity.getDragLayer().removeView(this);
}
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
new file mode 100644
index 0000000..8b41b58
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -0,0 +1,262 @@
+/*
+ * 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.quickstep.views;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LightingColorFilter;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.quickstep.TaskOverlayFactory;
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * A task in the Recents view.
+ */
+public class TaskThumbnailView extends View {
+
+ private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
+
+ private final float mCornerRadius;
+ private final float mFadeLength;
+
+ private final TaskOverlay mOverlay;
+ private final Paint mPaint = new Paint();
+ private final Paint mLockedPaint = new Paint();
+
+ private final Matrix mMatrix = new Matrix();
+
+ private Task mTask;
+ private ThumbnailData mThumbnailData;
+ protected BitmapShader mBitmapShader;
+
+ private float mDimAlpha = 1f;
+
+ public TaskThumbnailView(Context context) {
+ this(context, null);
+ }
+
+ public TaskThumbnailView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
+ mFadeLength = getResources().getDimension(R.dimen.task_fade_length);
+ mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
+ mPaint.setFilterBitmap(true);
+ mLockedPaint.setColor(Color.WHITE);
+ }
+
+ public void bind() {
+ mOverlay.reset();
+ }
+
+ /**
+ * Updates this thumbnail.
+ */
+ public void setThumbnail(Task task, ThumbnailData thumbnailData) {
+ mTask = task;
+ mPaint.setColor(task == null ? Color.BLACK : task.colorBackground | 0xFF000000);
+
+ if (thumbnailData != null && thumbnailData.thumbnail != null) {
+ Bitmap bm = thumbnailData.thumbnail;
+ bm.prepareToDraw();
+ mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+ mPaint.setShader(mBitmapShader);
+ mThumbnailData = thumbnailData;
+ updateThumbnailMatrix();
+ } else {
+ mBitmapShader = null;
+ mThumbnailData = null;
+ mPaint.setShader(null);
+ mOverlay.reset();
+ }
+ updateThumbnailPaintFilter();
+ }
+
+ /**
+ * Sets the alpha of the dim layer on top of this view.
+ */
+ public void setDimAlpha(float dimAlpha) {
+ mDimAlpha = dimAlpha;
+ updateThumbnailPaintFilter();
+ }
+
+ public Rect getInsets() {
+ if (mThumbnailData != null) {
+ return mThumbnailData.insets;
+ }
+ return new Rect();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mTask == null) {
+ return;
+ }
+ canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mCornerRadius,
+ mCornerRadius, mTask.isLocked ? mLockedPaint : mPaint);
+ }
+
+ private void updateThumbnailPaintFilter() {
+ int mul = (int) (mDimAlpha * 255);
+ if (mBitmapShader != null) {
+ LightingColorFilter filter = getLightingColorFilter(mul);
+ mPaint.setColorFilter(filter);
+ mLockedPaint.setColorFilter(filter);
+ } else {
+ mPaint.setColorFilter(null);
+ mPaint.setColor(Color.argb(255, mul, mul, mul));
+ }
+ invalidate();
+ }
+
+ private void updateThumbnailMatrix() {
+ boolean rotate = false;
+ if (mBitmapShader != null && mThumbnailData != null) {
+ float scale = mThumbnailData.scale;
+ float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
+ (mThumbnailData.insets.left + mThumbnailData.insets.right) * scale;
+ float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
+ (mThumbnailData.insets.top + mThumbnailData.insets.bottom) * scale;
+ final float thumbnailScale;
+ final DeviceProfile profile = BaseActivity.fromContext(getContext())
+ .getDeviceProfile();
+ if (getMeasuredWidth() == 0) {
+ // If we haven't measured , skip the thumbnail drawing and only draw the background
+ // color
+ thumbnailScale = 0f;
+ } else {
+ final Configuration configuration =
+ getContext().getApplicationContext().getResources().getConfiguration();
+ if (configuration.orientation == mThumbnailData.orientation) {
+ // If we are in the same orientation as the screenshot, just scale it to the
+ // width of the task view
+ thumbnailScale = getMeasuredWidth() / thumbnailWidth;
+ } else {
+ if (FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION) {
+ rotate = true;
+ // Scale the height (will be width after rotation) to the width of this view
+ thumbnailScale = getMeasuredWidth() / thumbnailHeight;
+ } else {
+ if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // Scale the landscape thumbnail up to app size, then scale that to the
+ // task view size to match other portrait screenshots
+ thumbnailScale = ((float) getMeasuredWidth() / profile.widthPx);
+ } else {
+ // Otherwise, scale the screenshot to fit 1:1 in the current orientation
+ thumbnailScale = 1;
+ }
+ }
+ }
+ }
+ if (rotate) {
+ int rotationDir = profile.isVerticalBarLayout() && !profile.isSeascape() ? -1 : 1;
+ mMatrix.setRotate(90 * rotationDir);
+ Rect thumbnailInsets = mThumbnailData.insets;
+ int newLeftInset = rotationDir == 1 ? thumbnailInsets.bottom : thumbnailInsets.top;
+ int newTopInset = rotationDir == 1 ? thumbnailInsets.left : thumbnailInsets.right;
+ mMatrix.postTranslate(-newLeftInset * scale, -newTopInset * scale);
+ if (rotationDir == -1) {
+ // Crop the right/bottom side of the screenshot rather than left/top
+ float excessHeight = thumbnailWidth * thumbnailScale - getMeasuredHeight();
+ mMatrix.postTranslate(0, -excessHeight);
+ }
+ // Move the screenshot to the thumbnail window (rotation moved it out).
+ if (rotationDir == 1) {
+ mMatrix.postTranslate(mThumbnailData.thumbnail.getHeight(), 0);
+ } else {
+ mMatrix.postTranslate(0, mThumbnailData.thumbnail.getWidth());
+ }
+ } else {
+ mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
+ -mThumbnailData.insets.top * scale);
+ }
+ mMatrix.postScale(thumbnailScale, thumbnailScale);
+ mBitmapShader.setLocalMatrix(mMatrix);
+
+ Shader shader = mBitmapShader;
+ if (!FeatureFlags.OVERVIEW_USE_SCREENSHOT_ORIENTATION) {
+ float bitmapHeight = Math.max(thumbnailHeight * thumbnailScale, 0);
+ if (Math.round(bitmapHeight) < getMeasuredHeight()) {
+ int color = mPaint.getColor();
+ LinearGradient fade = new LinearGradient(
+ 0, bitmapHeight - mFadeLength, 0, bitmapHeight,
+ color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
+ shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+ }
+
+ float bitmapWidth = Math.max(thumbnailWidth * thumbnailScale, 0);
+ if (Math.round(bitmapWidth) < getMeasuredWidth()) {
+ int color = mPaint.getColor();
+ LinearGradient fade = new LinearGradient(
+ bitmapWidth - mFadeLength, 0, bitmapWidth, 0,
+ color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
+ shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+ }
+ }
+ mPaint.setShader(shader);
+ }
+
+ if (rotate) {
+ // The overlay doesn't really work when the screenshot is rotated, so don't add it.
+ mOverlay.reset();
+ } else {
+ mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
+ }
+ invalidate();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ updateThumbnailMatrix();
+ }
+
+ private static LightingColorFilter getLightingColorFilter(int dimColor) {
+ if (dimColor < 0) {
+ dimColor = 0;
+ } else if (dimColor > 255) {
+ dimColor = 255;
+ }
+ if (sDimFilterCache[dimColor] == null) {
+ sDimFilterCache[dimColor] =
+ new LightingColorFilter(Color.argb(255, dimColor, dimColor, dimColor), 0);
+ }
+ return sDimFilterCache[dimColor];
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
new file mode 100644
index 0000000..42da472
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -0,0 +1,271 @@
+/*
+ * 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.quickstep.views;
+
+import android.animation.TimeInterpolator;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.R;
+import com.android.quickstep.RecentsAnimationInterpolator;
+import com.android.quickstep.TaskSystemShortcut;
+import com.android.quickstep.views.RecentsView.PageCallbacks;
+import com.android.quickstep.views.RecentsView.ScrollState;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+import java.util.function.Consumer;
+
+/**
+ * A task in the Recents view.
+ */
+public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks {
+
+ /** 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.
+ */
+ private static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
+
+ /**
+ * How much to scale down pages near the edge of the screen.
+ */
+ private static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
+
+ private static final long SCALE_ICON_DURATION = 120;
+
+ private Task mTask;
+ private TaskThumbnailView mSnapshotView;
+ private ImageView mIconView;
+ private float mCurveScale;
+
+ public TaskView(Context context) {
+ this(context, null);
+ }
+
+ public TaskView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setOnClickListener((view) -> launchTask(true /* animate */));
+ setOutlineProvider(new TaskOutlineProvider(getResources()));
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSnapshotView = findViewById(R.id.snapshot);
+ mIconView = findViewById(R.id.icon);
+ }
+
+ /**
+ * Updates this task view to the given {@param task}.
+ */
+ public void bind(Task task) {
+ if (mTask != null) {
+ mTask.removeCallback(this);
+ }
+ mTask = task;
+ mSnapshotView.bind();
+ task.addCallback(this);
+ setContentDescription(task.titleDescription);
+ }
+
+ public Task getTask() {
+ return mTask;
+ }
+
+ public TaskThumbnailView getThumbnail() {
+ return mSnapshotView;
+ }
+
+ public void launchTask(boolean animate) {
+ launchTask(animate, null, null);
+ }
+
+ public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
+ Handler resultCallbackHandler) {
+ if (mTask != null) {
+ final ActivityOptions opts;
+ if (animate) {
+ opts = BaseDraggingActivity.fromContext(getContext())
+ .getActivityLaunchOptions(this, false);
+ } else {
+ opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
+ }
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
+ opts, resultCallback, resultCallbackHandler);
+ }
+ }
+
+ @Override
+ public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
+ mSnapshotView.setThumbnail(task, thumbnailData);
+ mIconView.setImageDrawable(task.icon);
+ mIconView.setOnClickListener(icon -> TaskMenuView.showForTask(this));
+ mIconView.setOnLongClickListener(icon -> TaskMenuView.showForTask(this));
+ }
+
+ @Override
+ public void onTaskDataUnloaded() {
+ mSnapshotView.setThumbnail(null, null);
+ mIconView.setImageDrawable(null);
+ mIconView.setOnLongClickListener(null);
+ }
+
+ @Override
+ public void onTaskWindowingModeChanged() {
+ // Do nothing
+ }
+
+ public void animateIconToScale(float scale) {
+ mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
+ }
+
+ protected void setIconScale(float iconScale) {
+ mIconView.animate().cancel();
+ mIconView.setScaleX(iconScale);
+ mIconView.setScaleY(iconScale);
+ }
+
+ public void resetVisualProperties() {
+ setScaleX(1f);
+ setScaleY(1f);
+ setTranslationX(0f);
+ setTranslationY(0f);
+ setTranslationZ(0);
+ setAlpha(1f);
+ }
+
+ @Override
+ public void onPageScroll(ScrollState scrollState) {
+ float curveInterpolation =
+ CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
+
+ mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
+
+ mCurveScale = 1 - curveInterpolation * EDGE_SCALE_DOWN_FACTOR;
+ setScaleX(mCurveScale);
+ setScaleY(mCurveScale);
+ }
+
+ public float getCurveScale() {
+ return mCurveScale;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ // TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
+ return false;
+ }
+
+ public RecentsAnimationInterpolator getRecentsInterpolator() {
+ Rect taskViewBounds = new Rect();
+ BaseDraggingActivity activity = BaseDraggingActivity.fromContext(getContext());
+ DeviceProfile dp = activity.getDeviceProfile();
+ activity.getDragLayer().getDescendantRectRelativeToSelf(this, taskViewBounds);
+
+ // TODO: Use the actual target insets instead of the current thumbnail insets in case the
+ // device state has changed
+ return new RecentsAnimationInterpolator(
+ new Rect(0, 0, dp.widthPx, dp.heightPx),
+ getThumbnail().getInsets(),
+ taskViewBounds,
+ new Rect(0, getThumbnail().getTop(), 0, 0),
+ getScaleX(),
+ getTranslationX());
+ }
+
+ private static final class TaskOutlineProvider extends ViewOutlineProvider {
+
+ private final int mMarginTop;
+ private final float mRadius;
+
+ TaskOutlineProvider(Resources res) {
+ mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ mRadius = res.getDimension(R.dimen.task_corner_radius);
+ }
+
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, mMarginTop, view.getWidth(),
+ view.getHeight(), mRadius);
+ }
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+
+ info.addAction(
+ new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close_task,
+ getContext().getText(R.string.accessibility_close_task)));
+
+ final Context context = getContext();
+ final BaseDraggingActivity activity = BaseDraggingActivity.fromContext(context);
+ for (TaskSystemShortcut menuOption : TaskMenuView.MENU_OPTIONS) {
+ OnClickListener onClickListener = menuOption.getOnClickListener(activity, this);
+ if (onClickListener != null) {
+ info.addAction(new AccessibilityNodeInfo.AccessibilityAction(menuOption.labelResId,
+ context.getText(menuOption.labelResId)));
+ }
+ }
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == R.string.accessibility_close_task) {
+ ((RecentsView) getParent()).dismissTask(this, true /*animateTaskView*/,
+ true /*removeTask*/);
+ return true;
+ }
+
+ for (TaskSystemShortcut menuOption : TaskMenuView.MENU_OPTIONS) {
+ if (action == menuOption.labelResId) {
+ OnClickListener onClickListener = menuOption.getOnClickListener(
+ BaseDraggingActivity.fromContext(getContext()), this);
+ if (onClickListener != null) {
+ onClickListener.onClick(this);
+ }
+ return true;
+ }
+ }
+
+ return super.performAccessibilityAction(action, arguments);
+ }
+}
diff --git a/res/drawable-v24/ic_info_shadow.xml b/res/drawable-v24/ic_setup_shadow.xml
similarity index 94%
rename from res/drawable-v24/ic_info_shadow.xml
rename to res/drawable-v24/ic_setup_shadow.xml
index 1fe2c46..10aeee6 100644
--- a/res/drawable-v24/ic_info_shadow.xml
+++ b/res/drawable-v24/ic_setup_shadow.xml
@@ -15,5 +15,5 @@
-->
<com.android.launcher3.graphics.ShadowDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/ic_info_no_shadow"
+ android:src="@drawable/ic_setting"
android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/drawable/all_apps_search_divider.xml b/res/drawable/bg_all_apps_searchbox.xml
similarity index 78%
rename from res/drawable/all_apps_search_divider.xml
rename to res/drawable/bg_all_apps_searchbox.xml
index 99905e4..c324927 100644
--- a/res/drawable/all_apps_search_divider.xml
+++ b/res/drawable/bg_all_apps_searchbox.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!-- 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.
@@ -13,8 +13,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="rectangle">
- <solid android:color="?android:attr/colorAccent" />
- <size android:height="1dp" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+ <solid android:color="?attr/popupColorPrimary" />
+ <corners android:radius="2dp" />
</shape>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet_scrim.xml b/res/drawable/ic_close.xml
similarity index 62%
copy from res/layout/widgets_bottom_sheet_scrim.xml
copy to res/drawable/ic_close.xml
index 6c6626c..fc9ed49 100644
--- a/res/layout/widgets_bottom_sheet_scrim.xml
+++ b/res/drawable/ic_close.xml
@@ -1,4 +1,3 @@
-<?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");
@@ -13,11 +12,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<com.android.launcher3.graphics.GradientView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/gradient_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:layout_ignoreInsets="true" />
\ No newline at end of file
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_drag_indicator.xml b/res/drawable/ic_drag_indicator.xml
new file mode 100644
index 0000000..d50bdd3
--- /dev/null
+++ b/res/drawable/ic_drag_indicator.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="2dp"
+ android:width="16dp"
+ android:viewportHeight="2.0"
+ android:viewportWidth="16.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M1,0h14c0.55,0,1,0.45,1,1s-0.45,1-1,1H1C0.45,2,0,1.55,0,1S0.45,0,1,0z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index 08eba25..a83aab3 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -16,15 +16,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/options_menu_icon_size"
android:height="@dimen/options_menu_icon_size"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorPrimary" >
<path
- android:fillColor="?android:attr/textColorPrimary"
- android:pathData="M42.8,28.4l-3.88-2.9c0.06-0.5,0.08-1,0.08-1.52s-0.02-1.02-0.08-1.52l3.88-2.86c0.84-0.62,1.04-1.88,
- 0.48-2.82l-3.2-5.52c-0.56-0.96-1.76-1.4-2.72-1l-4.28,1.82c-0.96-0.74-2.02-1.36-3.14-1.84l-0.54-4.4C29.28,4.8,28.28,4,
- 27.18,4h-6.36c-1.1,0-2.1,0.8-2.22,1.84l-0.52,4.38c-1.14,0.48-2.2,1.1-3.16,1.84l-4.28-1.82c-0.96-0.4-2.16,0.04-2.72,
- 1l-3.2,5.52c-0.56,0.96-0.36,2.2,0.48,2.84l3.88,2.9C9.02,22.98,9,23.48,9,24s0.02,1.02,0.08,1.52L5.2,28.4c-0.84,0.62-1.04,
- 1.88-0.48,2.82l3.2,5.52c0.56,0.96,1.76,1.4,2.72,1l4.28-1.82c0.96,0.74,2.02,1.36,3.14,1.84l0.54,4.38C18.72,43.2,19.72,
- 44,20.82,44h6.36c1.1,0,2.08-0.8,2.22-1.84l0.54-4.38c1.12-0.48,2.18-1.1,3.14-1.84l4.28,1.82c0.96,0.4,2.16-0.04,
- 2.72-1l3.2-5.52C43.84,30.28,43.64,29.04,42.8,28.4z M24,31c-3.86,0-7-3.14-7-7s3.14-7,7-7s7,3.14,7,7S27.86,31,24,31z"/>
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.85,22.25h-3.7c-0.74,0-1.36-0.54-1.45-1.27l-0.27-1.89c-0.27-0.14-0.53-0.29-0.79-0.46l-1.8,0.72
+ c-0.7,0.26-1.47-0.03-1.81-0.65L2.2,15.53c-0.35-0.66-0.2-1.44,0.36-1.88l1.53-1.19c-0.01-0.15-0.02-0.3-0.02-0.46
+ c0-0.15,0.01-0.31,0.02-0.46l-1.52-1.19C1.98,9.9,1.83,9.09,2.2,8.47l1.85-3.19c0.34-0.62,1.11-0.9,1.79-0.63l1.81,0.73
+ c0.26-0.17,0.52-0.32,0.78-0.46l0.27-1.91c0.09-0.7,0.71-1.25,1.44-1.25h3.7c0.74,0,1.36,0.54,1.45,1.27l0.27,1.89
+ c0.27,0.14,0.53,0.29,0.79,0.46l1.8-0.72c0.71-0.26,1.48,0.03,1.82,0.65l1.84,3.18c0.36,0.66,0.2,1.44-0.36,1.88l-1.52,1.19
+ c0.01,0.15,0.02,0.3,0.02,0.46s-0.01,0.31-0.02,0.46l1.52,1.19c0.56,0.45,0.72,1.23,0.37,1.86l-1.86,3.22
+ c-0.34,0.62-1.11,0.9-1.8,0.63l-1.8-0.72c-0.26,0.17-0.52,0.32-0.78,0.46l-0.27,1.91C15.21,21.71,14.59,22.25,13.85,22.25z
+ M13.32,20.72c0,0.01,0,0.01,0,0.02L13.32,20.72z M10.68,20.7l0,0.02C10.69,20.72,10.69,20.71,10.68,20.7z M10.62,20.25h2.76
+ l0.37-2.55l0.53-0.22c0.44-0.18,0.88-0.44,1.34-0.78l0.45-0.34l2.38,0.96l1.38-2.4l-2.03-1.58l0.07-0.56
+ c0.03-0.26,0.06-0.51,0.06-0.78c0-0.27-0.03-0.53-0.06-0.78l-0.07-0.56l2.03-1.58l-1.39-2.4l-2.39,0.96l-0.45-0.35
+ c-0.42-0.32-0.87-0.58-1.33-0.77L13.75,6.3l-0.37-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7,8.84,6.95,8.38,7.3L7.93,7.63
+ L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47,6.06,11.74,6.06,12c0,0.26,0.02,0.53,0.06,0.78l0.07,0.56l-2.03,1.58
+ l1.38,2.4l2.39-0.96l0.45,0.35c0.43,0.33,0.86,0.58,1.33,0.77l0.53,0.22L10.62,20.25z M18.22,17.72c0,0.01-0.01,0.02-0.01,0.03
+ L18.22,17.72z M5.77,17.71l0.01,0.02C5.78,17.72,5.77,17.71,5.77,17.71z M3.93,9.47L3.93,9.47C3.93,9.47,3.93,9.47,3.93,9.47z
+ M18.22,6.27c0,0.01,0.01,0.02,0.01,0.02L18.22,6.27z M5.79,6.25L5.78,6.27C5.78,6.27,5.79,6.26,5.79,6.25z M13.31,3.28
+ c0,0.01,0,0.01,0,0.02L13.31,3.28z M10.69,3.26l0,0.02C10.69,3.27,10.69,3.27,10.69,3.26z"/>
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.5,12a3.5,3.5 0 1,0 7,0a3.5,3.5 0 1,0 -7,0"/>
</vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index 0c5a125..7fd9340 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -16,14 +16,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/options_menu_icon_size"
android:height="@dimen/options_menu_icon_size"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
android:fillColor="?android:attr/textColorPrimary"
- android:pathData="M8,8h13c0.56,0,1-0.44,1-1V5c0-0.56-0.44-1-1-1H8C5.8,4,4,5.8,4,8v13c0,0.56,0.44,1,1,1h2c0.56,0,1-0.44,
- 1-1V8zM18.44,27.96L12,36h24l-4.4-5.86c-0.8-1.06-2.4-1.06-3.2,0l-2.46,3.28l-4.38-5.46C20.76,26.96,19.24,26.96,18.44,27.96z M34,
- 17c0-1.66-1.34-3-3-3s-3,1.34-3,3s1.34,3,3,3S34,18.66,34,17z M40,4H27c-0.56,0-1,0.44-1,1v2c0,0.56,0.44,1,1,1h13v13c0,
- 0.56,0.44,1,1,1h2c0.56,0,1-0.44,1-1V8C44,5.8,42.2,4,40,4z M40,40H27c-0.56,0-1,0.44-1,1v2c0,0.56,0.44,1,1,1h13c2.2,
- 0,4-1.8,4-4V27c0-0.56-0.44-1-1-1h-2c-0.56,0-1,0.44-1,1V40z M7,26H5c-0.56,0-1,0.44-1,1v13c0,2.2,1.8,4,4,4h13c0.56,0,1-0.44,
- 1-1v-2c0-0.56-0.44-1-1-1H8V27C8,26.44,7.56,26,7,26z"/>
+ android:pathData="M9,12.71l2.14,2.58l3-3.87L18,16.57H6L9,12.71z M5,5h6V3H5C3.9,3,3,3.9,3,5v6h2V5z M19,19h-6v2h6c1.1,0,2-0.9,2-2v-6h-2V19z
+ M5,19v-6H3v6c0,1.1,0.9,2,2,2h6v-2H5z M19,5v6h2V5c0-1.1-0.9-2-2-2h-6v2H19z M16,9c0.55,0,1-0.45,1-1s-0.45-1-1-1
+ c-0.55,0-1,0.45-1,1S15.45,9,16,9z"/>
</vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index 4bb23b3..3ebbb68 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -16,11 +16,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="@dimen/options_menu_icon_size"
android:height="@dimen/options_menu_icon_size"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
android:fillColor="?android:attr/textColorPrimary"
- android:pathData="M26,28v12c0,1,0.8,2,2,2h12c1,0,2-1,2-2V28c0-1.2-1-2-2-2H28C26.8,26,26,26.8,26,28z M8,42h12c1.2,0,2-1,2-2V28
- c0-1.2-0.8-2-2-2H8c-1,0-2,0.8-2,2v12C6,41,7,42,8,42z M6,8v12c0,1.2,1,2,2,2h12c1.2,0,2-0.8,2-2V8c0-1-0.8-2-2-2H8C7,6,6,7,6,8z
- M32.6,4.6l-8,8c-0.8,0.8-0.8,2,0,2.8l8,8c0.8,0.8,2,0.8,2.8,0l8-8c0.8-0.8,0.8-2,0-2.8l-8-8C34.6,3.8,33.4,3.8,32.6,4.6z"/>
+ android:pathData="M16.66,4.52l2.83,2.83l-2.83,2.83l-2.83-2.83L16.66,4.52 M9,5v4H5V5H9 M19,15v4h-4v-4H19 M9,15v4H5v-4H9 M16.66,1.69
+ L11,7.34L16.66,13l5.66-5.66L16.66,1.69L16.66,1.69z M11,3H3v8h8V7.34V3L11,3z M21,13h-4.34H13v8h8V13L21,13z M11,13H3v8h8V13L11,13z"/>
</vector>
diff --git a/res/drawable/round_rect_primary.xml b/res/drawable/round_rect_primary.xml
index 9f8f4da..16310f8 100644
--- a/res/drawable/round_rect_primary.xml
+++ b/res/drawable/round_rect_primary.xml
@@ -17,5 +17,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimary" />
- <corners android:radius="8dp" />
+ <corners android:radius="@dimen/bg_round_rect_radius" />
</shape>
diff --git a/res/drawable/all_apps_search_divider.xml b/res/drawable/top_round_rect_primary.xml
similarity index 67%
copy from res/drawable/all_apps_search_divider.xml
copy to res/drawable/top_round_rect_primary.xml
index 99905e4..1caaa02 100644
--- a/res/drawable/all_apps_search_divider.xml
+++ b/res/drawable/top_round_rect_primary.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
+<!--
+ 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.
@@ -15,6 +16,11 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent" />
- <size android:height="1dp" />
-</shape>
\ No newline at end of file
+ <solid android:color="?android:attr/colorPrimary" />
+ <corners
+ android:topLeftRadius="@dimen/bg_round_rect_radius"
+ android:topRightRadius="@dimen/bg_round_rect_radius"
+ android:bottomLeftRadius="0dp"
+ android:bottomRightRadius="0dp"
+ />
+</shape>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 2ce6b8c..450d107 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -29,20 +29,13 @@
<include layout="@layout/all_apps_rv_layout" />
- <include layout="@layout/all_apps_fast_scroller" />
-
<include layout="@layout/all_apps_floating_header" />
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
platform bug, which prevents using custom attributes in <include> tag -->
<include
android:id="@id/search_container_all_apps"
- layout="?android:attr/keyboardLayout"/>
+ layout="@layout/search_container_all_apps"/>
- <View
- android:id="@+id/nav_bar_bg"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_alignParentBottom="true"
- android:background="?attr/allAppsNavBarScrimColor" />
+ <include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_rv_layout.xml b/res/layout/all_apps_rv_layout.xml
index 3c19f8c..c353b36 100644
--- a/res/layout/all_apps_rv_layout.xml
+++ b/res/layout/all_apps_rv_layout.xml
@@ -22,5 +22,4 @@
android:layout_below="@id/search_container_all_apps"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
- android:focusable="true"
- android:overScrollMode="never" />
+ android:focusable="true" />
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index 54a9b88..2accd2d 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -14,21 +14,23 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.launcher3.allapps.InterceptingViewPager
+<com.android.launcher3.allapps.AllAppsPagedView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/all_apps_tabs_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/search_container_all_apps"
android:layout_gravity="center_horizontal|top"
android:layout_marginTop="@dimen/all_apps_header_tab_height"
- android:clipChildren="false"
+ android:clipChildren="true"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
- android:paddingTop="@dimen/all_apps_header_top_padding">
+ android:paddingTop="@dimen/all_apps_header_top_padding"
+ launcher:pageIndicator="@+id/tabs" >
<include layout="@layout/all_apps_rv_layout" />
<include layout="@layout/all_apps_rv_layout" />
-</com.android.launcher3.allapps.InterceptingViewPager>
\ No newline at end of file
+</com.android.launcher3.allapps.AllAppsPagedView>
\ No newline at end of file
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 4a3db1f..4a2ad42 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -35,7 +35,6 @@
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif"
launcher:layoutHorizontal="true"
- launcher:deferShadowGeneration="true"
launcher:iconDisplay="shortcut_popup"
launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size" />
diff --git a/res/layout/widgets_bottom_sheet_scrim.xml b/res/layout/drag_handle_indicator.xml
similarity index 64%
rename from res/layout/widgets_bottom_sheet_scrim.xml
rename to res/layout/drag_handle_indicator.xml
index 6c6626c..d5a7b8a 100644
--- a/res/layout/widgets_bottom_sheet_scrim.xml
+++ b/res/layout/drag_handle_indicator.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,11 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<com.android.launcher3.graphics.GradientView
+<com.android.launcher3.views.LauncherDragIndicator
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/gradient_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:layout_ignoreInsets="true" />
\ No newline at end of file
+ android:id="@+id/drag_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:contentDescription="@string/all_apps_button_label"
+ android:scaleType="centerInside"
+ android:tint="?attr/workspaceTextColor" />
diff --git a/res/layout/drop_target_bar.xml b/res/layout/drop_target_bar.xml
index d376bcf..2f21c60 100644
--- a/res/layout/drop_target_bar.xml
+++ b/res/layout/drop_target_bar.xml
@@ -32,18 +32,8 @@
android:gravity="center"
android:text="@string/remove_drop_target_label" />
- <!-- App Info -->
- <com.android.launcher3.InfoDropTarget
- android:id="@+id/info_target_text"
- style="@style/DropTargetButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:text="@string/app_info_drop_target_label" />
-
<!-- Uninstall target -->
- <com.android.launcher3.UninstallDropTarget
+ <com.android.launcher3.SecondaryDropTarget
android:id="@+id/uninstall_target_text"
style="@style/DropTargetButton"
android:layout_width="wrap_content"
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 582a83f..00f0b5f 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -17,10 +17,12 @@
android:theme="@style/HomeScreenElementTheme"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
<com.android.launcher3.CellLayout
android:id="@+id/layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
- launcher:containerType="hotseat" />
+ launcher:containerType="hotseat"
+ android:importantForAccessibility="no" />
</com.android.launcher3.Hotseat>
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 314359b..a4acf06 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -44,24 +44,13 @@
layout="@layout/overview_panel"
android:visibility="gone" />
- <com.android.launcher3.views.AllAppsScrim
- android:id="@+id/all_apps_scrim"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <!-- DO NOT CHANGE THE ID -->
- <include
- android:id="@+id/hotseat"
- layout="@layout/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
<!-- Keep these behind the workspace so that they are not visible when
we go into AllApps -->
<com.android.launcher3.pageindicators.WorkspacePageIndicator
android:id="@+id/page_indicator"
android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+ android:layout_height="4dp"
+ android:layout_gravity="bottom|center_horizontal"
android:theme="@style/HomeScreenElementTheme" />
<include
@@ -74,6 +63,16 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
+
+ <include android:id="@+id/drag_indicator"
+ layout="@layout/drag_handle_indicator" />
+
+ <!-- DO NOT CHANGE THE ID -->
+ <include
+ android:id="@+id/hotseat"
+ layout="@layout/hotseat"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</com.android.launcher3.dragndrop.DragLayer>
</com.android.launcher3.LauncherRootView>
diff --git a/res/layout/widgets_bottom_sheet_scrim.xml b/res/layout/longpress_options_menu.xml
similarity index 60%
copy from res/layout/widgets_bottom_sheet_scrim.xml
copy to res/layout/longpress_options_menu.xml
index 6c6626c..168dbc3 100644
--- a/res/layout/widgets_bottom_sheet_scrim.xml
+++ b/res/layout/longpress_options_menu.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -13,11 +13,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<com.android.launcher3.graphics.GradientView
+<com.android.launcher3.views.OptionsPopupView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/gradient_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:layout_ignoreInsets="true" />
\ No newline at end of file
+ android:id="@+id/deep_shortcuts_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?attr/popupColorPrimary"
+ android:clipToPadding="false"
+ android:clipChildren="false"
+ android:elevation="@dimen/deep_shortcuts_elevation"
+ android:orientation="vertical" />
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index c795b81..bdd5d23 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -14,61 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.uioverrides.OverviewPanel
+<Space
xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/HomeScreenElementTheme"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:gravity="top"
- android:orientation="horizontal">
-
- <TextView
- android:id="@+id/wallpaper_button"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawablePadding="4dp"
- android:drawableTop="@drawable/ic_wallpaper"
- android:drawableTint="?attr/workspaceTextColor"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:stateListAnimator="@animator/overview_button_anim"
- android:text="@string/wallpaper_button_text"
- android:textAllCaps="true"
- android:textColor="?attr/workspaceTextColor"
- android:textSize="12sp" />
-
- <TextView
- android:id="@+id/widget_button"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawablePadding="4dp"
- android:drawableTop="@drawable/ic_widget"
- android:drawableTint="?attr/workspaceTextColor"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:stateListAnimator="@animator/overview_button_anim"
- android:text="@string/widget_button_text"
- android:textAllCaps="true"
- android:textColor="?attr/workspaceTextColor"
- android:textSize="12sp" />
-
- <TextView
- android:id="@+id/settings_button"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawablePadding="4dp"
- android:drawableTop="@drawable/ic_setting"
- android:drawableTint="?attr/workspaceTextColor"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:stateListAnimator="@animator/overview_button_anim"
- android:text="@string/settings_button_text"
- android:textAllCaps="true"
- android:textColor="?attr/workspaceTextColor"
- android:textSize="12sp" />
-
-</com.android.launcher3.uioverrides.OverviewPanel>
\ No newline at end of file
+ android:layout_width="0dp"
+ android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/layout/search_container_all_apps.xml b/res/layout/search_container_all_apps.xml
index fc07002..14d7b53 100644
--- a/res/layout/search_container_all_apps.xml
+++ b/res/layout/search_container_all_apps.xml
@@ -17,53 +17,23 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@id/search_container_all_apps"
android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_height"
- android:layout_gravity="center|top"
- android:layout_marginBottom="-8dp"
- android:gravity="center|bottom"
- android:paddingLeft="@dimen/dynamic_grid_edge_margin"
- android:paddingRight="@dimen/dynamic_grid_edge_margin"
- android:saveEnabled="false" >
-
- <!--
- Note: The following relation should always be true so that the shadows are aligned properly
- search_box_input.layout_marginBottom
- == search_divider.layout_marginBottom
- == - (search_container.layout_marginBottom)
- >= 5dp
- -->
- <com.android.launcher3.ExtendedEditText
- android:id="@+id/search_box_input"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_field_height"
- android:layout_gravity="bottom"
- android:layout_marginBottom="8dp"
- android:background="@android:color/transparent"
- android:focusableInTouchMode="true"
- android:gravity="center"
- android:hint="@string/all_apps_search_bar_hint"
- android:imeOptions="actionSearch|flagNoExtractUi"
- android:inputType="text|textNoSuggestions|textCapWords"
- android:maxLines="1"
- android:saveEnabled="false"
- android:scrollHorizontally="true"
- android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
- android:textColorHint="@drawable/all_apps_search_hint"
- android:textSize="16sp" />
-
- <!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
- We translate the header down, and its content up by the same amount, so that it gets
- clipped by the parent, making it look like the divider was scrolled out of the view. -->
- <FrameLayout
- android:id="@+id/search_divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_gravity="bottom"
- android:layout_marginBottom="8dp" >
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@drawable/all_apps_search_divider"/>
- </FrameLayout>
-</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
\ No newline at end of file
+ android:layout_height="@dimen/all_apps_search_bar_field_height"
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="top|center_horizontal"
+ android:layout_marginTop="8dp"
+ android:background="@drawable/bg_all_apps_searchbox"
+ android:elevation="1dp"
+ android:focusableInTouchMode="true"
+ android:gravity="center"
+ android:hint="@string/all_apps_search_bar_hint"
+ android:imeOptions="actionSearch|flagNoExtractUi"
+ android:inputType="text|textNoSuggestions|textCapWords"
+ android:maxLines="1"
+ android:padding="8dp"
+ android:saveEnabled="false"
+ android:scrollHorizontally="true"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textColorHint="@drawable/all_apps_search_hint"
+ android:textSize="16sp"
+ android:translationY="24dp" />
\ No newline at end of file
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index 1888e22..04f3d02 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -34,7 +34,6 @@
android:fontFamily="sans-serif"
launcher:iconDisplay="shortcut_popup"
launcher:layoutHorizontal="true"
- launcher:deferShadowGeneration="true"
android:focusable="false" />
<View
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index e8c6961..6bf9048 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="28dp"
- android:background="?android:attr/colorPrimary"
+ android:background="@drawable/top_round_rect_primary"
android:elevation="@dimen/deep_shortcuts_elevation"
android:layout_gravity="bottom"
android:theme="?attr/widgetsTheme">
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 1535299..f507a88 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -20,12 +20,7 @@
android:orientation="vertical"
android:theme="?attr/widgetsTheme" >
- <com.android.launcher3.graphics.GradientView
- android:id="@+id/gradient_bg"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
-
- <FrameLayout
+ <com.android.launcher3.views.TopRoundedCornerView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -36,29 +31,22 @@
android:id="@+id/widgets_list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:clipToPadding="false"
- />
+ android:clipToPadding="false" />
<!-- Fast scroller popup -->
<TextView
android:id="@+id/fast_scroller_popup"
style="@style/FastScrollerPopup"
- android:layout_gravity="top|end"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
<com.android.launcher3.views.RecyclerViewFastScroller
android:id="@+id/fast_scroller"
android:layout_width="@dimen/fastscroll_width"
android:layout_height="match_parent"
- android:layout_gravity="end"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/fastscroll_end_margin" />
-
- <View
- android:id="@+id/nav_bar_bg"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_gravity="bottom"
- android:background="?attr/allAppsNavBarScrimColor"
- android:focusable="false" />
- </FrameLayout>
+ </com.android.launcher3.views.TopRoundedCornerView>
</com.android.launcher3.widget.WidgetsFullSheet>
\ 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 4cd03ce..eec57a5 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -31,7 +31,6 @@
android:layout_height="@dimen/widget_section_height"
android:background="?android:attr/colorPrimary"
android:drawablePadding="@dimen/widget_section_horizontal_padding"
- android:ellipsize="end"
android:focusable="true"
android:gravity="start|center_vertical"
android:paddingBottom="@dimen/widget_section_vertical_padding"
@@ -42,7 +41,6 @@
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
android:textAlignment="viewStart"
- launcher:deferShadowGeneration="true"
launcher:iconDisplay="widget_section"
launcher:iconSizeOverride="@dimen/widget_section_icon_size"
launcher:layoutHorizontal="true" />
diff --git a/res/layout/work_tab_bottom_user_education_view.xml b/res/layout/work_tab_bottom_user_education_view.xml
index 2a4ba5d..ba6a939 100644
--- a/res/layout/work_tab_bottom_user_education_view.xml
+++ b/res/layout/work_tab_bottom_user_education_view.xml
@@ -20,38 +20,50 @@
android:layout_gravity="bottom"
android:background="?android:attr/colorAccent"
android:elevation="2dp"
- android:orientation="horizontal"
- android:paddingLeft="20dp"
- android:paddingRight="20dp">
+ android:focusable="true"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="134dp"
+ android:layout_height="134dp"
+ android:layout_marginTop="28dp"
+ android:layout_marginLeft="20dp"
+ android:src="@drawable/work_tab_user_education"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:orientation="vertical">
<ImageView
- android:layout_width="134dp"
- android:layout_height="134dp"
- android:layout_gravity="center_vertical"
- android:src="@drawable/work_tab_user_education"/>
+ android:id="@+id/close_bottom_user_tip"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginEnd="12dp"
+ android:layout_gravity="right"
+ android:contentDescription="@string/bottom_work_tab_user_education_close_button"
+ android:src="@drawable/ic_close"/>
- <LinearLayout
- android:layout_width="match_parent"
+ <TextView
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingBottom="12dp"
- android:paddingStart="24dp"
- android:paddingTop="12dp">
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="24dp"
+ android:fontFamily="roboto-medium"
+ android:text="@string/bottom_work_tab_user_education_title"
+ android:textColor="@android:color/white"
+ android:textSize="20sp"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fontFamily="roboto-medium"
- android:text="@string/bottom_work_tab_user_education_title"
- android:textColor="@android:color/white"
- android:textSize="20sp"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="24dp"
+ android:text="@string/bottom_work_tab_user_education_body"
+ android:textColor="@android:color/white"
+ android:textSize="14sp"/>
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/bottom_work_tab_user_education_body"
- android:textColor="@android:color/white"
- android:textSize="14sp"/>
- </LinearLayout>
+ </LinearLayout>
</com.android.launcher3.views.BottomUserEducationView>
\ No newline at end of file
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index 21ff55e..379e9d0 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -17,6 +17,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:focusable="true"
android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_bottom_padding"
android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 9d0158b..02dd1c9 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Kon geen programme kry wat by \"<xliff:g id="QUERY">%1$s</xliff:g>\" pas nie"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Soek meer programme"</string>
<string name="notifications_header" msgid="1404149926117359025">"Kennisgewings"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Raak en hou om \'n kortpad op te tel."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dubbeltik en hou om \'n kortpad op te tel of gebruik gepasmaakte handelinge."</string>
<string name="out_of_space" msgid="4691004494942118364">"Niks meer spasie op die tuisskerm nie."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen plek meer in die Gunstelinge-laai nie"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Programmelys"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lys persoonlike programme"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lys werkprogramme"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Tuis"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Verwyder"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> het <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> kennisgewing</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Bladsy %1$d van %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tuisskerm %1$d van %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nuwe tuisskermbladsy"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home-instellings"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Oorsig"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Laat toe dat tuisskerm gedraai word"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer foon gedraai word"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Huidige vertooninstelling laat nie rotasie toe nie"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Kennisgewingkolle"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aan"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Af"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Kennisgewingtoegang word benodig"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Wys kennisgewingkolle"</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="icon_shape_override_label" msgid="2977264953998281004">"Verander ikoon se vorm"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"op tuisskerm"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gebruik stelselverstek"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Sirkelvierkant"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Skep vouer met: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Vouer geskep"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Skuif na tuisskerm"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Skuif skerm na links"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Skuif skerm na regs"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skerm is geskuif"</string>
<string name="action_resize" msgid="1802976324781771067">"Verander grootte"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Vermeerder breedte"</string>
<string name="action_increase_height" msgid="459390020612501122">"Vermeerder hoogte"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Kry werkprogramme hier"</string>
- <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Elke werkprogram het \'n oranje kenteken en word deur jou organisasie veilig gehou. Skuif programme na jou Tuisskerm vir makliker toegang."</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>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 56742d3..21b01dd 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያዎች አለው</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>፣ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ማሳወቂያዎች አለው</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"ገጽ %1$d ከ%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገጽ %1$d ከ%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"አዲስ የመነሻ ማያ ገጽ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
<string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"አጠቃላይ ዕይታ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"የመነሻ ማያ ገጽ ማሽከርከርን ይፍቀዱ"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ስልኩ ሲዞር"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"የአሁኑ የማሳያ ቅንብር ማሽከርከርን አይፈቅድም"</string>
<string name="icon_badging_title" msgid="874121399231955394">"የማሳወቂያ ነጥቦች"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"በርቷል"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ጠፍቷል"</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="icon_badging_service_title" msgid="2309733118428242174">"የማሳወቂያ ነጥቦችን አሳይ"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"አዶ ወደ የመነሻ ማያ ገጽ አክል"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ለአዲስ መተግበሪያዎች"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"የአዶ ቅርፅ ለውጥ"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"በመነሻ ማያ ገጽ ላይ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"የሥርዓቱን ነባሪ ተጠቀም"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ካሬ"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"አቃፊ ፍጠር ከዚህ ጋር፦ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"አቃፊ ተፈጥሮዋል"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ወደ መነሻ ማያ ገጽ አንቀሳቅስ"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"ማያ ገጽን ወደ ግራ አንቀሳቅስ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"ማያ ገጽን ወደ ቀኝ አንቀሳቅስ"</string>
- <string name="screen_moved" msgid="266230079505650577">"ማያ ገጽ ተንቀሳቅሷል"</string>
<string name="action_resize" msgid="1802976324781771067">"መጠን ቀይር"</string>
<string name="action_increase_width" msgid="8773715375078513326">"ስፋት ጨምር"</string>
<string name="action_increase_height" msgid="459390020612501122">"ቁመት ጨምር"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"እያንዳንዱ የሥራ መተግበሪያ ብርቱካናማ ባጅ አለው እና በእርስዎ ድርጅት በኩል ደህንነቱ ተጠብቋል። ለቀለለ መዳረሻ መተግበሪያዎችን ወደ የእርስዎ መነሻ ማያ ገጽ ያንቀሳቅሱ።"</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>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 17075ce..8598a8b 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -33,16 +33,20 @@
<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="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="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_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>
@@ -60,6 +64,14 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="zero"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار</item>
+ <item quantity="two"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به إشعاران (<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>)</item>
+ <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارات</item>
+ <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعارًا</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> إشعار</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>، به <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> إشعار</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"الصفحة %1$d من %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"الشاشة الرئيسية %1$d من %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"صفحة الشاشة الرئيسية الجديدة"</string>
@@ -73,19 +85,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
<string name="settings_button_text" msgid="8873672322605444408">"إعدادات الصفحة الرئيسية"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"عطَّل المشرف هذه الميزة"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"نظرة عامة"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"السماح بتدوير الشاشة الرئيسية"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"عند تدوير الهاتف"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"لا يسمح إعداد العرض الحالي بالتدوير"</string>
<string name="icon_badging_title" msgid="874121399231955394">"نقاط الإشعارات"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"قيد التشغيل"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"قيد الإيقاف"</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="icon_badging_service_title" msgid="2309733118428242174">"عرض نقاط الإشعارات"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"إضافة رمز إلى الشاشة الرئيسية"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"للتطبيقات الجديدة"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"تغيير شكل الرمز"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"على الشاشة الرئيسية"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"استخدام الإعداد الافتراضي للنظام"</string>
<string name="icon_shape_square" msgid="633575066111622774">"مربّع"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"رمز دائري مربّع"</string>
@@ -115,9 +125,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"إنشاء مجلد يتضمن: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"تم إنشاء المجلد"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"نقل إلى الشاشة الرئيسية"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"نقل الشاشة إلى اليسار"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"نقل الشاشة إلى اليمين"</string>
- <string name="screen_moved" msgid="266230079505650577">"تم نقل الشاشة"</string>
<string name="action_resize" msgid="1802976324781771067">"تغيير حجم"</string>
<string name="action_increase_width" msgid="8773715375078513326">"زيادة العرض"</string>
<string name="action_increase_height" msgid="459390020612501122">"زيادة الارتفاع"</string>
@@ -133,7 +140,9 @@
<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="1485375451542813426">"يحتوي كل تطبيق للعمل على شارة برتقالية اللون ويظل تحت حماية مؤسستك. يمكنك نقل التطبيقات إلى شاشتك الرئيسية لتسهيل الوصول."</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>
</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 1e42fe8..a0a1f57 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> sorğusuna uyğun tətbiq tapılmadı"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Daha çox tətbiq üçün axtarış edin"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirişlər"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Qısayolu seçmək üçün basıb saxlayın."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
<string name="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Tətbiq siyahısı"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Şəxsi tətbiqlərin siyahısı"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"İş tətbiqlərinin siyahısı"</string>
<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>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tətbiqində <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildiriş var</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Səhifə %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Əsas Səhifə ekranı %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Yeni əsas ekran səhifəsi"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"İcmal"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon çevrilən zaman"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cari Ekran ayarı fırlatmağa icazə vermir"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Bildiriş nişanı"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiv"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Deaktiv"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Bildiriş girişi tələb edilir"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Bildiriş nöqtələrini göstərin"</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="icon_shape_override_label" msgid="2977264953998281004">"İkona formasını dəyişin"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Əsas səhifə ekranında"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem defoltu istifadə edin"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kənarları dairəvi kvadrat"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Qovluq yaradın: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Qovluq yaradıldı"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Əsas ekrana köçürün"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Ekranı sola köçürün"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Ekranı sağa köçürün"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekran köçürülüb"</string>
<string name="action_resize" msgid="1802976324781771067">"Ölçüsünü dəyişin"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Eni artırın"</string>
<string name="action_increase_height" msgid="459390020612501122">"Hündürlüyü artırın"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</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="1485375451542813426">"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="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>
</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 6fc1968..275cf0f 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži još aplikacija"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obaveštenja"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i zadržite da biste izabrali prečicu."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvaput dodirnite i zadržite da biste izabrali prečicu ili koristite prilagođene radnje."</string>
<string name="out_of_space" msgid="4691004494942118364">"Nema više prostora na ovom početnom ekranu."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Omiljeno"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista 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>
@@ -60,6 +64,11 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obaveštenja</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. stranica od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. početni ekran od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
@@ -73,19 +82,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Podešavanja početnog ekrana"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator je onemogućio"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotaciju početnog ekrana"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon rotira"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuelno podešavanje prikaza ne dozvoljava rotaciju"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Tačke za obaveštenja"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup za obaveštenja"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Prikazuj tačke za obaveštenja"</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="icon_shape_override_label" msgid="2977264953998281004">"Promenite oblik ikona"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na početnom ekranu"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Koristi podrazumevano sistemsko podešavanje"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
@@ -115,9 +122,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Napravite direktorijum sa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Direktorijum je napravljen"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Premesti na početni ekran"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Pomeri ekran ulevo"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Pomeri ekran udesno"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekran je pomeren"</string>
<string name="action_resize" msgid="1802976324781771067">"Promeni veličinu"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Povećaj širinu"</string>
<string name="action_increase_height" msgid="459390020612501122">"Povećaj visinu"</string>
@@ -133,7 +137,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</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="1485375451542813426">"Svaka poslovna aplikacija ima narandžastu značku i štiti je vaša organizacija. Premestite aplikacije na početni ekran da biste im lakše pristupali."</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>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index f9e73ca..a1f1d89 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -40,9 +40,13 @@
<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="notifications_header" msgid="1404149926117359025">"Апавяшчэнні"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго."</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_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>
@@ -60,6 +64,12 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнне</item>
+ <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнні</item>
+ <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэнняў</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: ёсць <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> апавяшчэння</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Старонка %1$d з %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Галоўны экран %1$d з %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Новая старонка галоўнага экрана"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Агляд"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Пры павароце тэлефона"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Бягучая налада дысплэя не прадугледжвае паварот"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Значкі апавяшчэнняў"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Уключана"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Выключана"</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="icon_badging_service_title" msgid="2309733118428242174">"Паказаць значкі апавяшчэнняў"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Дадаць значок на Галоўны экран"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для новых праграм"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Змяніць форму значка"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на галоўным экране"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Выкарыстоўваць стандартныя формы"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Прамавугольнік са скругленымі вугламі"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Стварыць папку з: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Папка створана"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Перамясціць на Галоўны экран"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Перамясціць экран налева"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Перамясціць экран направа"</string>
- <string name="screen_moved" msgid="266230079505650577">"Экран перамешчаны"</string>
<string name="action_resize" msgid="1802976324781771067">"Змяніць памер"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Павялічыць шырыню"</string>
<string name="action_increase_height" msgid="459390020612501122">"Павялічыць вышыню"</string>
@@ -133,7 +138,9 @@
<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="1485375451542813426">"Кожная працоўная праграма мае аранжавы значок і знаходзіцца пад аховай вашай арганізацыі. Для больш простага доступу перамясціце праграмы на свой Галоўны экран."</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>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 86a79b3..3c04905 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известия</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – има <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> известие</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d от %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Начален екран %1$d от %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова страница на началния екран"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Настройки за Google Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Общ преглед"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешаване на завъртането на началния екран"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"При завъртане на телефона"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Текущата настройка на екрана не разрешава завъртане"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Точки за известия"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Включено"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Изключено"</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="icon_badging_service_title" msgid="2309733118428242174">"Показване на точките за известия"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавяне на икона към началния екран"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови приложения"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Промяна на формата на иконите"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на началния екран"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Използване на стандартната системна настройка"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Комбинация от кръг и квадрат"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Създаване на папка с елемента „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="folder_created" msgid="6409794597405184510">"Папката е създадена"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Преместване към началния екран"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Преместване на екрана наляво"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Преместване на екрана надясно"</string>
- <string name="screen_moved" msgid="266230079505650577">"Екранът е преместен"</string>
<string name="action_resize" msgid="1802976324781771067">"Преоразмеряване"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Увеличаване на ширината"</string>
<string name="action_increase_height" msgid="459390020612501122">"Увеличаване на височината"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Всяко служебно приложение има оранжева значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран."</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>
</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index d7f61eb..a0e4812 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> এ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> এ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>টি বিজ্ঞপ্তি আছে</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রিন"</string>
<string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
<string name="settings_button_text" msgid="8873672322605444408">"হোম এর সেটিংস"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"এক নজরে"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"হোমস্ক্রীন ঘোরানোর অনুমতি দিন"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"যখন ফোনটি ঘোরানো হয়"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"বর্তমান প্রদর্শনের সেটিংস ঘোরানোর মঞ্জুরি দেয় না"</string>
<string name="icon_badging_title" msgid="874121399231955394">"বিজ্ঞপ্তি ডট"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"চালু হয়েছে"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"বন্ধ আছে"</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="icon_badging_service_title" msgid="2309733118428242174">"বিজ্ঞপ্তির ডট দেখুন"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"হোম স্ক্রিনে আইকন যোগ করুন"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপ্লিকেশানগুলির জন্যে"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"আইকনের আকৃতি পরিবর্তন করুন"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"হোম স্ক্রিনে"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"সিস্টেমের ডিফল্ট মান ব্যবহার করুন"</string>
<string name="icon_shape_square" msgid="633575066111622774">"চৌকো"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"চৌকো-গোলাকার"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"এর সাথে ফোল্ডার তৈরি করুন: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ফোল্ডার তৈরি করা হয়েছে"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"হোম স্ক্রীনে সরান"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"স্ক্রীন বাঁ দিকে সরান"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"স্ক্রীন ডান দিকে সরান"</string>
- <string name="screen_moved" msgid="266230079505650577">"স্ক্রীন সরানো হয়েছে"</string>
<string name="action_resize" msgid="1802976324781771067">"আবার আকার দিন"</string>
<string name="action_increase_width" msgid="8773715375078513326">"প্রস্থ বাড়ান"</string>
<string name="action_increase_height" msgid="459390020612501122">"উচ্চতা বাড়ান"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"প্রতিটি কাজের অ্যাপে একটি করে কমলা ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।"</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>
</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index fbbab0e..08a4533 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nije pronađena nijedna aplikacija za upit \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i držite da uzmete prečicu."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvaput dodirnite 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 Favoriti"</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">"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>
@@ -60,6 +64,11 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenje</item>
+ <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavještenja</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Početni ekran %1$d od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
@@ -73,19 +82,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Postavke za Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutne postavke ekrana ne dozvoljavaju rotiranje"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Tačke za obavještenja"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup obavještenjima"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Za prikaz tačaka 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="icon_badging_service_title" msgid="2309733118428242174">"Prikaži tačke za obavještenja"</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="icon_shape_override_label" msgid="2977264953998281004">"Promjena oblika ikona"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na Početnom ekranu"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Koristite sistemski zadano"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
@@ -107,7 +114,7 @@
<string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
<string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
<string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
- <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među favoritima"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među omiljenim"</string>
<string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</string>
<string name="add_to_folder" msgid="9040534766770853243">"Dodaj u folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="add_to_folder_with_app" msgid="4534929978967147231">"Dodaj u folder sa aplikacijom <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -115,9 +122,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Kreirajte folder sa stavkom: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Folder je kreiran"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Pomjeri na početni ekran"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Pomjeri ekran ulijevo"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Pomjeri ekran udesno"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekran je pomjeren"</string>
<string name="action_resize" msgid="1802976324781771067">"Promijeni veličinu"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Povećaj širinu"</string>
<string name="action_increase_height" msgid="459390020612501122">"Povećaj visinu"</string>
@@ -133,7 +137,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</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="1485375451542813426">"Svaka poslovna aplikacija ima narandžastu značku i osigurava je vaša organizacija. Premjestite aplikacije na Početni ekran, radi lakšeg pristupa."</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>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 793b1c7..27b2979 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No s\'ha trobat cap aplicació que coincideixi amb \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca més aplicacions"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificacions"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén premuda una drecera per seleccionar-la."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Fes doble toc i mantén premut per seleccionar una drecera o per utilitzar accions personalitzades."</string>
<string name="out_of_space" msgid="4691004494942118364">"Ja no queda espai en aquesta pantalla d\'inici."</string>
<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 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>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> té <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificació</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Pàgina %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla d\'inici %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Pàgina de la pantalla d\'inici nova"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Configuració de la pantalla d\'inici"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Visió general"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Permet la rotació de la pantalla d\'inici"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuració actual de la pantalla no permet la rotació"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Punts de notificació"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activat"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivat"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Cal que tingui accés a les notificacions"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Mostra els punts de notificació"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Afegeix la icona a la pantalla d\'inici"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per a les aplicacions noves"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Canvia la forma de les icones"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"a la pantalla d\'inici"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utilitza l\'opció predeterminada del sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrat arrodonit"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Crea una carpeta amb: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Carpeta creada"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Desplaça a la pantalla d\'inici"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Desplaça pantalla a l\'esquerra"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Desplaça pantalla a la dreta"</string>
- <string name="screen_moved" msgid="266230079505650577">"S\'ha desplaçat la pantalla"</string>
<string name="action_resize" msgid="1802976324781771067">"Canvia la mida"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Augmenta l\'amplada"</string>
<string name="action_increase_height" msgid="459390020612501122">"Augmenta l\'alçada"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Totes les aplicacions per a la feina tenen una insígnia taronja 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="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>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 0d73f82..eee945c 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Vyhledat další aplikace"</string>
<string name="notifications_header" msgid="1404149926117359025">"Oznámení"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Zkratku vyberete podržením."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvojitým klepnutím a podržením vyberte zkratku, případně použijte vlastní akce."</string>
<string name="out_of_space" msgid="4691004494942118364">"Na této ploše již není místo."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na panelu Oblíbené položky již není místo."</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Seznam aplikací"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Seznam osobních aplikací"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Seznam pracovních aplikací"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Plocha"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Odstranit"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
@@ -60,6 +64,12 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="many">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>
+ <item quantity="other">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>
+ <item quantity="one">Aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> oznámení</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Přehled"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Povolit otáčení plochy"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Při otočení telefonu"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuální nastavení displeje neumožňuje otáčení"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Puntíky s oznámením"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Zapnuto"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Vypnuto"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Je třeba udělit přístup k oznámením"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Zobrazovat puntíky s oznámením"</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="icon_shape_override_label" msgid="2977264953998281004">"Změnit tvar ikony"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na ploše"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Použít výchozí nastavení systému"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Čtverec"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kruh/čtverec"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Vytvořit složku s položkou <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Složka byla vytvořena"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Přesunout na plochu"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Přesunout obrazovku doleva"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Přesunout obrazovku doprava"</string>
- <string name="screen_moved" msgid="266230079505650577">"Obrazovka byla přesunuta"</string>
<string name="action_resize" msgid="1802976324781771067">"Změnit velikost"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Zvýšit šířku"</string>
<string name="action_increase_height" msgid="459390020612501122">"Zvýšit výšku"</string>
@@ -133,7 +138,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</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="1485375451542813426">"Každý pracovní profil má oranžový odznak a je zabezpečen vaší organizací. Aplikace si můžete pro jednoduchost přesunout na plochu."</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>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 27d615a..203a9da 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Søg efter flere apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Underretninger"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Hold en genvej nede for at samle den op."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tryk to gange, og hold en genvej nede for at samle den op eller bruge tilpassede handlinger."</string>
<string name="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Foretrukne"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Liste med apps"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste over personlige apps"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste over apps til arbejdet"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startskærm"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> underretning</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> underretninger</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ny startskærm"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærmen"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Oversigt"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Den aktuelle indstilling for visning tillader ikke rotation"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Underretningscirkler"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Til"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Fra"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Kræver adgang til underretninger"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Hvis du vil se underretningscirkler, skal du aktivere appunderretninger for <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Skift indstillinger"</string>
+ <string name="icon_badging_service_title" msgid="2309733118428242174">"Vis underretningscirkler"</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="icon_shape_override_label" msgid="2977264953998281004">"Skift ikonform"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"på startskærmen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Brug systemstandarden"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrat med runde hjørner"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Opret mappe med: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mappen blev oprettet"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Flyt til startskærmen"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Flyt skærmen til venstre"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Flyt skærmen til højre"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skærmen er flyttet"</string>
<string name="action_resize" msgid="1802976324781771067">"Tilpas størrelse"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Øg bredden"</string>
<string name="action_increase_height" msgid="459390020612501122">"Øg højden"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find arbejdsapps her"</string>
- <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Alle arbejdsapps har et orange badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem."</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">"Underretninger 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>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8fec587..f012a9e 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Weitere Apps suchen"</string>
<string name="notifications_header" msgid="1404149926117359025">"Benachrichtigungen"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tippen und halten, um eine Verknüpfung auszuwählen."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Doppeltippen und halten, um eine Verknüpfung auszuwählen oder benutzerdefinierte Aktionen zu nutzen."</string>
<string name="out_of_space" msgid="4691004494942118364">"Auf diesem Startbildschirm ist kein Platz mehr vorhanden."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ablage \"Favoriten\" ist voll."</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Liste der Apps"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste der privaten Apps"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste der geschäftlichen Apps"</string>
<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>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, hat <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> Benachrichtigung</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Seite %1$d von %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startbildschirm %1$d von %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Neue Startbildschirmseite"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Einstellungen für den Startbildschirm"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Übersicht"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Drehung des Startbildschirms zulassen"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Bei Drehung des Smartphones"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Die aktuelle \"Display\"-Einstellung verhindert eine Drehung der Anzeige"</string>
<string name="icon_badging_title" msgid="874121399231955394">"App-Benachrichtigungspunkte"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiviert"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Deaktiviert"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Benachrichtigungszugriff erforderlich"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"App-Benachrichtigungspunkte anzeigen"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Symbol zu Startbildschirm hinzufügen"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Für neue Apps"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Form des Symbols ändern"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"auf dem Startbildschirm"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Systemstandardeinstellung verwenden"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Superkreis"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Anhand von <xliff:g id="NAME">%1$s</xliff:g> Ordner erstellen"</string>
<string name="folder_created" msgid="6409794597405184510">"Ordner erstellt"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Auf Startbildschirm verschieben"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Bildschirm nach links"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Bildschirm nach rechts"</string>
- <string name="screen_moved" msgid="266230079505650577">"Bildschirm verschoben"</string>
<string name="action_resize" msgid="1802976324781771067">"Größe anpassen"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Breite vergrößern"</string>
<string name="action_increase_height" msgid="459390020612501122">"Höhe vergrößern"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</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="1485375451542813426">"Jede App für die Arbeit hat ein orangefarbenes Logo. Deine Organisation kümmert sich um den entsprechenden Schutz. Damit du leichter auf Apps zugreifen kannst, verschiebe sie auf deinen Startbildschirm."</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>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index a78b820..18eec84 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other">Η εφαρμογή <xliff:g id="APP_NAME_2">%1$s</xliff:g> έχει <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ειδοποιήσεις</item>
+ <item quantity="one">Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g> έχει <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ειδοποίηση</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Σελίδα %1$d από %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Αρχική οθόνη %1$d από %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Νέα σελίδα αρχικής οθόνης"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχικής σελίδας"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Επισκόπηση"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Όταν το τηλέφωνο περιστρέφεται"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Η τρέχουσα ρύθμιση οθόνης δεν επιτρέπει την περιστροφή"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Κουκκίδες ειδοποίησης"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Ενεργή"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Ανενεργή"</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="icon_badging_service_title" msgid="2309733118428242174">"Εμφάνιση κουκκίδων ειδοποίησης"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Προσθήκη εικονιδίου στην Αρχική οθόνη"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Για νέες εφαρμογές"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Αλλαγή σχήματος εικονιδίου"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"στην Αρχική οθόνη"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Χρήση προεπιλογής συστήματος"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Τετράγωνο"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Στρογγυλεμένο τετράγωνο"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Δημιουργία φακέλου με: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Δημιουργήθηκε φάκελος"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Μετακίνηση Αρχικής οθόνης"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Μετακίνηση οθόνης αριστερά"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Μετακίνηση οθόνης δεξιά"</string>
- <string name="screen_moved" msgid="266230079505650577">"Η οθόνη μετακινήθηκε"</string>
<string name="action_resize" msgid="1802976324781771067">"Προσαρμογή μεγέθους"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Αύξηση του πλάτους"</string>
<string name="action_increase_height" msgid="459390020612501122">"Αύξηση του ύψους"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Κάθε εφαρμογή εργασίας φέρει ένα πορτοκαλί σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση."</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>
</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 11389d4..c2e37b8 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -40,9 +40,13 @@
<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="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>
@@ -60,6 +64,10 @@
<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="6617836969016293992">"Unnamed Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"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="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Notification dots"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"On"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"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="icon_badging_service_title" msgid="2309733118428242174">"Show notification dots"</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="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"on Home screen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<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_move_screen_left" msgid="8854216831569401665">"Move screen to left"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Move screen to right"</string>
- <string name="screen_moved" msgid="266230079505650577">"Screen moved"</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>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</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="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</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>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 11389d4..c2e37b8 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -40,9 +40,13 @@
<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="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>
@@ -60,6 +64,10 @@
<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="6617836969016293992">"Unnamed Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"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="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Notification dots"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"On"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"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="icon_badging_service_title" msgid="2309733118428242174">"Show notification dots"</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="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"on Home screen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<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_move_screen_left" msgid="8854216831569401665">"Move screen to left"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Move screen to right"</string>
- <string name="screen_moved" msgid="266230079505650577">"Screen moved"</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>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</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="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</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>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 11389d4..c2e37b8 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -40,9 +40,13 @@
<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="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>
@@ -60,6 +64,10 @@
<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="6617836969016293992">"Unnamed Folder"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"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="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Notification dots"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"On"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"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="icon_badging_service_title" msgid="2309733118428242174">"Show notification dots"</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="icon_shape_override_label" msgid="2977264953998281004">"Change icon shape"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"on Home screen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<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_move_screen_left" msgid="8854216831569401665">"Move screen to left"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Move screen to right"</string>
- <string name="screen_moved" msgid="266230079505650577">"Screen moved"</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>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</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="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</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>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 5a13087..989cd61 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No hay apps que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén presionado para elegir un acceso directo."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Presiona dos veces y mantén presionado para elegir un acceso directo o usar acciones personalizadas."</string>
<string name="out_of_space" msgid="4691004494942118364">"No hay más espacio en esta pantalla principal."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está llena."</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista de apps"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de apps personales"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de apps del trabajo"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Pantalla principal"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla principal %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nueva página en la pantalla principal"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Configuración de Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Recientes"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración actual no permite la rotación de la pantalla"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificación"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activada"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivada"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Se necesita acceso a las notificaciones"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar puntos de notificación"</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="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los íconos"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"en la pantalla principal"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar el sistema predeterminado"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Crear carpeta con: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Carpeta creada"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Mover a la pantalla principal"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Mover pantalla a la izquierda"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Mover pantalla a la derecha"</string>
- <string name="screen_moved" msgid="266230079505650577">"Se movió la pantalla."</string>
<string name="action_resize" msgid="1802976324781771067">"Ajustar tamaño"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Aumentar el ancho"</string>
<string name="action_increase_height" msgid="459390020612501122">"Aumentar la altura"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Laborales"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Apps de trabajo"</string>
- <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Cada app de trabajo tiene una insignia naranja y está protegida por tu organización. Mueve las apps a la pantalla principal para acceder a ellas con mayor facilidad."</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>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 1f1e3b7..5b948da 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más aplicaciones"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificaciones"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén pulsado para elegir un acceso directo."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toca dos veces y mantén pulsado para elegir un acceso directo o utilizar acciones personalizadas."</string>
<string name="out_of_space" msgid="4691004494942118364">"No queda espacio en la pantalla de inicio."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está completa"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicaciones"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicaciones personales"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicaciones del trabajo"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> tiene <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nueva página de pantalla de inicio"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ajustes de Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión general"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotación de la pantalla de inicio"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración de pantalla actual no permite girar la pantalla"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificación"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activada"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivada"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Se necesita acceso a las notificaciones"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Para mostrar burbujas 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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar burbujas de notificación"</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="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma de los iconos"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"en la pantalla de inicio"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar opción predeterminada del sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Crear carpeta con: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Carpeta creada"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Mover a la pantalla de inicio"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Mover pantalla a la izquierda"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Mover pantalla a la derecha"</string>
- <string name="screen_moved" msgid="266230079505650577">"Pantalla movida"</string>
<string name="action_resize" msgid="1802976324781771067">"Modificar tamaño"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Aumentar ancho"</string>
<string name="action_increase_height" msgid="459390020612501122">"Aumentar altura"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil 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="1485375451542813426">"Cada aplicación de trabajo tiene una insignia naranja 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="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>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 162da73..2946076 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Otsi rohkem rakendusi"</string>
<string name="notifications_header" msgid="1404149926117359025">"Märguanded"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Otsetee valimiseks puudutage seda pikalt."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks."</string>
<string name="out_of_space" msgid="4691004494942118364">"Sellel avaekraanil pole enam ruumi."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Rakenduste loend"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Isiklike rakenduste loend"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Töörakenduste loend"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Avaekraan"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> märguanne</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Leht %1$d/%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Avaekraan %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Uus avaekraan"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Avalehe seaded"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Ülevaade"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Praegune kuvaseade ei luba pööramist"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Märguandetäpid"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Sees"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Väljas"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Vaja on juurdepääsu märguannetele"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Kuva märguandetäpid"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisa ikoon avaekraanile"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uute rakenduste puhul"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Ikooni kuju muutmine"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"avaekraanil"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Kasuta süsteemi vaikeseadet"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Ruut"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Ümarate nurkadega ruut"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Kausta loomine nimega <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Kaust on loodud"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Teisalda avaekraanile"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Teisalda ekraan vasakule"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Teisalda ekraan paremale"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekraan teisaldati"</string>
<string name="action_resize" msgid="1802976324781771067">"Muuda suurust"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Suurenda laiust"</string>
<string name="action_increase_height" msgid="459390020612501122">"Suurenda kõrgust"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</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="1485375451542813426">"Igal töörakendusel on oranž märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda."</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>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 3f137e4..c3099bd 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketaren emaitzarik"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Bilatu aplikazio gehiago"</string>
<string name="notifications_header" msgid="1404149926117359025">"Jakinarazpenak"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Eduki sakatuta lasterbide bat aukeratzeko."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
<string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Aplikazioen zerrenda"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Aplikazio pertsonalen zerrenda"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Laneko aplikazioen zerrenda"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Hasiera"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Kendu"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> jakinarazpen dauzka <xliff:g id="APP_NAME_2">%1$s</xliff:g> aplikazioak</item>
+ <item quantity="one"><xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> jakinarazpen dauka <xliff:g id="APP_NAME_0">%1$s</xliff:g> aplikazioak</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$d orria"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d/%2$d hasierako pantaila"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Hasierako pantailaren orri berria"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"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="accessibility_action_overview" msgid="6257665857640347026">"Ikuspegi orokorra"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzen denean"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Uneko pantaila-ezarpenak ez du onartzen ikuspegia biratzea"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Jakinarazteko biribiltxoak"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktibatuta"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desaktibatuta"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Jakinarazpenetarako sarbidea behar da"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Jakinarazteko 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="icon_badging_service_title" msgid="2309733118428242174">"Erakutsi jakinarazteko biribiltxoak"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrietan"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Aldatu ikonoaren forma"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Hasierako pantailan"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Erabili sistemaren balio lehenetsiak"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Karratua"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Ertz biribilduko karratua"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Sortu karpeta <xliff:g id="NAME">%1$s</xliff:g> elementuarekin"</string>
<string name="folder_created" msgid="6409794597405184510">"Karpeta sortu da"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Eraman hasierako pantailara"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Eraman pantaila ezkerrera"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Eraman pantaila eskuinera"</string>
- <string name="screen_moved" msgid="266230079505650577">"Mugitu da pantaila"</string>
<string name="action_resize" msgid="1802976324781771067">"Aldatu tamaina"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Handitu zabalera"</string>
<string name="action_increase_height" msgid="459390020612501122">"Handitu altuera"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</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="1485375451542813426">"Laneko aplikazio bakoitzak bereizgarri laranja bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara."</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>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index f4b000d..4843d6d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اعلان دارد</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"صفحه %1$d از %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"صفحه اصلی %1$d از %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"صفحه اصلی جدید"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواریها"</string>
<string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"نمای کلی"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"امکان دادن به چرخش صفحه اصلی"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"وقتی تلفن چرخانده میشود"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"تنظیم نمایشگر کنونی اجازه چرخش نمیدهد"</string>
<string name="icon_badging_title" msgid="874121399231955394">"نقطههای اعلان"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"روشن"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"خاموش"</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="icon_badging_service_title" msgid="2309733118428242174">"نمایش نقطههای اعلان"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"افزودن نماد به صفحه اصلی"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"برای برنامههای جدید"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"تغییر شکل نماد"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"در صفحه اصلی"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"استفاده از پیشفرض سیستم"</string>
<string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"مربع با گوشههای گرد"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ایجاد پوشه با: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"پوشه ایجاد شد"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"انتقال به صفحه اصلی"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"انتقال صفحه به چپ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"انتقال صفحه به راست"</string>
- <string name="screen_moved" msgid="266230079505650577">"صفحه منتقل شد"</string>
<string name="action_resize" msgid="1802976324781771067">"تغییر اندازه"</string>
<string name="action_increase_width" msgid="8773715375078513326">"افزایش عرض"</string>
<string name="action_increase_height" msgid="459390020612501122">"افزایش ارتفاع"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"همه برنامههای کاری نشان نارنجیرنگی دارند و توسط سازمان شما امن نگه داشته میشود. برنامههای کاری را برای دسترسی آسانتر به صفحه اصلی انتقال دهید."</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>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 6146ef9..5ffc414 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"<xliff:g id="QUERY">%1$s</xliff:g> ei palauttanut sovelluksia."</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Hae lisää sovelluksia"</string>
<string name="notifications_header" msgid="1404149926117359025">"Ilmoitukset"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Valitse pikakuvake painamalla sitä pitkään."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Valitse pikakuvake tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla pitkään."</string>
<string name="out_of_space" msgid="4691004494942118364">"Tässä aloitusruudussa ei ole enää tilaa."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Sovellusluettelo"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Omat sovellukset ‑luettelo"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Työsovellusluettelo"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Aloitusruutu"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Poista"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ilmoitus</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Sivu %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Aloitusruutu %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Uusi aloitusnäytön sivu"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</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="accessibility_action_overview" msgid="6257665857640347026">"Yleiskatsaus"</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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Nykyiset näyttöasetukset eivät salli näytön kiertämistä."</string>
<string name="icon_badging_title" msgid="874121399231955394">"Pistemerkit"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Käytössä"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Pois käytöstä"</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="icon_badging_service_title" msgid="2309733118428242174">"Näytä ilmoituksista kertovat pistemerkit"</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="icon_shape_override_label" msgid="2977264953998281004">"Muuta kuvakkeen muotoa"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"aloitusnäytöllä"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Käytä järjestelmän oletusarvoa"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Neliö"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Ympyräneliö"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Luo kansio, jossa on <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="folder_created" msgid="6409794597405184510">"Kansio on luotu."</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Siirrä aloitusnäytölle"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Siirrä näyttöä vasemmalle"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Siirrä näyttöä oikealle"</string>
- <string name="screen_moved" msgid="266230079505650577">"Näyttö siirrettiin."</string>
<string name="action_resize" msgid="1802976324781771067">"Muuta kokoa"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Lisää leveyttä"</string>
<string name="action_increase_height" msgid="459390020612501122">"Lisää korkeutta"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Työsovellukset"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Työprofiili"</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="1485375451542813426">"Kaikissa työsovelluksissa on oranssi merkki ja ne ovat organisaatiosi suojaamia. Voit siirtää työsovelluksia aloitusnäytölle käytön helpottamiseksi."</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>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 43bcc5c..d6903ef 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Maintenez un doigt sur le raccourci pour l\'ajouter"</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Touchez 2x un raccourci et maintenez doigt dessus pour l’aj. ou utiliser des actions personnalisées."</string>
<string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur l\'écran d\'accueil."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Il n\'y a plus d\'espace dans la zone des favoris"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Liste des applications"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste des applications personnelles"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste des applications professionnelles"</string>
<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>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> a <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Présentation"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Le mode d\'affichage actuel ne permet pas le pivotement"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Points de notification"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activé"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Désactivé"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"L\'accès aux notifications est requis"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Afficher les points de notification"</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="icon_shape_override_label" msgid="2977264953998281004">"Modifier la forme de l\'icône"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sur l\'écran d\'accueil"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser les valeurs système par défaut"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Carré aux coins ronds"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Créer un dossier avec : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Dossier créé"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Déplacer sur l\'écran d\'accueil"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Déplacer l\'écran à gauche"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Déplacer l\'écran à droite"</string>
- <string name="screen_moved" msgid="266230079505650577">"Écran déplacé"</string>
<string name="action_resize" msgid="1802976324781771067">"Redimensionner"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Augmenter la largeur"</string>
<string name="action_increase_height" msgid="459390020612501122">"Augmenter la hauteur"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Travail"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</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="1485375451542813426">"Chaque application professionnelle comporte un badge orange, 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="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>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 8994ef4..e1b7581 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifications"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Appui prolongé pour sélectionner un raccourci."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Appuyez 2X et maintenez la pression pour choisir un raccourci ou utilisez les actions personnalisées"</string>
<string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Liste d\'applications"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Liste des applications personnelles"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Liste des applications professionnelles"</string>
<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>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> comporte <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notifications</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Paramètres de l\'écran d\'accueil"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Vue d\'ensemble"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Le paramètre d\'affichage actuel n\'autorise pas la rotation."</string>
<string name="icon_badging_title" msgid="874121399231955394">"Pastilles de notification"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activé"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Désactivé"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Accès aux notifications requis"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Afficher les pastilles de notification"</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="icon_shape_override_label" msgid="2977264953998281004">"Modifier la forme des icônes"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sur l\'écran d\'accueil"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser la valeur système par défaut"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Créer un dossier avec \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="folder_created" msgid="6409794597405184510">"Dossier créé"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Déplacer vers l\'écran d\'accueil"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Déplacer l\'écran vers la gauche"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Déplacer l\'écran vers la droite"</string>
- <string name="screen_moved" msgid="266230079505650577">"L\'écran a bien été déplacé."</string>
<string name="action_resize" msgid="1802976324781771067">"Redimensionner"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Augmenter la largeur"</string>
<string name="action_increase_height" msgid="459390020612501122">"Augmenter la hauteur"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnelles"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</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="1485375451542813426">"Les applications professionnelles sont accompagnées d\'un badge orange 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="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">"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>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index f7b433b..0cfdfa8 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar máis aplicacións"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificacións"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Mantén premido un atallo para seleccionalo."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas."</string>
<string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicacións"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicacións persoais"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicacións de traballo"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ten <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notificacións</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ten <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificación</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova páxina da pantalla de inicio"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Configuración de inicio"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión xeral"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuración de visualización actual non permite xirar a pantalla"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Puntos de notificacións"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activado"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivado"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Necesítase acceso ás notificacións"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar puntos de notificación"</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="icon_shape_override_label" msgid="2977264953998281004">"Cambiar forma das iconas"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na pantalla de inicio"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados do sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Cadrado"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Cadrado de bordos redondeados"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Crear cartafol con: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Creouse o cartafol"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Mover á pantalla de inicio"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Mover pantalla á esquerda"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Mover pantalla á dereita"</string>
- <string name="screen_moved" msgid="266230079505650577">"Moveuse a pantalla"</string>
<string name="action_resize" msgid="1802976324781771067">"Cambiar tamaño"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Aumentar ancho"</string>
<string name="action_increase_height" msgid="459390020612501122">"Aumentar altura"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de 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="1485375451542813426">"As aplicacións do traballo teñen unha insignia laranxa 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="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>
</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index c292826..efc953c 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>ના <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> નોટિફિકેશન છે</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
<string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ્સ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"ઝલક"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી"</string>
<string name="icon_badging_title" msgid="874121399231955394">"સૂચના બિંદુઓ"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ચાલુ"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"બંધ"</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="icon_badging_service_title" msgid="2309733118428242174">"નોટિફિકેશન માટેનું ચિહ્ન બતાવો"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"હોમ સ્ક્રીન પર આઇકન ઉમેરો"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"નવી ઍપ્લિકેશનો માટે"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"આઇકનનો આકાર બદલો"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"હોમ સ્ક્રીન પર"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"સિસ્ટમ ડિફૉલ્ટનો ઉપયોગ કરો"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ચોરસ"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ચોરસ જેવું ગોળ"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"આની સાથે ફોલ્ડર બનાવો: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ફોલ્ડર બનાવ્યું"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"હોમ સ્ક્રીન પર ખસેડો"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"સ્ક્રીનને ડાબી બાજુ ખસેડો"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"સ્ક્રીનને જમણી બાજુ ખસેડો"</string>
- <string name="screen_moved" msgid="266230079505650577">"સ્ક્રીન ખસેડી"</string>
<string name="action_resize" msgid="1802976324781771067">"આકાર બદલો"</string>
<string name="action_increase_width" msgid="8773715375078513326">"પહોળાઈ વધારો"</string>
<string name="action_increase_height" msgid="459390020612501122">"ઊંચાઈ વધારો"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"દરેક કાર્ય ઍપ પાસે એક નારંગી બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો."</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>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5282a93..f98859d 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाएं हैं</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> की <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाएं हैं</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"पेज %2$d में से %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रीन %2$d में से %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"नया होम स्क्रीन पेज"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम पेज की सेटिंग"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके एडमिन ने बंद किया हुआ है"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"खास जानकारी"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"होमस्क्रीन घुमाने की अनुमति दें"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"फ़ोन घुुमाए जाने पर"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"इस डिसप्ले सेटिंग में रोटेशन (स्क्रीन पर सामग्री को घुमाकर देखने) की सुविधा काम नहीं करती"</string>
<string name="icon_badging_title" msgid="874121399231955394">"सूचना बिंदु"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"चालू"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"बंद"</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="icon_badging_service_title" msgid="2309733118428242174">"नए नोटिफ़िकेशन बताने वाला गोल निशान दिखाएं"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीन में आइकॉन जोड़ें"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नए ऐप के लिए"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"आइकॉन का आकार बदलें"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"होम स्क्रीन पर"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टम डिफ़ॉल्ट का उपयोग करें"</string>
<string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"गोल कोनों वाला वर्ग"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"इसके साथ फ़ोल्डर बनाएं: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"फ़ोल्डर बनाया गया"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"होम स्क्रीन पर ले जाएं"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"स्क्रीन को बाएं ले जाएं"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"स्क्रीन को दाएं ले जाएं"</string>
- <string name="screen_moved" msgid="266230079505650577">"स्क्रीन ले जाई गई"</string>
<string name="action_resize" msgid="1802976324781771067">"आकार बदलें"</string>
<string name="action_increase_width" msgid="8773715375078513326">"चौड़ाई बढ़ाएं"</string>
<string name="action_increase_height" msgid="459390020612501122">"ऊंचाई बढ़ाएं"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"काम से जुड़े हर ऐप्लिकेशन पर एक नारंगी रंग का बैज (निशान) होता है जिसकी सुरक्षा आपका संगठन करता है. आसानी से इस्तेमाल करने के लिए ऐप्लिकेशन को अपनी होम स्क्रीन पर ले जाएं."</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>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 42eb733..35a7963 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Traži više aplikacija"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obavijesti"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Dodirnite i zadržite kako biste podigli prečac."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvaput dodirnite i zadržite pritisak kako biste podigli prečac ili pokušajte prilagođenim radnjama."</string>
<string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom zaslonu više nema mjesta."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Favoriti"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Popis aplikacija"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Popis osobnih aplikacija"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Popis radnih 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>
@@ -60,6 +64,11 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijesti</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obavijesti</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Stranica %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Početni zaslon %1$d od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog zaslona"</string>
@@ -73,19 +82,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Postavke Homea"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Dopusti zakretanje početnog zaslona"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zakrene"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutačna postavka zaslona ne dopušta zakretanje"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Točke obavijesti"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Uključeno"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Isključeno"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Potreban je pristup obavijestima"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Prikaži točke obavijesti"</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="icon_shape_override_label" msgid="2977264953998281004">"Promijeni oblik ikona"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na početnom zaslonu"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Upotrijebi zadane postavke sustava"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
@@ -115,9 +122,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Izrada mape pomoću stavke: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mapa izrađena"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Premještanje na početni zaslon"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Premještanje zaslona ulijevo"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Premještanje zaslona udesno"</string>
- <string name="screen_moved" msgid="266230079505650577">"Zaslon je premješten"</string>
<string name="action_resize" msgid="1802976324781771067">"Promjena veličine"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Povećanje širine"</string>
<string name="action_increase_height" msgid="459390020612501122">"Povećanje visine"</string>
@@ -133,7 +137,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</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="1485375451542813426">"Svaka radna aplikacija ima narančastu značku i štiti je vaša organizacija. Premjestite aplikacije na početni zaslon radi lakšeg pristupa."</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>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index db39449..4972e81 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nem található alkalmazás a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"További alkalmazások keresése"</string>
<string name="notifications_header" msgid="1404149926117359025">"Értesítések"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Felvételhez tartsa nyomva a parancsikont."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Parancsikon felvételéhez koppintson rá duplán és tartsa nyomva, vagy használjon egyéni műveleteket."</string>
<string name="out_of_space" msgid="4691004494942118364">"Nincs több hely ezen a kezdőképernyőn."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nincs több hely a Kedvencek tálcán"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Alkalmazások listája"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Személyes alkalmazások listája"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Munkahelyi alkalmazások listája"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Főoldal"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Törlés"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one">A(z) <xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> értesítéssel rendelkezik</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d/%1$d. oldal"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d/%1$d. kezdőképernyő"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Új kezdőképernyő oldal"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
<string name="settings_button_text" msgid="8873672322605444408">"A Home beállításai"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Áttekintés"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A jelenlegi kijelzőbeállítások nem teszik lehetővé az elforgatást"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Értesítési pöttyök"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Bekapcsolva"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Kikapcsolva"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Értesítésekhez való hozzáférésre van szükség"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Értesítési pöttyök megjelenítése"</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="icon_shape_override_label" msgid="2977264953998281004">"Ikon formájának módosítása"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"a kezdőképernyőn"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Alapértelmezett érték használata"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Négyzet"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Mappa létrehozása a következővel: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mappa létrehozva"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Áthelyezés a kezdőképernyőre"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Képernyő mozgatása balra"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Képernyő mozgatása jobbra"</string>
- <string name="screen_moved" msgid="266230079505650577">"Képernyő áthelyezve"</string>
<string name="action_resize" msgid="1802976324781771067">"Átméretezés"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Szélesség növelése"</string>
<string name="action_increase_height" msgid="459390020612501122">"Magasság növelése"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</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="1485375451542813426">"Az egyes munkahelyi alkalmazásokon narancs 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="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>
</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 611a038..8c3ae25 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ունի <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ծանուցում</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Էջ %1$d՝ %2$d-ից"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Հիմնական էկրան %1$d` %2$d-ից"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Հիմնական էկրանի նոր էջ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Գլխավոր էջի կարգավորումներ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Համատեսք"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Հեռախոսը պտտելու դեպքում"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ցուցադրման ընթացիկ կարգավորումներն արգելում են պտտումը"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Ծանուցումների կետիկներ"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Միացված է"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Անջատված է"</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="icon_badging_service_title" msgid="2309733118428242174">"Ցուցադրել ծանուցումների կետիկները"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ավելացնել պատկերակը Հիմնական էկրանին"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Նոր հավելվածների համար"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Փոխել պատկերակների տեսքը"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"հիմնական էկրանին"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Օգտագործել համակարգի կանխադրված կարգավորումը"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Քառակուսի"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Քառանկյուն"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Ստեղծել թղթապանակ, օգտագործելով՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Պանակը ստեղծվեց"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Տեղափոխել Հիմնական էկրան"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Տեղափոխել էկրանը ձախ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Տեղափոխել էկրանը աջ"</string>
- <string name="screen_moved" msgid="266230079505650577">"Էկրանը տեղափոխվեց"</string>
<string name="action_resize" msgid="1802976324781771067">"Չափափոխել"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Ավելացնել լայնությունը"</string>
<string name="action_increase_height" msgid="459390020612501122">"Ավելացնել բարձրությունը"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Աշխատանքային հավելվածները նշված են նարնջագույն նշանով, նման հավելվածների անվտանգությունը ապահովում է ձեր կազմակերպությունը։ Հարմարության համար աշխատանքային հավելվածները կարող եք տեղափոխել հիմնական էկրան։"</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>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index ff49b49..ea0ea4a 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Tidak ditemukan aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Telusuri aplikasi lainnya"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifikasi"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tap lama untuk memilih pintasan."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tap dua kali & tahan untuk memilih pintasan atau menggunakan tindakan khusus."</string>
<string name="out_of_space" msgid="4691004494942118364">"Tidak ada ruang lagi pada layar Utama ini."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tidak ada ruang tersisa di baki Favorit"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Daftar aplikasi"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Daftar aplikasi pribadi"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Daftar aplikasi kantor"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Layar Utama"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Hapus"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstal"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, memiliki <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifikasi</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d dari %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Layar utama %1$d dari %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Halaman layar utama baru"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Setelan layar Utama"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Ringkasan"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan layar Utama diputar"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Saat ponsel diputar"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Setelan Tampilan Saat Ini tidak memungkinkan putaran"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Titik notifikasi"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktif"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Nonaktif"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Perlu akses notifikasi"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Tampilkan titik notifikasi"</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="icon_shape_override_label" msgid="2977264953998281004">"Ubah bentuk ikon"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"di layar Utama"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan default sistem"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Persegi"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Persegi bundar"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Buat folder dengan: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Folder dibuat"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Pindahkan ke layar Utama"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Pindahkan layar ke kiri"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Pindahkan layar ke kanan"</string>
- <string name="screen_moved" msgid="266230079505650577">"Layar dipindahkan"</string>
<string name="action_resize" msgid="1802976324781771067">"Ubah ukuran"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Tambahi lebar"</string>
<string name="action_increase_height" msgid="459390020612501122">"Tambahi tinggi"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kantor"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil 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="1485375451542813426">"Setiap aplikasi kerja memiliki badge oranye dan dibuat tetap aman oleh organisasi. Pindahkan aplikasi ke Layar utama untuk memudahkan akses."</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>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index c3988f3..3dc2d22 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Leita að fleiri forritum"</string>
<string name="notifications_header" msgid="1404149926117359025">"Tilkynningar"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Haltu fingri á flýtileið til að grípa hana."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ýttu tvisvar og haltu fingri á flýtileið til að grípa hana eða notaðu sérsniðnar aðgerðir."</string>
<string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Forritalisti"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Listi yfir eigin forrit"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Listi yfir vinnuforrit"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Heim"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Fjarlægja"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
@@ -60,6 +64,10 @@
<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="6617836969016293992">"Ónefnd mappa"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <plurals name="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, er með <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> tilkynningar</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Síða %1$d af %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Heimaskjár %1$d af %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ný síða á heimaskjá"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Heimastillingar"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Yfirlit"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning fyrir heimaskjá"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Núverandi skjástilling leyfir ekki snúning"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Tilkynningapunktar"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Kveikt"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Slökkt"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Aðgangs að tilkynningum er krafist"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Sýna tilkynningapunkta"</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="icon_shape_override_label" msgid="2977264953998281004">"Breyta formi tákns"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"á heimaskjá"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Nota sjálfgildi kerfis"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Ferningur"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Ferhringur"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Búa til möppu með: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mappa búin til"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Færa á heimaskjá"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Færa skjá til vinstri"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Færa skjá til hægri"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skjár færður"</string>
<string name="action_resize" msgid="1802976324781771067">"Breyta stærð"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Auka breidd"</string>
<string name="action_increase_height" msgid="459390020612501122">"Auka hæð"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</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="1485375451542813426">"Öll vinnuforrit eru með appelsínugulu merki og fyrirtækið þitt tryggir öryggi þeirra. Færðu forrit yfir á heimaskjáinn fyrir auðveldari aðgang að þeim."</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>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 174949e..44e12c0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca altre app"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notifiche"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tocca e tieni premuto per scegliere la scorciatoia"</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tocca due volte e tieni premuto per scegliere una scorciatoia o per usare azioni personalizzate."</string>
<string name="out_of_space" msgid="4691004494942118364">"Spazio nella schermata Home esaurito."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spazio esaurito nella barra dei Preferiti"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Elenco di app"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Elenco di app personali"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Elenco di app di lavoro"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home page"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Rimuovi"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> ha <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notifica</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Impostazioni Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Panoramica"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Consenti rotazione della schermata Home"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Con il telefono ruotato"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"L\'impostazione corrente del display non consente la rotazione"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Indicatori notifica"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Attiva"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Non attiva"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Accesso alle notifiche necessario"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Mostra indicatori di notifica"</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="icon_shape_override_label" msgid="2977264953998281004">"Cambia la forma delle icone"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"nella schermata Home"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usa impostazione predefinita di sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Quadrato"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Supercerchio"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Crea cartella con: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Cartella creata"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Sposta nella schermata Home"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Sposta schermata a sinistra"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Sposta schermata a destra"</string>
- <string name="screen_moved" msgid="266230079505650577">"Schermata spostata"</string>
<string name="action_resize" msgid="1802976324781771067">"Ridimensiona"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Aumenta larghezza"</string>
<string name="action_increase_height" msgid="459390020612501122">"Aumenta altezza"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</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="1485375451542813426">"Ogni app di lavoro è contrassegnata da un badge arancione e viene tenuta al sicuro dalla tua organizzazione. Sposta le app nella schermata Home per accedervi più facilmente."</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>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index db156e1..bc2061b 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,12 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="two">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> הודעות</item>
+ <item quantity="many">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> הודעות</item>
+ <item quantity="other">לאפליקציה <xliff:g id="APP_NAME_2">%1$s</xliff:g> יש <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> הודעות</item>
+ <item quantity="one">לאפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> יש הודעה אחת (<xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>)</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"דף %1$d מתוך %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"מסך דף הבית %1$d מתוך %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"מסך דף הבית חדש"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
<string name="settings_button_text" msgid="8873672322605444408">"הגדרות דף הבית"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"סקירה"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"אפשרות סיבוב של מסך דף הבית"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר הטלפון מסובב"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"הגדרת התצוגה הנוכחית אינה מאפשרת סיבוב"</string>
<string name="icon_badging_title" msgid="874121399231955394">"סימני הודעות"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"מופעלת"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"כבויה"</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="icon_badging_service_title" msgid="2309733118428242174">"הצגה של סימן ההודעות"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"הוספת סמל במסך דף הבית"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"לאפליקציות חדשות"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"שינוי הצורה של הסמלים"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"במסך דף הבית"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"השתמש בברירת המחדל של המערכת"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ריבוע"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ריבוע בעל פינות מעוגלות"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"צור תיקייה עם: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"התיקייה נוצרה"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"העבר אל מסך דף הבית"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"הזז את המסך שמאלה"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"הזז את המסך ימינה"</string>
- <string name="screen_moved" msgid="266230079505650577">"המסך הועבר"</string>
<string name="action_resize" msgid="1802976324781771067">"שנה גודל"</string>
<string name="action_increase_width" msgid="8773715375078513326">"הגדל רוחב"</string>
<string name="action_increase_height" msgid="459390020612501122">"הגדל גובה"</string>
@@ -133,7 +138,9 @@
<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="1485375451542813426">"לכל אפליקציית עבודה יש תג כתום ואבטחתה מטופלת בידי הארגון. אפשר להעביר אפליקציות אל מסך דף הבית כדי להקל את הגישה אליהן."</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>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 527cb95..c5ab310 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 件の通知</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>: <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 件の通知</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$dページ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ホーム画面: %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"新しいホーム画面ページ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ホームの設定"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"概要"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ホーム画面の回転を許可"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"スマートフォンが回転したとき"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"現在の [ディスプレイ] 設定では回転を使用できません"</string>
<string name="icon_badging_title" msgid="874121399231955394">"通知ドット"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ON"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"OFF"</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>」のアプリ通知を ON にしてください"</string>
<string name="title_change_settings" msgid="1376365968844349552">"設定を変更"</string>
+ <string name="icon_badging_service_title" msgid="2309733118428242174">"通知ドットの表示"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ホーム画面にアイコンを追加"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"新しいアプリをダウンロードしたとき"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"アイコンの形の変更"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ホーム画面上"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"システムのデフォルトを使用"</string>
<string name="icon_shape_square" msgid="633575066111622774">"スクエア"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"スクワークル"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"「<xliff:g id="NAME">%1$s</xliff:g>」フォルダを作成"</string>
<string name="folder_created" msgid="6409794597405184510">"フォルダを作成しました"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ホーム画面に移動"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"画面を左に移動"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"画面を右に移動"</string>
- <string name="screen_moved" msgid="266230079505650577">"画面を移動しました"</string>
<string name="action_resize" msgid="1802976324781771067">"サイズを変更"</string>
<string name="action_increase_width" msgid="8773715375078513326">"幅を広くする"</string>
<string name="action_increase_height" msgid="459390020612501122">"高さを高くする"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"仕事用アプリにはオレンジのバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。"</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>
</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7e1752c..02ca862 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ში <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> შეტყობინებაა</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ში <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> შეტყობინებაა</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"გვერდი %1$d %2$d-დან"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"მთავარი ეკრანი %1$d, %2$d-დან"</string>
<string name="workspace_new_page" msgid="257366611030256142">"მთავარი ეკრანის ახალი გვერდი"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
<string name="settings_button_text" msgid="8873672322605444408">"მთავარი გვერდის პარამეტრები"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"მიმოხილვა"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ტელეფონის შეტრიალებისას"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ბრუნვა დაუშვებელია ჩვენების მიმდინარე პარამეტრებით"</string>
<string name="icon_badging_title" msgid="874121399231955394">"შეტყობინების ნიშნულები"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ჩართული"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"გამორთული"</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="icon_badging_service_title" msgid="2309733118428242174">"შეტყობინების ნიშნულების ჩვენება"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ხატულას მთავარ ეკრანზე დამატება"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ახალი აპებისთვის"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ხატულას ფორმის შეცვლა"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"მთავარ ეკრანზე"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ნაგულისხმევი სისტემური პარამეტრების გამოყენება"</string>
<string name="icon_shape_square" msgid="633575066111622774">"კვადრატი"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"წრეკუთხედი"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"საქაღალდის შექმნა ერთეულით: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"საქაღალდე შექმნილია"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"მთავარ ეკრანზე გადატანა"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"ეკრანის გადატანა მარცხნივ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"ეკრანის გადატანა მარჯვნით"</string>
- <string name="screen_moved" msgid="266230079505650577">"ეკრანი გადაადგილდა"</string>
<string name="action_resize" msgid="1802976324781771067">"ზომის შეცვლა"</string>
<string name="action_increase_width" msgid="8773715375078513326">"სიგანის გაზრდა"</string>
<string name="action_increase_height" msgid="459390020612501122">"სიმაღლის გაზრდა"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"სამსახურის აპები მონიშნულია სტაფილოსფერი ბეჯით, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ."</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>
</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index ef92146..b66fe69 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> хабарландыру бар</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасында <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> хабарландыру бар</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d бет, барлығы %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d негізгі экран, барлығы %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Жаңа негізгі экран беті"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Шолу"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Негізгі экранның бұрылуына рұқсат ету"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бұрылғанда"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранның ағымдағы параметрі айналуға рұқсат бермейді"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Хабарландыру белгілері"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Қосулы"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Өшірулі"</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="icon_badging_service_title" msgid="2309733118428242174">"Хабарландыру белгілерін көрсету"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Негізгі экранға белгіше енгізу"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңа қолданбаларға арналған"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Белгіше пішінін өзгерту"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Негізгі экранда"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Жүйенің әдепкі параметрін пайдалану"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Шаршы"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Жұмыр төртбұрыш"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Мына бар қалтаны жасау: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Қалта жасалды"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Негізгі экранға жылжыту"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Экранды солға жылжыту"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Экранды оңға жылжыту"</string>
- <string name="screen_moved" msgid="266230079505650577">"Экран жылжытылды"</string>
<string name="action_resize" msgid="1802976324781771067">"Өлшемін өзгерту"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Енін арттыру"</string>
<string name="action_increase_height" msgid="459390020612501122">"Биіктігін арттыру"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Әрбір жұмыс қолданбасында қызғылт сары танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады."</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>
</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index e52e0b2..6b56372 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> មានការជូនដំណឹង <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> មានការជូនដំណឹង <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g></item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"ទំព័រ %1$d នៃ %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"អេក្រង់ដើម %1$d នៃ %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ទំព័រអេក្រង់ដើមថ្មី"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំងរូបភាព"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់ទំព័រដើម"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"សង្ខេប"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"នៅពេលដែលបង្វិលទូរស័ព្ទរបស់អ្នក"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ការកំណត់អេក្រង់បច្ចុប្បន្នមិនអនុញ្ញាតការបង្វិលទេ"</string>
<string name="icon_badging_title" msgid="874121399231955394">"ស្លាកជូនដំណឹង"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"បើក"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"បិទ"</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="icon_badging_service_title" msgid="2309733118428242174">"បង្ហាញស្លាកជូនដំណឹង"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"បញ្ចូលរូបតំណាងទៅអេក្រង់ដើម"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"សម្រាប់កម្មវិធីថ្មី"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ប្តូររូបរាងរូបតំណាង"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"នៅលើអេក្រង់ដើម"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ប្រើលំនាំដើមរបស់ប្រព័ន្ធ"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ការ៉េ"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ការ៉េជ្រុងកោង"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"បង្កើតថតឯកសារជាមួយ៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"បានបង្កើតថតឯកសារ"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ផ្លាស់ទៅអេក្រង់ដើម"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"រំកិលអេក្រង់ទៅខាងឆ្វេង"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"រំកិលអេក្រង់ទៅខាងស្តាំ"</string>
- <string name="screen_moved" msgid="266230079505650577">"អេក្រង់ដែលបានផ្លាស់ទី"</string>
<string name="action_resize" msgid="1802976324781771067">"ប្ដូរទំហំ"</string>
<string name="action_increase_width" msgid="8773715375078513326">"បង្កើនទទឹង"</string>
<string name="action_increase_height" msgid="459390020612501122">"បង្កើនកម្ពស់"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"កម្មវិធីការងារនីមួយៗមានស្លាកពណ៌ទឹកក្រូច និងត្រូវបានរក្សាទុកយ៉ាងមានសុវត្ថិភាពដោយស្ថាប័នរបស់អ្នក។ សូមផ្លាស់ទីកម្មវិធីទៅកាន់អេក្រង់ដើមរបស់អ្នក ដើម្បីងាយស្រួលចូលប្រើជាងមុន។"</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>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 4bb0d22..d4813b5 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳನ್ನು ಹೊಂದಿದೆ</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ಹೊಸ ಮುಖಪುಟ ಪರದೆ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್ಪೇಪರ್ಗಳು"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"ಅವಲೋಕನ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್ ತಿರುಗಿಸಿದಾಗ"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ಪ್ರಸ್ತುತ ಪ್ರದರ್ಶನ ಸೆಟ್ಟಿಂಗ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
<string name="icon_badging_title" msgid="874121399231955394">"ಅಧಿಸೂಚನೆ ಡಾಟ್ಗಳು"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ಆನ್"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ಆಫ್"</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="icon_badging_service_title" msgid="2309733118428242174">"ಅಧಿಸೂಚನೆ ಡಾಟ್ಗಳನ್ನು ತೋರಿಸಿ"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ಹೊಸ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ಮುಖಪುಟ ಪರದೆಯಲ್ಲಿ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ಚೌಕ"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ಚೌಕವೃತ್ತ"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಫೋಲ್ಡರ್ ರಚಿಸಿ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ಫೋಲ್ಡರ್ ರಚಿಸಲಾಗಿದೆ"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ಮುಖಪುಟಕ್ಕೆ ಸರಿಸಿ"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"ಪರದೆಯನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"ಪರದೆಯನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
- <string name="screen_moved" msgid="266230079505650577">"ಪರದೆ ಸರಿಸಲಾಗಿದೆ"</string>
<string name="action_resize" msgid="1802976324781771067">"ಮರುಗಾತ್ರ"</string>
<string name="action_increase_width" msgid="8773715375078513326">"ಅಗಲವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ"</string>
<string name="action_increase_height" msgid="459390020612501122">"ಎತ್ತರವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಕಿತ್ತಳೆ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸರಿಸಿ."</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>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index a50a107..9275abf 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>개의 <xliff:g id="APP_NAME_2">%1$s</xliff:g> 알림 있음</item>
+ <item quantity="one"><xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g>개의 <xliff:g id="APP_NAME_0">%1$s</xliff:g> 알림 있음</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"페이지 %1$d/%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"홈 화면 %1$d/%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"새로운 홈 화면 페이지"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
<string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"개요"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"홈 화면 회전 허용"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"휴대전화 회전 시"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"현재 표시 설정에는 회전 기능이 허용되지 않습니다."</string>
<string name="icon_badging_title" msgid="874121399231955394">"알림 표시 점"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"사용"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"사용 안함"</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="icon_badging_service_title" msgid="2309733118428242174">"알림 표시 점 보기"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"홈 화면에 아이콘 추가"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"새로 설치한 앱에 적용"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"아이콘 모양 변경"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"홈 화면에 표시"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"시스템 기본값 사용"</string>
<string name="icon_shape_square" msgid="633575066111622774">"정사각형"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"모서리가 둥근 정사각형"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"다음이 포함된 폴더 만들기: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"폴더를 만들었습니다."</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"홈 화면으로 이동"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"화면을 왼쪽으로 이동"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"화면을 오른쪽으로 이동"</string>
- <string name="screen_moved" msgid="266230079505650577">"화면 이동됨"</string>
<string name="action_resize" msgid="1802976324781771067">"크기 조정"</string>
<string name="action_increase_width" msgid="8773715375078513326">"폭 늘리기"</string>
<string name="action_increase_height" msgid="459390020612501122">"높이 늘리기"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"각 업무용 앱에는 주황색 배지가 있으며 업무용 앱은 조직에서 안전하게 보호됩니다. 앱을 홈 화면으로 이동하여 더 간편하게 사용하세요."</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>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index e2fde48..e0e7403 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> эскертме бар</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> эскертме бар</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Жаңы башкы экран барагы"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Башкы беттин жөндөөлөрү"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Көз жүгүртүү"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон айланганда"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранды айлантуу параметри өчүрүлгөн"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Эскертме белгилери"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Күйүк"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Өчүк"</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="icon_badging_service_title" msgid="2309733118428242174">"Эскертме белгилерин көрсөтүү"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Башкы экранга сүрөтчө кошуу"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Сүрөтчөнүн формасын өзгөртүү"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Башкы экранда"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Тутум сушунтаган демейкисин колдонуу"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Чарчы"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Бурчтары жумуру төрт бурчтук"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Төмөнкү менен куржун түзүү: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Куржун түзүлдү"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Башкы экранга жылдыруу"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Экранды солго жылдыруу"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Экранды оңго жылдыруу"</string>
- <string name="screen_moved" msgid="266230079505650577">"Экран жылдырылды"</string>
<string name="action_resize" msgid="1802976324781771067">"Өлчөмүн өзгөртүү"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Кеңейтүү"</string>
<string name="action_increase_height" msgid="459390020612501122">"Бийиктетүү"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Ар бир жумуш колдонмосунун кызгылт сары бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз."</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>
</resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 40ffffe..7b52529 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -25,16 +25,12 @@
<dimen name="fastscroll_popup_text_size">24dp</dimen>
<!-- Dynamic grid -->
- <dimen name="dynamic_grid_overview_bar_item_width">120dp</dimen>
<dimen name="dynamic_grid_min_page_indicator_size">48dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
<dimen name="dynamic_grid_cell_layout_padding">0dp</dimen>
<dimen name="dynamic_grid_cell_layout_bottom_padding">5.5dp</dimen>
- <!-- Folders -->
- <dimen name="folder_preview_padding">2dp</dimen>
-
<!-- Hotseat -->
<!-- Will be set to equal the hotseat icon size. -->
<dimen name="dynamic_grid_hotseat_size">0dp</dimen>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 3099d1f..2b73628 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ມີ <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ການແຈ້ງເຕືອນ</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"ໜ້າ %1$d ຈາກ %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ໜ້າຂອງໜ້າຈໍຫຼັກໃໝ່"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"ພາບຮວມ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ເມື່ອໝຸນໂທລະສັບ"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ການຕັ້ງຄ່າສະແດງຜົນປັດຈຸບັນບໍ່ອະນຸຍາດໃຫ້ໝຸນໄດ້"</string>
<string name="icon_badging_title" msgid="874121399231955394">"ຈຸດການແຈ້ງເຕືອນ"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ເປີດ"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ປິດ"</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="icon_badging_service_title" msgid="2309733118428242174">"ສະແດງຈຸດການແຈ້ງເຕືອນ"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ສຳລັບແອັບໃໝ່"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ປ່ຽນຮູບຮ່າງໄອຄອນ"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ຢູ່ໜ້າຈໍຫຼັກ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ສີ່ຫຼ່ຽມຈັດຕຸລັດ"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ສີ່ຫຼ່ຽມຂອບມົນ"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ສ້າງໂຟລເດີກັບ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ສ້າງໂຟລເດີແລ້ວ"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ຍ້າຍໄປໃສ່ໜ້າຈໍຫຼັກ"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"ຍ້າຍໜ້າຈໍໄປທາງຊ້າຍ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"ຍ້າຍໜ້າຈໍໄປທາງຂວາ"</string>
- <string name="screen_moved" msgid="266230079505650577">"ຍ້າຍໜ້າຈໍແລ້ວ"</string>
<string name="action_resize" msgid="1802976324781771067">"ປັບຂະໜາດ"</string>
<string name="action_increase_width" msgid="8773715375078513326">"ເພີ່ມລວງກ້ວາງຂຶ້ນ"</string>
<string name="action_increase_height" msgid="459390020612501122">"ເພີ່ມລວງສູງຂຶ້ນ"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"ແຕ່ລະແອັບວຽກຈະມີປ້າຍສີສົ້ມ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້."</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>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 5b2e951..db751da 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Ieškoti daugiau programų"</string>
<string name="notifications_header" msgid="1404149926117359025">"Pranešimai"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Paliesk. ir palaikyk., kad pasirinkt. spart. klav."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dukart palieskite ir palaikykite, kad pasirinkt. spartųjį klavišą ar naudotumėte tinkintus veiksmus."</string>
<string name="out_of_space" msgid="4691004494942118364">"Šiame pagrindiniame ekrane vietos nebėra."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Programų sąrašas"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Asmeninių programų sąrašas"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Darbo programų sąrašas"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Pagrindinis"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ištrinti"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
@@ -60,6 +64,12 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimas</item>
+ <item quantity="few">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimai</item>
+ <item quantity="many">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimo</item>
+ <item quantity="other">„<xliff:g id="APP_NAME_2">%1$s</xliff:g>“, yra <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> pranešimų</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d psl. iš %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d pagrindinis ekranas iš %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Naujas pagrindinio ekrano puslapis"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
<string name="settings_button_text" msgid="8873672322605444408">"„Home“ nustatymai"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Apžvalga"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Leisti pasukti pagrindinį ekraną"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Kai telefonas pasukamas"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Naudojant dabartinį pateikties nustatymą neleidžiama pasukti"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Pranešimų taškai"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Įjungta"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Išjungta"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Reikalinga prieiga prie pranešimų"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Rodyti pranešimų taškus"</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="icon_shape_override_label" msgid="2977264953998281004">"Pakeisti piktogramos formą"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pagrindiniame ekrane"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Naudoti numatytuosius sistemos nustatymus"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadratas"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadratais suapvalintais kampais"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Kurti aplanką naudojant: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="folder_created" msgid="6409794597405184510">"Aplankas sukurtas"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Perkelti į pagrindinį ekraną"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Perkelti ekraną į kairę"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Perkelti ekraną į dešinę"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekranas perkeltas"</string>
<string name="action_resize" msgid="1802976324781771067">"Pakeisti dydį"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Padidinti plotį"</string>
<string name="action_increase_height" msgid="459390020612501122">"Padidinti aukštį"</string>
@@ -133,7 +138,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Darbo"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Darbo profilis"</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="1485375451542813426">"Kiekvienai darbo programai priskirtas oranžinis ženklelis, o tokių programų sauga rūpinasi jūsų organizacija. Perkelkite darbo programas į pagrindinį ekraną, kad galėtumėte lengviau jas pasiekti."</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>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index eec27b1..a6e16de 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Meklēt citas lietotnes"</string>
<string name="notifications_header" msgid="1404149926117359025">"Paziņojumi"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Lai atlasītu saīsni, pieskarieties un turiet to."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Lai atlasītu saīsni, veiciet dubultskārienu uz tās un turiet to. Varat arī veikt pielāgotas darbības."</string>
<string name="out_of_space" msgid="4691004494942118364">"Šajā sākuma ekrānā vairs nav vietas."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Izlases joslā vairs nav vietas."</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lietotņu saraksts"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personīgo lietotņu saraksts"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Darba lietotņu saraksts"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Sākums"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Noņemt"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
@@ -60,6 +64,11 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="zero"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojums</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ir <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> paziņojumi</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. lapa no %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Sākuma ekrāns: %1$d no %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Jauna sākuma ekrāna lapa"</string>
@@ -73,19 +82,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Sākumlapas iestatījumi"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Kopsavilkums"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Atļaut sākuma ekrāna pagriešanu"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Pagriežot tālruni"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Pašreizējā displeja iestatījumā nav atļauta pagriešana."</string>
<string name="icon_badging_title" msgid="874121399231955394">"Paziņojumu punkti"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Ieslēgts"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Izslēgts"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Nepieciešama piekļuve paziņojumiem"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Rādīt paziņojumu punktus"</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="icon_shape_override_label" msgid="2977264953998281004">"Mainīt ikonu formu"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sākuma ekrānā"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Izmantot sistēmas noklusējumu"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrāts"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrāts ar noapaļotiem stūriem"</string>
@@ -115,9 +122,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Izveidot mapi ar: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mape izveidota"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Pārvietot uz sākuma ekrānu"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Pārvietot ekrānu pa kreisi"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Pārvietot ekrānu pa labi"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekrāns pārvietots"</string>
<string name="action_resize" msgid="1802976324781771067">"Mainīt lielumu"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Palielināt platumu"</string>
<string name="action_increase_height" msgid="459390020612501122">"Palielināt augstumu"</string>
@@ -133,7 +137,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Darba lietotnes"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Darba profils"</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="1485375451542813426">"Katrai darba lietotnei ir oranža emblēma, un jūsu organizācija aizsargā šīs lietotnes. Ērtākai piekļuvei pārvietojiet darba lietotnes uz sākuma ekrānu."</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>
</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 53944cb..29e837b 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известување</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> известувања</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d од %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Екран на почетна страница %1$d од %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова страница на почетен екран"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Краток преглед"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Кога телефонот се ротира"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Тековната поставка на Екранот не дозволува ротација"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Точки за известување"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Вклучено"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Исклучено"</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="icon_badging_service_title" msgid="2309733118428242174">"Прикажи точки за известување"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икона на почетниот екран"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови апликации"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Променете ја формата на иконата"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на „Почетен екран“"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Користи ја стандардната поставка на системот"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Заоблен квадрат"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Создај папка со: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Папката е создадена"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Премести на Почетен екран"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Движи го екранот налево"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Движи го екранот надесно"</string>
- <string name="screen_moved" msgid="266230079505650577">"Екранот е преместен"</string>
<string name="action_resize" msgid="1802976324781771067">"Промени големина"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Зголеми ширина"</string>
<string name="action_increase_height" msgid="459390020612501122">"Зголеми висина"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Секоја апликација за работа има портокалова значка и е обезбедена од вашата организација. За полесен пристап, апликациите за работа преместете ги на почетниот екран."</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>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index c247c4e..5ccac13 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -40,9 +40,13 @@
<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="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">"കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ 2 തവണ ടാപ്പ് ചെയ്ത് പിടിക്കുക."</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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>-ന്, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> അറിയിപ്പുകൾ ഉണ്ട്</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>-ന്, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> അറിയിപ്പ് ഉണ്ട്</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്ക്രീൻ %1$d / %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"പുതിയ ഹോം സ്ക്രീൻ പേജ്"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ഹോം ക്രമീകരണം"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"അവലോകനം"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല"</string>
<string name="icon_badging_title" msgid="874121399231955394">"അറിയിപ്പ് ഡോട്ടുകൾ"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ഓൺ"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ഓഫ്"</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="icon_badging_service_title" msgid="2309733118428242174">"അറിയിപ്പ് ഡോട്ടുകൾ കാണിക്കുക"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"പുതിയ ആപ്പുകൾക്ക്"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ഐക്കണിന്റെ ആകാരം മാറ്റുക"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ഹോം സ്ക്രീനിൽ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"സിസ്റ്റം ഡിഫോൾട്ട് ഉപയോഗിക്കുക"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ചതുരം"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ചതുരവൃത്തം"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ഇതുപയോഗിച്ച് ഫോൾഡർ സൃഷ്ടിക്കുക: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ഫോൾഡർ സൃഷ്ടിച്ചു"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ഹോം സ്ക്രീനിലേക്ക് നീക്കുക"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"സ്ക്രീൻ ഇടത്തേക്ക് നീക്കുക"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"സ്ക്രീൻ വലത്തേക്ക് നീക്കുക"</string>
- <string name="screen_moved" msgid="266230079505650577">"സ്ക്രീൻ നീക്കി"</string>
<string name="action_resize" msgid="1802976324781771067">"വലുപ്പംമാറ്റുക"</string>
<string name="action_increase_width" msgid="8773715375078513326">"വീതി കൂട്ടുക"</string>
<string name="action_increase_height" msgid="459390020612501122">"ഉയരം കൂട്ടുക"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഓറഞ്ച് നിറത്തിലുള്ള ഒരു ബാഡ്ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്ക്രീനിലേക്ക് നീക്കുക."</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>
</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 1deef00..7c0b00e 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> мэдэгдэл байна</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> мэдэгдэл байна</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Шинэ үндсэн нүүр хуудас"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Тойм"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Утсыг эргүүлсэн үед"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Дэлгэцийн одоогийн тохиргоогоор эргүүлэх боломжгүй"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Мэдэгдлийн цэг"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Асаалттай"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Унтраалттай"</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="icon_badging_service_title" msgid="2309733118428242174">"Мэдэгдлийн цэгийг харуулах"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Нүүр хуудаст дүрс тэмдэг нэмэх"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Шинэ аппад зориулсан"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Дүрс тэмдгийн хэлбэрийг өөрчлөх"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Үндсэн нүүр хэсэгт"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Системийн өгөгдмөл тохиргоог ашиглах"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Дөрвөлжин"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Мохоо өнцөгтэй дөрвөлжин"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Хавтсыг: <xliff:g id="NAME">%1$s</xliff:g> нэрээр үүсгэ"</string>
<string name="folder_created" msgid="6409794597405184510">"Үүсгэсэн хавтас"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Нүүр дэлгэц рүү зөөх"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Дэлгэцийг зүүн тийш зөөх"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Дэлгэцийг баруун тийш зөөх"</string>
- <string name="screen_moved" msgid="266230079505650577">"Дэлгэцийг зөөсөн"</string>
<string name="action_resize" msgid="1802976324781771067">"Хэмжээг өөрчлөх"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Өргөсгөх"</string>
<string name="action_increase_height" msgid="459390020612501122">"Өндөрсгөх"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Ажлын апп тус бүр улбар шар тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбараар хандахын тулд тэдгээрийг Үндсэн Нүүрэнд зөөнө үү."</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>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index c11883d..0e2f9c8 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, कडे <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहे</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, कडे <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचना आहेत</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
<string name="workspace_new_page" msgid="257366611030256142">"नवीन मुख्य स्क्रीन पृष्ठ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्ज"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"अवलोकन"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्यास अनुमती द्या"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"फोन फिरविला जातो तेव्हा"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"वर्तमान डिस्प्ले सेटिंग रोटेशनला परवानगी देत नाही"</string>
<string name="icon_badging_title" msgid="874121399231955394">"सूचना बिंदू"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"चालू"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"बंद"</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="icon_badging_service_title" msgid="2309733118428242174">"सूचना बिंदू दाखवा"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"होम स्क्रीनवर आयकन जोडा"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन अॅप्ससाठी"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"चिन्हाचा आकार बदला"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"होम स्क्रीनवर"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टमचे डीफॉल्ट वापरा"</string>
<string name="icon_shape_square" msgid="633575066111622774">"चौरस"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"गोलाकार चौरस"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"यासह फोल्डर तयार करा: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"फोल्डर तयार केले"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"मुख्य स्क्रीनवर हलवा"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"स्क्रीन डावीकडे हलवा"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"स्क्रीन उजवीकडे हलवा"</string>
- <string name="screen_moved" msgid="266230079505650577">"स्क्रीन हलविली"</string>
<string name="action_resize" msgid="1802976324781771067">"आकार बदला"</string>
<string name="action_increase_width" msgid="8773715375078513326">"रूंदी वाढवा"</string>
<string name="action_increase_height" msgid="459390020612501122">"उंची वाढवा"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"प्रत्येक कार्य अॅपमध्ये नारिंगी बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अॅक्सेससाठी अॅप्स तुमच्या होम स्क्रीनवर हलवा."</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>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index f88fe96..130e008 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Tiada apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Cari lagi apl"</string>
<string name="notifications_header" msgid="1404149926117359025">"Pemberitahuan"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Sentuh & tahan untuk mengambil pintasan."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ketik dua kali & tahan untuk mengambil pintasan atau menggunakan tindakan tersuai."</string>
<string name="out_of_space" msgid="4691004494942118364">"Tiada lagi ruang pada skrin Laman Utama ini."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Senarai apl"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Senarai apl peribadi"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Senarai apl kerja"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Laman Utama"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Alih keluar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, mempunyai <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> pemberitahuan</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d daripada %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Skrin Laman Utama %1$d daripada %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Halaman skrin utama baharu"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Tetapan laman utama"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Ikhtisar"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Tetapan Paparan semasa tidak membenarkan putaran"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Titik pemberitahuan"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Hidup"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Mati"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Akses pemberitahuan diperlukan"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Tunjukkan titik pemberitahuan"</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="icon_shape_override_label" msgid="2977264953998281004">"Tukar bentuk ikon"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pada Skrin Utama"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan lalai sistem"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Segi empat sama"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Segi empat berbucu bulat"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Buat folder dengan: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Folder dibuat"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Alihkan ke Skrin Utama"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Alihkan skrin ke kiri"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Alihkan skrin ke kanan"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skrin dialihkan"</string>
<string name="action_resize" msgid="1802976324781771067">"Ubah saiz"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Tambahkan kelebaran"</string>
<string name="action_increase_height" msgid="459390020612501122">"Tambahkan ketinggian"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</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="1485375451542813426">"Setiap apl kerja terdapat lencana berwarna oren dan dilindungi oleh organisasi anda. Alihkan apl ke Skrin Utama untuk akses yang lebh mudah."</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>
</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index f44f28c..778402a 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> တွင် အကြောင်းကြားချက် <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ခု ရှိသည်</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> တွင် အကြောင်းကြားချက် <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ခု ရှိသည်</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"စာမျက်နှာ %1$d မှ %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ပင်မစာမျက်နှာ %1$d မှ %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ပင်မမျက်နှာပြင် စာမျက်နှာသစ်"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ပင်မဆက်တင်များ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"ခြုံငုံသုံးသပ်ချက်"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ဖုန်းကိုလှည့်ထားစဉ်"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"လက်ရှိ မြင်ကွင်းဆက်တင်တွင် မြင်ကွင်းကို လှည့်ခွင့်မပေးပါ"</string>
<string name="icon_badging_title" msgid="874121399231955394">"အကြောင်းကြားချက်အမှတ်အသားများ"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ဖွင့်ထားသည်"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ပိတ်ထားသည်"</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="icon_badging_service_title" msgid="2309733118428242174">"အကြောင်းကြားချက် အမှတ်အသားများကို ပြရန်"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"သင်္ကေတပုံစံကို ပြောင်းရန်"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"\'ပင်မမျက်နှာပြင်\' ပေါ်တွင်"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်"</string>
<string name="icon_shape_square" msgid="633575066111622774">"လေးထောင့်"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"စတုရန်းမကျ စက်ဝိုင်းမကျပုံ"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ဖိုလ်ဒါ ပြုလုပ်ရန်- <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ဖိုလ်ဒါ ပြုလုပ်ပြီး"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"မျက်နှာပြင် ဘယ်ဘက်သို့ ရွှေ့ပါ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"မျက်နှာပြင် ညာဘက်သို့ ရွှေ့ပါ"</string>
- <string name="screen_moved" msgid="266230079505650577">"ဖန်မျက်နှာပြင် ပြောင်းရွှေ့ပြီး၏"</string>
<string name="action_resize" msgid="1802976324781771067">"အရွယ်အစားပြောင်းပါ"</string>
<string name="action_increase_width" msgid="8773715375078513326">"အကျယ်အား တိုးပါ"</string>
<string name="action_increase_height" msgid="459390020612501122">"အမြင့်အား တိုးပါ"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"အလုပ်အက်ပ်တိုင်းတွင် လိမ္မော်ရောင်တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။"</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>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index ee374f8..5542079 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Søk etter flere apper"</string>
<string name="notifications_header" msgid="1404149926117359025">"Varsler"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Trykk og hold for å velge en snarvei."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</string>
<string name="out_of_space" msgid="4691004494942118364">"Denne startsiden er full."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritter-skuffen er full"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"App-liste"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Personlige apper-liste"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Jobbapper-liste"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startside"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> varsel</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d av %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startside %1$d av %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ny side på startskjermen"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Startsideinnstillinger"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Oversikt"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Tillat rotasjon av startskjermen"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Med den nåværende skjerminnstillingen støttes ikke rotasjon"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Varselsprikker"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"På"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Av"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Tilgang til varsler er nødvendig"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Vis varselsprikker"</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="icon_shape_override_label" msgid="2977264953998281004">"Endre formen på ikonet"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"på startskjermen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Bruk systemstandard"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Superellipse"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Opprett mappe med: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mappen er opprettet"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Flytt til startskjermen"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Flytt skjermen til venstre"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Flytt skjermen til høyre"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skjermen er flyttet"</string>
<string name="action_resize" msgid="1802976324781771067">"Endre størrelse"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Øk bredden"</string>
<string name="action_increase_height" msgid="459390020612501122">"Øk høyden"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Jobb"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
<string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Finn jobbapper her"</string>
- <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Alle jobbapper har et oransje merke og sikres av organisasjonen din. Flytt apper til startskjermen for å gjøre det enklere å finne dem."</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>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 8d6819a..fa2f9cf 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, यसमा <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> सूचनाहरू छन्</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, यसमा <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> सूचना छ</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %1$d को %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"नयाँ गृह स्क्रिन पृष्ठ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
<string name="settings_button_text" msgid="8873672322605444408">"गृहपृष्ठका सेटिङहरू"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"परिदृश्य"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"फोनलाई घुमाइँदा"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"हालको प्रदर्शन सम्बन्धी सेटिङले घुमाउने सुविधालाई अनुमति दिँदैन"</string>
<string name="icon_badging_title" msgid="874121399231955394">"सूचनाको प्रतीक जनाउने थोप्लोहरू"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"सक्रिय छ"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"निष्क्रिय छ"</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="icon_badging_service_title" msgid="2309733118428242174">"सूचनाको प्रतीक जनाउने थोप्लाहरू देखाउनुहोस्"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"गृह स्क्रिनमा आइकन थप्नुहोस्"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ अनुप्रयोगका लागि"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"आइकनको आकार परिवर्तन गर्नुहोस्"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"गृह स्क्रिनमा"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"प्रणालीको पूर्वनिर्धारित सेटिङ प्रयोग गर्नुहोस्"</string>
<string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"वर्गाकार वृत्त"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"<xliff:g id="NAME">%1$s</xliff:g>: मार्फत फोल्डर सिर्जना गर्नुहोस्"</string>
<string name="folder_created" msgid="6409794597405184510">"फोल्डर सिर्जना गरियो"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"गृह स्क्रिनमा सार्नुहोस्"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"स्क्रिनलाई बायाँ सार्नुहोस्"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"स्क्रिनलाई दायाँ सार्नुहोस्"</string>
- <string name="screen_moved" msgid="266230079505650577">"स्क्रिन सारियो"</string>
<string name="action_resize" msgid="1802976324781771067">"पुनःआकार मिलाउनुहोस्"</string>
<string name="action_increase_width" msgid="8773715375078513326">"चौडाइ बढाउनुहोस्"</string>
<string name="action_increase_height" msgid="459390020612501122">"उँचाइ बढाउनुहोस्"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा सुन्तला रङको ब्याज छ र यसलाई तपाईंको संगठनद्वारा सुरक्षित राखिएको छ । अझ सजिलो गरी पहुँच राख्नका लागि कार्यसम्बन्धी अनुप्रयोगहरूलाई तपाईंको गृहस्क्रिनमा सार्नुहोस्।"</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>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a9123bd..458b6dd 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Zoeken naar meer apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Meldingen"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tik en houd vast om snelkoppeling toe te voegen."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dubbeltik en houd vast om een snelkoppeling toe te voegen of aangepaste acties te gebruiken."</string>
<string name="out_of_space" msgid="4691004494942118364">"Er is geen ruimte meer op dit startscherm."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen ruimte meer in het vak \'Favorieten\'"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lijst met apps"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lijst met persoonlijke apps"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lijst met werk-apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Homepage"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Verwijderen"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> heeft <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> melding</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d van %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startscherm %1$d van %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nieuwe startschermpagina"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Instellingen voor homepage"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Overzicht"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Huidige scherminstelling staat draaien niet toe"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Meldingsstipjes"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aan"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Uit"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Toegang tot meldingen vereist"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Meldingsstipjes weergeven"</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="icon_shape_override_label" msgid="2977264953998281004">"Vorm van pictogram wijzigen"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"op het startscherm"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Systeemstandaard gebruiken"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Map maken met: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Map gemaakt"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Verplaatsen naar startscherm"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Scherm naar links verplaatsen"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Scherm naar rechts verplaatsen"</string>
- <string name="screen_moved" msgid="266230079505650577">"Scherm verplaatst"</string>
<string name="action_resize" msgid="1802976324781771067">"Formaat aanpassen"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Breedte vergroten"</string>
<string name="action_increase_height" msgid="459390020612501122">"Hoogte vergroten"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</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="1485375451542813426">"Elke werk-app heeft een oranje badge en wordt beveiligd door je organisatie. Verplaats apps naar je startscherm om gemakkelijker toegang te krijgen."</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>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 602bc34..0242403 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ਦੀ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾ</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, ਦੀਆਂ <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"ਰੂਪ-ਰੇਖਾ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ"</string>
<string name="icon_badging_title" msgid="874121399231955394">"ਸੂਚਨਾ ਬਿੰਦੂ"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ਚਾਲੂ"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ਬੰਦ"</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="icon_badging_service_title" msgid="2309733118428242174">"ਸੂਚਨਾ ਬਿੰਦੂ ਦਿਖਾਓ"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ਨਵੀਆਂ ਐਪਾਂ ਲਈ"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ਪ੍ਰਤੀਕ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ"</string>
<string name="icon_shape_square" msgid="633575066111622774">"ਵਰਗ"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"ਵਰਗਾਕਾਰ-ਚੱਕਰ"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ਇਸਦੇ ਨਾਲ ਫੋਲਡਰ ਬਣਾਓ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ਫੋਲਡਰ ਬਣਾਇਆ ਗਿਆ"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਮੂਵ ਕਰੋ"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਮੂਵ ਕਰੋ"</string>
- <string name="screen_moved" msgid="266230079505650577">"ਸਕ੍ਰੀਨ ਨੂੰ ਮੂਵ ਕੀਤਾ ਗਿਆ"</string>
<string name="action_resize" msgid="1802976324781771067">"ਮੁੜ ਆਕਾਰ ਦਿਓ"</string>
<string name="action_increase_width" msgid="8773715375078513326">"ਚੌੜਾਈ ਵਧਾਓ"</string>
<string name="action_increase_height" msgid="459390020612501122">"ਉਂਚਾਈ ਵਧਾਓ"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਸੰਤਰੀ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਰਾਹੀਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।"</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>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 0db8d62..558fab2 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Wyszukaj więcej aplikacji"</string>
<string name="notifications_header" msgid="1404149926117359025">"Powiadomienia"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Kliknij i przytrzymaj, by wybrać skrót."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Kliknij dwukrotnie i przytrzymaj, by wybrać skrót lub użyć działań niestandardowych."</string>
<string name="out_of_space" msgid="4691004494942118364">"Brak miejsca na tym ekranie głównym."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Brak miejsca w Ulubionych"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacji"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista aplikacji osobistych"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista aplikacji do pracy"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ekran główny"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Usuń"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
@@ -60,6 +64,12 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="many"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomień</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> powiadomienia</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> – <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> powiadomienie</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Strona %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ekran główny %1$d z %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nowa strona ekranu głównego"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</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="accessibility_action_overview" msgid="6257665857640347026">"Przegląd"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Obecne ustawienia wyświetlania nie pozwalają na obrót ekranu"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Plakietki z powiadomieniami"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Włączono"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Wyłączono"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Wymagany jest dostęp do powiadomień"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Aby pokazać plakietki z powiadomieniami, włącz powiadomienia aplikacji <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Zmień ustawienia"</string>
+ <string name="icon_badging_service_title" msgid="2309733118428242174">"Pokaż plakietki z powiadomieniami"</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="icon_shape_override_label" msgid="2977264953998281004">"Zmień kształt ikon"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na ekranie głównym"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Użyj ustawienia domyślnego"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kwadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Zaokrąglony kwadrat"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Utwórz folder z: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Folder został utworzony"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Przenieś na ekran główny"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Przenieś ekran w lewo"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Przenieś ekran w prawo"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekran został przeniesiony"</string>
<string name="action_resize" msgid="1802976324781771067">"Zmień rozmiar"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Zwiększ szerokość"</string>
<string name="action_increase_height" msgid="459390020612501122">"Zwiększ wysokość"</string>
@@ -131,9 +136,11 @@
<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">"Praca"</string>
- <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil do pracy"</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="1485375451542813426">"Każda aplikacja do pracy ma pomarańczową 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="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>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index ca4a1c6..0d3224d 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -40,9 +40,13 @@
<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="notifications_header" msgid="1404149926117359025">"Notificações"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Toque sem soltar para escolher um atalho."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toque duas vezes sem soltar para escolher um atalho ou utilize ações personalizadas."</string>
<string name="out_of_space" msgid="4691004494942118364">"Sem espaço suficiente neste Ecrã principal."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Não existe mais espaço no tabuleiro de Favoritos"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicações"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicações pessoais"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicações de trabalho"</string>
<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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><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"><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>
<string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Definições da página inicial"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo gestor"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Vista geral"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação do ecrã principal"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o telemóvel é rodado"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A definição de visualização atual não permite a rotação"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Pontos de notificação"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Ativada"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desativada"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Acesso a notificações necessário"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar pontos de notificação"</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="icon_shape_override_label" msgid="2977264953998281004">"Alterar forma do ícone"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"no ecrã principal"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utilizar a predefinição do sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado e círculo"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Criar pasta com: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Pasta criada"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Mover para o Ecrã principal"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Mover ecrã para a esquerda"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Mover ecrã para a direita"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ecrã movido"</string>
<string name="action_resize" msgid="1802976324781771067">"Redimensionar"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Aumentar largura"</string>
<string name="action_increase_height" msgid="459390020612501122">"Aumentar altura"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</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="1485375451542813426">"Cada aplicação de trabalho apresenta um emblema laranja, 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="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>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 77cc50f..73dad35 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais apps"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificações"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Toque e segure para selecionar um atalho."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Toque duas vezes na tela e segure para selecionar um atalho ou usar ações personalizadas."</string>
<string name="out_of_space" msgid="4691004494942118364">"Não há mais espaço na tela inicial."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Sem espaço na bandeja de favoritos"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista de apps"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de apps pessoais"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de apps profissionais"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Início"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="other">O 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>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova página na tela inicial"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Configurações da página inicial"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Visão geral"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação da tela inicial"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o smartphone for girado"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuração atual de exibição não permite rotação"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Pontos de notificação"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Ativado"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Desativado"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Acesso a notificações necessário"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Mostrar pontos de notificação"</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="icon_shape_override_label" msgid="2977264953998281004">"Alterar forma de ícones"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na tela inicial"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar padrão do sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado arredondado"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Criar pasta com: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Pasta criada"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Mover para a tela inicial"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Mover tela para a esquerda"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Mover tela para a direita"</string>
- <string name="screen_moved" msgid="266230079505650577">"Tela movida"</string>
<string name="action_resize" msgid="1802976324781771067">"Redimensionar"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Aumentar largura"</string>
<string name="action_increase_height" msgid="459390020612501122">"Aumentar altura"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Comerciais"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil 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="1485375451542813426">"Cada app de trabalho tem um selo laranja e é mantido em segurança pela sua organização. Mova os apps para sua tela inicial para facilitar o acesso."</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>
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 7b2409c..500512a 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Căutați mai multe aplicații"</string>
<string name="notifications_header" msgid="1404149926117359025">"Notificări"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Atingeți lung pentru a selecta o comandă rapidă."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Atingeți lung pentru a selecta o comandă rapidă sau folosiți acțiuni personalizate."</string>
<string name="out_of_space" msgid="4691004494942118364">"Nu mai este loc pe acest Ecran de pornire."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicații"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista de aplicații personale"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista de aplicații de serviciu"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ecran de pornire"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminați"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</string>
@@ -60,6 +64,11 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> de notificări</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> are <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> notificare</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ecranul de pornire %1$d din %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Pagină nouă pe ecranul de pornire"</string>
@@ -73,19 +82,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Setări pentru ecranul de pornire"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Prezentare generală"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Permiteți rotirea ecranului de pornire"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Când telefonul este rotit"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Setarea actuală a afișajului nu permite rotirea"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Puncte de notificare"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Activat"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Dezactivat"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Este necesar accesul la notificări"</string>
<string name="msg_missing_notification_access" msgid="281113995110910548">"Pentru a afișa punctele de notificare, activați notificările din aplicație pentru <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="title_change_settings" msgid="1376365968844349552">"Modificați setările"</string>
+ <string name="icon_badging_service_title" msgid="2309733118428242174">"Afișați punctele de notificare"</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="icon_shape_override_label" msgid="2977264953998281004">"Schimbați forma pictogramei"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"pe ecranul de pornire"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Folosiți setarea prestabilită a sistemului"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Pătrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Pătrat cu colțuri rotunjite"</string>
@@ -115,9 +122,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Creați dosar cu: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Dosar creat"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Mutați pe ecranul de pornire"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Mutați ecranul la stânga"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Mutați ecranul la dreapta"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ecran mutat"</string>
<string name="action_resize" msgid="1802976324781771067">"Redimensionați"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Creșteți lățimea"</string>
<string name="action_increase_height" msgid="459390020612501122">"Creșteți înălțimea"</string>
@@ -133,7 +137,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Profesionale"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil de serviciu"</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="1485375451542813426">"Fiecare aplicație de serviciu are o insignă portocalie și este păstrată în siguranță de organizația dvs. Mutați aplicațiile de serviciu pe ecranul de pornire pentru acces mai ușor."</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>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 5242322..d04ca5c 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -33,16 +33,20 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Чтобы выбрать виджет или использовать специальные действия, нажмите на него дважды и не отпускайте."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %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>
<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="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_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>
@@ -60,8 +64,14 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомление</item>
+ <item quantity="few">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
+ <item quantity="many">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомлений</item>
+ <item quantity="other">В приложении \"<xliff:g id="APP_NAME_2">%1$s</xliff:g>\" <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> уведомления</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Стр. %1$d из %2$d"</string>
- <string name="workspace_scroll_format" msgid="8458889198184077399">"Главные экран %1$d из %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Главный экран %1$d из %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Новый экран"</string>
<string name="folder_opened" msgid="94695026776264709">"Папка открыта, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Нажмите, чтобы закрыть папку"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Настройки главного экрана"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Обзор"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешить поворачивать главный экран"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Когда телефон повернут"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"В настройках отключен поворот экрана"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Значки уведомлений"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ВКЛ"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ВЫКЛ"</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="icon_badging_service_title" msgid="2309733118428242174">"Показывать значки уведомлений"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Добавлять значки"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Добавлять значки установленных приложений на главный экран."</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Изменить форму значков"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на главном экране"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Использовать системные настройки по умолчанию"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Квадрат с закругленными краями"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Создать папку с элементом <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="folder_created" msgid="6409794597405184510">"Папка создана."</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Переместить на главный экран"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Переместить экран влево"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Переместить экран вправо"</string>
- <string name="screen_moved" msgid="266230079505650577">"Экран перемещен"</string>
<string name="action_resize" msgid="1802976324781771067">"Изменить размер"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Увеличить ширину"</string>
<string name="action_increase_height" msgid="459390020612501122">"Увеличить высоту"</string>
@@ -133,7 +138,9 @@
<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="1485375451542813426">"Рабочие приложения отмечены оранжевым значком, который подтверждает, что ваша организация гарантирует их безопасность. Для удобства перенесите эти приложения на главный экран."</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>
</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 31d26e4..7a4771e 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, දැනුම්දීම් <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g>ක් ඇත</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"නව මුල් පිටුව"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home සැකසීම්"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"දළ විශ්ලේෂණය"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"දුරකථනය කරකවන විට"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"වත්මන් සංදර්ශක සැකසීම් කරකැවීමට සහාය නොදක්වයි"</string>
<string name="icon_badging_title" msgid="874121399231955394">"දැනුම්දීම් තිත්"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ක්රියාත්මකයි"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ක්රියාවිරහිතයි"</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="icon_badging_service_title" msgid="2309733118428242174">"දැනුම් දීමේ තිත් පෙන්වන්න"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"නව යෙදුම් සඳහා"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"නිරූපක හැඩය වෙනස් කරන්න"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"මුල් පිටු තිරය මත"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"පද්ධති පෙරනිමි භාවිත කරන්න"</string>
<string name="icon_shape_square" msgid="633575066111622774">"සමචතුරස්රය"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"හතරැස් කවය"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"මේ සමග ෆෝල්ඩරය සාදන්න: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ෆෝල්ඩරය සාදන ලදි"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"මුල් තිරය වෙත ගෙන යන්න"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"තිරය වම් පැත්තට ගෙනයන්න"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"තිරය දකුණු පැත්තට ගෙනයන්න"</string>
- <string name="screen_moved" msgid="266230079505650577">"තිරය ගෙන යන ලදි"</string>
<string name="action_resize" msgid="1802976324781771067">"නැවත ප්රමාණගත කිරීම"</string>
<string name="action_increase_width" msgid="8773715375078513326">"පළල වැඩි කරන්න"</string>
<string name="action_increase_height" msgid="459390020612501122">"උස වැඩි කරන්න"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"සෑම කාර්යාල යෙදුමකම තැඹිලි ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය විසින් සුරක්ෂිතව තබා ගනී. වඩා පහසුවෙන් පිවිසීමට යෙදුම් ඔබගේ මුල් පිටු තිරය වෙත ගෙන යන්න."</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>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 9567889..4d90dee 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Hľadať ďalšie aplikácie"</string>
<string name="notifications_header" msgid="1404149926117359025">"Upozornenia"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Skratku pridáte pridržaním."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Skratku pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
<string name="out_of_space" msgid="4691004494942118364">"Na tejto ploche už nie je miesto"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na paneli Obľúbené položky už nie je miesto"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Zoznam aplikácií"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Zoznam osobných aplikácií"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Zoznam pracovných aplikácií"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Domovská stránka"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrániť"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
@@ -60,6 +64,12 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="few">Aplikácia <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
+ <item quantity="many">Aplikácia <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornenia</item>
+ <item quantity="other">Aplikácia <xliff:g id="APP_NAME_2">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> upozornení</item>
+ <item quantity="one">Aplikácia <xliff:g id="APP_NAME_0">%1$s</xliff:g> má <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> upozornenie</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Nastavenia služby Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Prehľad"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Povoliť otáčanie plochy"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Pri otočení telefónu"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuálne nastavenie obrazovky nepovoľuje otáčanie"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Bodky upozornení"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Zapnuté"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Vypnuté"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Vyžaduje sa prístup k upozorneniam"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Zobrazovať bodky upozornení"</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="icon_shape_override_label" msgid="2977264953998281004">"Zmeniť tvar ikony"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na ploche"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Použiť predvolené nastavenie systému"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Štvorec"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Okrúhly štvorec"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Vytvoriť priečinok pomocou: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Priečinok bol vytvorený"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Presunúť na plochu"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Presunúť obrazovku doľava"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Presunúť obrazovku doprava"</string>
- <string name="screen_moved" msgid="266230079505650577">"Obrazovka bola posunutá"</string>
<string name="action_resize" msgid="1802976324781771067">"Zmeniť veľkosť"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Zvýšiť šírku"</string>
<string name="action_increase_height" msgid="459390020612501122">"Zväčšiť výšku"</string>
@@ -133,7 +138,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovné"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovný profil"</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="1485375451542813426">"Všetky pracovné aplikácie majú oranžový š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="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>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index ca59140..82b7912 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Iskanje več aplikacij"</string>
<string name="notifications_header" msgid="1404149926117359025">"Obvestila"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Pridržite bližnjico, da jo izberete."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Dvakrat se dotaknite bližnjice in jo pridržite, da jo izberete, ali pa uporabite dejanja po meri."</string>
<string name="out_of_space" msgid="4691004494942118364">"Na tem začetnem zaslonu ni več prostora."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"V vrstici za priljubljene ni več prostora"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Seznam aplikacij"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Seznam osebnih aplikacij"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Seznam delovnih aplikacij"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Začetni zaslon"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrani"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odstrani"</string>
@@ -60,6 +64,12 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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> obvestilo</item>
+ <item quantity="two">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestili</item>
+ <item quantity="few">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestila</item>
+ <item quantity="other">Aplikacija <xliff:g id="APP_NAME_2">%1$s</xliff:g> ima <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> obvestil</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Stran %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Začetni zaslon %1$d od %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Nova stran na začetnem zaslonu"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</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="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutna nastavitev zaslona ne dovoljuje sukanja"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Obvestilne pike"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Vklopljeno"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Izklopljeno"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Potreben je dostop do obvestil"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Pokaži obvestilne pike"</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="icon_shape_override_label" msgid="2977264953998281004">"Spremeni obliko ikon"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"na začetnem zaslonu"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Uporabi privzeto nastavitev sistema"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljen kvadrat"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Ustvarjanje mape s tem: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mapa je ustvarjena"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Premik na začetni zaslon"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Premik zaslona levo"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Premika zaslona desno"</string>
- <string name="screen_moved" msgid="266230079505650577">"Zaslon je bil premaknjen"</string>
<string name="action_resize" msgid="1802976324781771067">"Spreminjanje velikosti"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Povečanje širine"</string>
<string name="action_increase_height" msgid="459390020612501122">"Povečanje višine"</string>
@@ -133,7 +138,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Služba"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Delovni profil"</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="1485375451542813426">"Vsaka delovna aplikacija ima oranžno značko. Za varnost teh aplikacij skrbi vaša organizacija. Za preprostejši dostop premaknite aplikacije na začetni zaslon."</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>
</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 7f4c3bc..a4ad7a0 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Kërko për më shumë aplikacione"</string>
<string name="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Prek dhe mbaj prekur për të zgjedhur një shkurtore."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
<string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Lista e aplikacioneve"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Lista e aplikacioneve personale"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Lista e aplikacioneve të punës"</string>
<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>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, ka <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> njoftime</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Faqja e ekranit të ri kryesor"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</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="accessibility_action_overview" msgid="6257665857640347026">"Përmbledhje"</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>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cilësimi aktuali i afishimit nuk lejon rrotullimin"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Pikat e njoftimeve"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Aktiv"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Joaktiv"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Nevojitet qasja në njoftime"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Shfaq pikat e njoftimeve"</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="icon_shape_override_label" msgid="2977264953998281004">"Ndrysho formën e ikonës"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"në ekranin bazë"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Përdor parazgjedhjen e sisteit"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Katror"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Katror me kënde të rrumbullakëta"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Krijo një dosje me: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Dosja u krijua"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Zhvendose në Ekranin bazë"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Zhvendose ekranin në të majtë"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Zhvendose ekranin në të djathtë"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekrani u zhvendos"</string>
<string name="action_resize" msgid="1802976324781771067">"Ndrysho madhësinë"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Rrit gjerësinë"</string>
<string name="action_increase_height" msgid="459390020612501122">"Rrit lartësinë"</string>
@@ -133,7 +136,9 @@
<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="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="1485375451542813426">"Secili aplikacion pune ka një distinktiv portokalli 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="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>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 6d757fd..3ae87f9 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,11 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештење</item>
+ <item quantity="few"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> има <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> обавештења</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. страница од %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. почетни екран од %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова страница почетног екрана"</string>
@@ -73,19 +82,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Подешавања почетног екрана"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Преглед"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволи ротацију почетног екрана"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Када се телефон ротира"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Актуелно подешавање приказа не дозвољава ротацију"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Тачке за обавештења"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Укључено"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Искључено"</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="icon_badging_service_title" msgid="2309733118428242174">"Приказуј тачке за обавештења"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додај икону на почетни екран"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нове апликације"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Промените облик икона"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на почетном екрану"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Користи подразумевано системско подешавање"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Заобљени квадрат"</string>
@@ -115,9 +122,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Направите директоријум са: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Директоријум је направљен"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Премести на почетни екран"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Помери екран улево"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Помери екран удесно"</string>
- <string name="screen_moved" msgid="266230079505650577">"Екран је померен"</string>
<string name="action_resize" msgid="1802976324781771067">"Промени величину"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Повећај ширину"</string>
<string name="action_increase_height" msgid="459390020612501122">"Повећај висину"</string>
@@ -133,7 +137,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Пословне"</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="1485375451542813426">"Свака пословна апликација има наранџасту значку и штити је ваша организација. Преместите апликације на почетни екран да бисте им лакше приступали."</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>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index b61ed69..3c91c30 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Inga appar som matchar <xliff:g id="QUERY">%1$s</xliff:g> hittades"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Sök efter fler appar"</string>
<string name="notifications_header" msgid="1404149926117359025">"Aviseringar"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Tryck länge om du vill ta upp en genväg."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Tryck snabbt två gånger och håll kvar om du vill ta upp en genväg eller använda anpassade åtgärder."</string>
<string name="out_of_space" msgid="4691004494942118364">"Det finns inte plats för mer på den här startskärmen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritfältet är fullt"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Applista"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Listan Personliga appar"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Listan Jobbappar"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startskärm"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ta bort"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> har <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> avisering</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Sidan %1$d av %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskärmen %1$d av %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ny sida på startskärmen"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Inställningar för startsidan"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Översikt"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Tillåt rotering av startskärmen"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"När mobilen vrids"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Rotering tillåts inte i de nuvarande skärminställningarna"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Aviseringsprickar"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"På"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Av"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Åtkomst till aviseringar krävs"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Visa aviseringsprickar"</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="icon_shape_override_label" msgid="2977264953998281004">"Ändra form på ikoner"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"på startskärmen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Använd systemstandard"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kvirkel"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Skapa mapp med: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Mappen har skapats"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Flytta till startskärmen"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Flytta skärmen till vänster"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Flytta skärmen till höger"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skärmen har flyttats"</string>
<string name="action_resize" msgid="1802976324781771067">"Ändra storlek"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Öka bredden"</string>
<string name="action_increase_height" msgid="459390020612501122">"Öka höjden"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Arbete"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</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="1485375451542813426">"Alla jobbappar har ett orange 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="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>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 05864fa..61d7f8b 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Haikupata programu zozote zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Tafuta programu zaidi"</string>
<string name="notifications_header" msgid="1404149926117359025">"Arifa"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Gusa na ushikilie ili uchague njia ya mkato."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Gusa mara mbili na ushikilie ili uchague njia ya mkato au utumie vitendo maalum."</string>
<string name="out_of_space" msgid="4691004494942118364">"Hakuna nafasi katika skrini hii ya Mwanzo."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Hakuna nafasi zaidi katika treya ya Vipendeleo"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Orodha ya programu"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Orodha ya programu za binafsi"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Orodha ya programu za kazini"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Mwanzo"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Ondoa"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ondoa"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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.format failed for translation -->
<!-- no translation found for workspace_scroll_format (8458889198184077399) -->
@@ -75,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Mipangilio ya ukurasa wa mwanzo"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Muhtasari"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Ruhusu kuzungusha skrini ya Kwanza"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Simu inapozungushwa"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Mipangilio ya sasa ya sehemu ya Onyesho hairuhusu kuzungusha"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Vitone vya arifa"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Imewashwa"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Imezimwa"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Inahitaji idhini ya kufikia arifa"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Onyesha kitone cha arifa"</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="icon_shape_override_label" msgid="2977264953998281004">"Badilisha umbo la aikoni"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"kwenye Skrini ya mwanzo"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Tumia umbo chaguo-msingi la mfumo"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Mraba"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Mstatili wenye pembe duara"</string>
@@ -117,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Unda folda ukitumia: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Folda imeundwa"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Hamishia Skrini ya kwanza"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Sogeza skrini kushoto"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Sogeza skrini kulia"</string>
- <string name="screen_moved" msgid="266230079505650577">"Skrini imesogezwa"</string>
<string name="action_resize" msgid="1802976324781771067">"Badilisha ukubwa"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Ongeza upana"</string>
<string name="action_increase_height" msgid="459390020612501122">"Ongeza urefu"</string>
@@ -135,7 +138,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Wasifu wa 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="1485375451542813426">"Kila programu ya kazi ina beji ya rangi ya machungwa na hulindwa na shirika lako. Hamishia programu kwenye skrini yako ya kwanza ili uzifikie kwa urahisi."</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>
</resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index 72894dc..f322e9f 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -26,7 +26,6 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
- <item name="android:keyboardLayout">@layout/search_container_all_apps</item>
</style>
<!-- Workspace -->
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index aafb982..13358c0 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -40,9 +40,15 @@
<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="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
+ <!-- no translation found for long_press_shortcut_to_add (4524750017792716791) -->
+ <skip />
+ <!-- no translation found for long_accessible_way_to_add_shortcut (3327314059613154633) -->
+ <skip />
<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_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>
@@ -60,6 +66,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> பயன்பாட்டில், <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> அறிவிப்புகள் வந்துள்ளன</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> பயன்பாட்டில், <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> அறிவிப்பு வந்துள்ளது</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"புதிய முகப்புத் திரை பக்கம்"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
<string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"மேலோட்டப் பார்வை"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"முகப்புத் திரை சுழற்சியை அனுமதி"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"மொபைலைச் சுழற்றும் போது"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை"</string>
<string name="icon_badging_title" msgid="874121399231955394">"அறிவிப்புப் புள்ளிகள்"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ஆன்"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"முடக்கப்பட்டுள்ளது"</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="icon_badging_service_title" msgid="2309733118428242174">"அறிவிப்புப் புள்ளிகளைக் காட்டு"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"புதிய பயன்பாடுகளுக்கு"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ஐகான் வடிவத்தை மாற்று"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"முகப்புத் திரையில்"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"அமைப்பின் இயல்புநிலையைப் பயன்படுத்து"</string>
<string name="icon_shape_square" msgid="633575066111622774">"சதுரம்"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"சதுரவட்டம்"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"இதனுடன் கோப்புறையை உருவாக்கும்: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"கோப்புறை உருவாக்கப்பட்டது"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"முகப்புத் திரைக்கு நகர்த்து"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"திரையை இடப்புறம் நகர்த்து"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"திரையை வலப்புறம் நகர்த்து"</string>
- <string name="screen_moved" msgid="266230079505650577">"திரை நகர்த்தப்பட்டது"</string>
<string name="action_resize" msgid="1802976324781771067">"அளவு மாற்று"</string>
<string name="action_increase_width" msgid="8773715375078513326">"அகலத்தை அதிகரி"</string>
<string name="action_increase_height" msgid="459390020612501122">"உயரத்தை அதிகரி"</string>
@@ -133,7 +138,9 @@
<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="1485375451542813426">"ஒவ்வொரு பணிப் பயன்பாடும் ஆரஞ்சு நிற பேட்ஜைக் கொண்டிருக்கும். இவை, உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்."</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>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 83d32f2..3f1762d 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> నోటిఫికేషన్లను కలిగి ఉన్నారు</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> నోటిఫికేషన్ను కలిగి ఉన్నారు</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
<string name="workspace_new_page" msgid="257366611030256142">"కొత్త హోమ్ స్క్రీన్ పేజీ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్పేపర్లు"</string>
<string name="settings_button_text" msgid="8873672322605444408">"హోమ్ సెట్టింగ్లు"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"స్థూలదృష్టి"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"ఫోన్ను తిప్పినప్పుడు"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ప్రస్తుత డిస్ప్లే సెట్టింగ్ భ్రమణాన్ని అనుమతించలేదు"</string>
<string name="icon_badging_title" msgid="874121399231955394">"నోటిఫికేషన్ డాట్లు"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"ఆన్"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ఆఫ్"</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="icon_badging_service_title" msgid="2309733118428242174">"నోటిఫికేషన్ డాట్లను చూపుతుంది"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"హోమ్ స్క్రీన్కి చిహ్నాన్ని జోడించు"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"కొత్త యాప్ల కోసం"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"చిహ్న ఆకారాన్ని మార్చు"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"హోమ్ స్క్రీన్పై"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"సిస్టమ్ డిఫాల్ట్ను ఉపయోగించండి"</string>
<string name="icon_shape_square" msgid="633575066111622774">"చతురస్రం"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"చతురస్రాకార వృత్తం"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"ఈ పేరుతో ఫోల్డర్ను సృష్టించండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"ఫోల్డర్ సృష్టించబడింది"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"హోమ్స్క్రీన్కు తరలించు"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"స్క్రీన్ను ఎడమవైపుకి జరుపు"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"స్క్రీన్ను కుడివైపుకి జరుపు"</string>
- <string name="screen_moved" msgid="266230079505650577">"స్క్రీన్ జరపబడింది"</string>
<string name="action_resize" msgid="1802976324781771067">"పరిమాణం మార్చు"</string>
<string name="action_increase_width" msgid="8773715375078513326">"వెడల్పును పెంచు"</string>
<string name="action_increase_height" msgid="459390020612501122">"ఎత్తును పెంచు"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"ప్రతి కార్యాలయ యాప్కు నారింజ బ్యాడ్జ్ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్లను మీ హోమ్ స్క్రీన్కి తరలించండి."</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>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 5006d2e..cac2333 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> รายการ</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> มีการแจ้งเตือน <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> รายการ</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"หน้า %1$d จาก %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"หน้าจอหลัก %1$d จาก %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"หน้าใหม่ในหน้าจอหลัก"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
<string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าแรก"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"ภาพรวม"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"อนุญาตให้หมุนหน้าจอหลัก"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"เมื่อหมุนโทรศัพท์"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"การตั้งค่าการแสดงผลปัจจุบันไม่อนุญาตให้มีการหมุน"</string>
<string name="icon_badging_title" msgid="874121399231955394">"จุดแจ้งเตือน"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"เปิด"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"ปิด"</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="icon_badging_service_title" msgid="2309733118428242174">"แสดงจุดแจ้งเตือน"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"เพิ่มไอคอนในหน้าจอหลัก"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"สำหรับแอปใหม่"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"เปลี่ยนรูปร่างไอคอน"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ในหน้าจอหลัก"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ใช้ค่าเริ่มต้นของระบบ"</string>
<string name="icon_shape_square" msgid="633575066111622774">"สี่เหลี่ยมจัตุรัส"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"สี่เหลี่ยมขอบมน"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"สร้างโฟลเดอร์ด้วย: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"สร้างโฟลเดอร์แล้ว"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ย้ายไปที่หน้าจอหลัก"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"เลื่อนหน้าจอไปทางซ้าย"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"เลื่อนหน้าจอไปทางขวา"</string>
- <string name="screen_moved" msgid="266230079505650577">"ย้ายหน้าจอแล้ว"</string>
<string name="action_resize" msgid="1802976324781771067">"ปรับขนาด"</string>
<string name="action_increase_width" msgid="8773715375078513326">"เพิ่มความกว้าง"</string>
<string name="action_increase_height" msgid="459390020612501122">"เพิ่มความสูง"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"แอปงานแต่ละแอปมีป้ายสีส้มและได้รับการรักษาความปลอดภัยจากองค์กรของคุณ ย้ายแอปไปยังหน้าจอหลักเพื่อให้เข้าถึงได้ง่ายขึ้น"</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>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index de90f30..0b90214 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Walang nahanap na app na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Maghanap ng higit pang mga app"</string>
<string name="notifications_header" msgid="1404149926117359025">"Mga Notification"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Pindutin nang matagal para kumuha ng shortcut."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"I-double tap nang matagal para kumuha ng shortcut o gumamit ng mga custom na pagkilos."</string>
<string name="out_of_space" msgid="4691004494942118364">"Wala nang lugar sa Home screen na ito."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Wala nang lugar sa tray ng Mga Paborito"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Listahan ng mga app"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Listahan ng mga personal na app"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Listahan ng mga app sa trabaho"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Alisin"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one">May <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> notification ang <xliff:g id="APP_NAME_2">%1$s</xliff:g></item>
+ <item quantity="other">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>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Pahina %1$d ng %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d ng %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Bagong page ng home screen"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Mga setting ng Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Pangkalahatang-ideya"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Payagan ang pag-rotate ng Home screen"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Kailan maro-rotate ang telepono"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Hindi pinahihintulutan ng kasalukuyang setting ng Display ang pag-rotate"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Mga notification dot"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Naka-on"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Naka-off"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Kinakailangan ng access sa notification"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Ipakita ang mga notification dot"</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="icon_shape_override_label" msgid="2977264953998281004">"Baguhin ang hugis ng icon"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"sa Home screen"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gamitin ang default ng system"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Parisukat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Gumawa ng folder na may: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Nagawa ang folder"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Ilipat sa Home screen"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Ilipat sa kaliwa ang screen"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Ilipat sa kanan ang screen"</string>
- <string name="screen_moved" msgid="266230079505650577">"Nailipat ang screen"</string>
<string name="action_resize" msgid="1802976324781771067">"I-resize"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Dagdagan ang lapad"</string>
<string name="action_increase_height" msgid="459390020612501122">"Dagdagan ang taas"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Trabaho"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Profile 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="1485375451542813426">"Ang bawat app para sa trabaho ay may orange na badge at pinapanatiling ligtas ng iyong organisasyon. Ilipat ang mga app sa iyong Home screen para mas madaling ma-access."</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>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index d8184e4..9725544 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Başka uygulamalar ara"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirimler"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Kısayol seçmek için dokunun ve basılı tutun."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Bir kısayolu seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun."</string>
<string name="out_of_space" msgid="4691004494942118364">"Bu Ana ekranda yer kalmadı."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoriler tepsisinde başka yer kalmadı"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Uygulamalar listesi"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Kişisel uygulamalar listesi"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"İş uygulamaları listesi"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ana ekran"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Kaldır"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Yüklemeyi kaldır"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> uygulamasının <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> bildirimi var</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Sayfa %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ana ekran %1$d / %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Yeni ana ekran sayfası"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Ana ekran ayarları"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Genel bakış"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Ana ekranı döndürmeye izin ver"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon döndürüldüğünde"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Mevcut Ekran ayarı, döndürmeye izin vermiyor"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Bildirim noktaları"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Açık"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Kapalı"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Bildirim erişimi gerekli"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Bildirim noktalarını göster"</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="icon_shape_override_label" msgid="2977264953998281004">"Simge şeklini değiştir"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Ana ekranda"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem varsayılanını kullan"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kare"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Kare-daire"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Şu öğeyle klasör oluştur: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Klasör oluşturuldu"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Ana ekrana taşı"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Ekranı sola taşı"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Ekranı sağa taşı"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekran taşındı"</string>
<string name="action_resize" msgid="1802976324781771067">"Yeniden boyutlandır"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Genişliği artır"</string>
<string name="action_increase_height" msgid="459390020612501122">"Yüksekliği artır"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</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="1485375451542813426">"Her iş uygulamasında, uygulama güvenliğinin kuruluşunuz tarafından sağlandığını gösteren turuncu bir rozet bulunur. Uygulamaları daha kolay erişim için Ana ekranınıza taşıyın."</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>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index eb0d2be..47ddc7c 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,12 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
+ <item quantity="few">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
+ <item quantity="many">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщень</item>
+ <item quantity="other">Додаток <xliff:g id="APP_NAME_2">%1$s</xliff:g> має <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> сповіщення</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Сторінка %1$d з %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Головний екран %1$d з %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Нова сторінка головного екрана"</string>
@@ -73,19 +83,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Налаштування Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Огляд"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволити обертання головного екрана"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Коли телефон обертається"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Поточні налаштування дисплея не підтримують обертання"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Значки сповіщень"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Увімкнено"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Вимкнено"</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="icon_badging_service_title" msgid="2309733118428242174">"Показувати значки сповіщень"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додати значок на головний екран"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для нових додатків"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Змінити форму значка"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"на головному екрані"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Використовувати налаштування системи за умовчанням"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Квадрат із заокругленими кутами"</string>
@@ -115,9 +123,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Створити папку з: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Папку створено"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Перемістити на головний екран"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Перемістити екран ліворуч"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Перемістити екран праворуч"</string>
- <string name="screen_moved" msgid="266230079505650577">"Екран переміщено"</string>
<string name="action_resize" msgid="1802976324781771067">"Змінити розміри"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Збільшити ширину"</string>
<string name="action_increase_height" msgid="459390020612501122">"Збільшити висоту"</string>
@@ -133,7 +138,9 @@
<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="1485375451542813426">"Кожний робочий додаток має оранжевий значок. Його захищає організація. Для швидкого доступу перенесіть додатки на головний екран."</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>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 1070d89..2558076 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g> میں <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> اطلاعات ہیں</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g> میں <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> اطلاع ہے</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"صفحہ %1$d از %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"ہوم اسکرین %1$d از %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"نیا ہوم اسکرین صفحہ"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
<string name="settings_button_text" msgid="8873672322605444408">"ہوم ترتیبات"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"مجموعی جائزہ"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"جب فون گھمایا جاتا ہے"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"موجودہ ڈسپلے ترتیب گھمانے کی اجازت نہیں دیتی"</string>
<string name="icon_badging_title" msgid="874121399231955394">"اطلاعاتی ڈاٹس"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"آن"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"آف"</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="icon_badging_service_title" msgid="2309733118428242174">"اطلاعاتی ڈاٹس دکھائیں"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"آئیکن کو ہوم اسکرین میں شامل کریں"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"نئی ایپس کیلئے"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"آئیکن کی شکل تبدیل کریں"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"ہوم اسکرین پر"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"سسٹم ڈیفالٹ کا استعمال کریں"</string>
<string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"اسکورکل"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"اس کے ساتھ فولڈر بنائیں: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"فولڈر بنا دیا گیا"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"ہوم اسکرین میں منتقل کریں"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"اسکرین کو بائیں منتقل کریں"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"اسکرین کو دائیں منتقل کریں"</string>
- <string name="screen_moved" msgid="266230079505650577">"اسکرین منتقل کر دی گئی"</string>
<string name="action_resize" msgid="1802976324781771067">"سائز تبدیل کریں"</string>
<string name="action_increase_width" msgid="8773715375078513326">"چوڑائی بڑھائیں"</string>
<string name="action_increase_height" msgid="459390020612501122">"اونچائی بڑھائیں"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"ہر دفتری ایپ میں نارنجی بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔"</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>
</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 54a4ec0..e9f36a8 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"“<xliff:g id="QUERY">%1$s</xliff:g>” bilan mos hech qanday ilova topilmadi"</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Boshqa ilovalarni qidirish"</string>
<string name="notifications_header" msgid="1404149926117359025">"Bildirishnomalar"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Yorliqni tanlab olish uchun bosib turing."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning."</string>
<string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
<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">"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>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> ta bildirishnoma bor</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> ta bildirishnoma bor</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Yangi bosh ekran sahifasi"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home sozlamalari"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Umumiy ko‘rinish"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon burilganda"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Bildirishnoma belgilari"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Yoniq"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"O‘chiq"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Bildirishnomalarga ruxsat berilmagan"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Bildirishnoma belgilarini ko‘rsatish"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka qo‘shish"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"Ikonka shaklini o‘zgartirish"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"Bosh ekranda"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Standart tizim parametrlaridan foydalanish"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Qirralari aylana kvadrat"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"<xliff:g id="NAME">%1$s</xliff:g> bilan jild yaratish"</string>
<string name="folder_created" msgid="6409794597405184510">"Jild yaratildi"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Bosh ekranga ko‘chirish"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Ekranni chapga siljitish"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Ekranni o‘ngga siljitish"</string>
- <string name="screen_moved" msgid="266230079505650577">"Ekran siljitildi"</string>
<string name="action_resize" msgid="1802976324781771067">"O‘lchamini o‘zgartirish"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Enini uzaytirish"</string>
<string name="action_increase_height" msgid="459390020612501122">"Bo‘yini uzaytirish"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"Apelsinrangli nishonga ega har bir ishga oid ilova tashkilotingiz tomonidan himoyalanadi. Ishga oid ilovalarga osonroq kirish uchun ularni bosh ekranga chiqaring."</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>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 1bd7317..65a5ecc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Tìm kiếm thêm ứng dụng"</string>
<string name="notifications_header" msgid="1404149926117359025">"Thông báo"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Chạm và giữ để chọn lối tắt."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng hành động tùy chỉnh."</string>
<string name="out_of_space" msgid="4691004494942118364">"Không còn chỗ trên Màn hình chính này."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Không còn chỗ trong khay Mục yêu thích"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Danh sách ứng dụng"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Danh sách ứng dụng cá nhân"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Danh sách ứng dụng công việc"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Màn hình chính"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Xóa"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
@@ -60,6 +64,10 @@
<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="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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <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>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>, có <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> thông báo</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Trang %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Màn hình chính %1$d / %2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Trang màn hình chính mới"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Cài đặt trang chủ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Tổng quan"</string>
- <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="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cài đặt Hiển thị hiện tại không cho phép xoay"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Dấu chấm thông báo"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Đang bật"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Đã 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="icon_badging_service_title" msgid="2309733118428242174">"Hiển thị dấu chấm thông báo"</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="icon_shape_override_label" msgid="2977264953998281004">"Thay đổi hình dạng biểu tượng"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"trên Màn hình chính"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sử dụng mặc định của hệ thống"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Hình vuông"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"Hình vuông cạnh bo tròn"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Tạo thư mục bằng: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Đã tạo thư mục"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Di chuyển đến màn hình chính"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Di chuyển màn hình sang trái"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Di chuyển màn hình sang phải"</string>
- <string name="screen_moved" msgid="266230079505650577">"Đã di chuyển màn hình"</string>
<string name="action_resize" msgid="1802976324781771067">"Đổi kích thước"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Tăng chiều rộng"</string>
<string name="action_increase_height" msgid="459390020612501122">"Tăng chiều cao"</string>
@@ -133,7 +136,9 @@
<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="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="1485375451542813426">"Mỗi ứng dụng công việc đều có một huy hiệu màu cam 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="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>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3610ef0..f8dd3cd 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 个通知</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 个通知</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"第%1$d页,共%2$d页"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"主屏幕:第%1$d屏,共%2$d屏"</string>
<string name="workspace_new_page" msgid="257366611030256142">"主屏幕新页面"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
<string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"概览"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"允许旋转主屏幕"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"手机旋转时"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"当前的显示设置不允许旋转设备"</string>
<string name="icon_badging_title" msgid="874121399231955394">"通知圆点"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"开启"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"关闭"</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="icon_badging_service_title" msgid="2309733118428242174">"显示通知圆点"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"将图标添加到主屏幕"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"适用于新应用"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"更改图标形状"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"在主屏幕上"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"使用系统默认设置"</string>
<string name="icon_shape_square" msgid="633575066111622774">"方形"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"方圆形"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"创建“<xliff:g id="NAME">%1$s</xliff:g>”文件夹"</string>
<string name="folder_created" msgid="6409794597405184510">"文件夹已创建"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"移至主屏幕"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"将屏幕向左移动"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"将屏幕向右移动"</string>
- <string name="screen_moved" msgid="266230079505650577">"屏幕已移动"</string>
<string name="action_resize" msgid="1802976324781771067">"调整大小"</string>
<string name="action_increase_width" msgid="8773715375078513326">"增加宽度"</string>
<string name="action_increase_height" msgid="459390020612501122">"增加高度"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"每个工作应用均有一个橙色徽标,并由贵单位负责确保其安全。请将工作应用移到主屏幕,以便轻松访问。"</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>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 0895651..4828006 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 項通知</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 項通知</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"主畫面 %1$d,共 %2$d 個"</string>
<string name="workspace_new_page" msgid="257366611030256142">"新主畫面頁面"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"概覽"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"允許主畫面旋轉"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"「目前顯示屏」設定不允許旋轉"</string>
<string name="icon_badging_title" msgid="874121399231955394">"通知圓點"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"開啟"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"關閉"</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="icon_badging_service_title" msgid="2309733118428242174">"顯示通知圓點"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主畫面"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"變更圖示形狀"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"在主畫面上"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設設定"</string>
<string name="icon_shape_square" msgid="633575066111622774">"正方形"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"方圓形"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"使用以下項目建立資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"已建立資料夾"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"移動至主畫面"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"向左移動螢幕"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"向右移動螢幕"</string>
- <string name="screen_moved" msgid="266230079505650577">"已移動螢幕"</string>
<string name="action_resize" msgid="1802976324781771067">"重新調整大小"</string>
<string name="action_increase_width" msgid="8773715375078513326">"增加闊度"</string>
<string name="action_increase_height" msgid="459390020612501122">"增加高度"</string>
@@ -133,7 +136,9 @@
<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="1485375451542813426">"每個工作應用程式都有橙色徽章,並由您的機構負責保持安全。您可以將工作應用程式移至主畫面,以便輕鬆存取。"</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>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 76fbdb1..1a8d515 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -40,9 +40,13 @@
<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="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_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>
@@ -60,6 +64,10 @@
<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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g> 則通知</item>
+ <item quantity="one"><xliff:g id="APP_NAME_0">%1$s</xliff:g>,有 <xliff:g id="NOTIFICATION_COUNT_1">%2$d</xliff:g> 則通知</item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"主螢幕:第 %1$d 頁,共 %2$d 頁"</string>
<string name="workspace_new_page" msgid="257366611030256142">"新的主畫面頁面"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"總覽"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"目前的顯示設定不允許旋轉畫面"</string>
<string name="icon_badging_title" msgid="874121399231955394">"通知圓點"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"已啟用"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"已停用"</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="icon_badging_service_title" msgid="2309733118428242174">"顯示通知圓點"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"將圖示加到主螢幕"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"適用於新安裝的應用程式"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"變更圖示形狀"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"在主螢幕上"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設值"</string>
<string name="icon_shape_square" msgid="633575066111622774">"正方形"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"方圓形"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"建立「<xliff:g id="NAME">%1$s</xliff:g>」資料夾"</string>
<string name="folder_created" msgid="6409794597405184510">"已建立資料夾"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"移至主畫面"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"將畫面往左移"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"將畫面往右移"</string>
- <string name="screen_moved" msgid="266230079505650577">"已移動畫面"</string>
<string name="action_resize" msgid="1802976324781771067">"調整大小"</string>
<string name="action_increase_width" msgid="8773715375078513326">"增加寬度"</string>
<string name="action_increase_height" msgid="459390020612501122">"增加高度"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"公司"</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="1485375451542813426">"每個辦公應用程式都有橘色徽章,並由貴機構負責管理及確保其安全。請將辦公應用程式移至主螢幕以便輕鬆存取。"</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>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 53dbdd6..126145e 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -40,9 +40,13 @@
<string name="all_apps_no_search_results" msgid="3200346862396363786">"Azikho izinhlelo zokusebenza ezitholiwe ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="1366263386197059176">"Sesha izinhlelo zokusebenza eziningi"</string>
<string name="notifications_header" msgid="1404149926117359025">"Izaziso"</string>
+ <string name="long_press_shortcut_to_add" msgid="4524750017792716791">"Thinta futhi ubambe ukuze ukhethe isinqamuleli."</string>
+ <string name="long_accessible_way_to_add_shortcut" msgid="3327314059613154633">"Thepha kabili uphinde ubambe ukuze uphakamise isinqamuleli noma usebenzise izenzo zangokwezifiso."</string>
<string name="out_of_space" msgid="4691004494942118364">"Asisekho isikhala kulesi sikrini Sasekhaya."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Asisekho isikhala kwitreyi lezintandokazi"</string>
<string name="all_apps_button_label" msgid="8130441508702294465">"Uhlu lwezinhlelo zokusebenza"</string>
+ <string name="all_apps_button_personal_label" msgid="1315764287305224468">"Uhlu lwezinhlelo zokusebenza zomuntu siqu"</string>
+ <string name="all_apps_button_work_label" msgid="7270707118948892488">"Uhlu lwezinhlelo zokusebenza zomsebenzi"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Ikhaya"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Susa"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
@@ -60,6 +64,10 @@
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</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="badged_app_label" formatted="false" msgid="7948068486082879291">
+ <item quantity="one"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, inezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
+ <item quantity="other"><xliff:g id="APP_NAME_2">%1$s</xliff:g>, inezaziso ezingu-<xliff:g id="NOTIFICATION_COUNT_3">%2$d</xliff:g></item>
+ </plurals>
<string name="default_scroll_format" msgid="7475544710230993317">"Ikhasi elingu-%1$d kwangu-%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Isikrini sasekhaya esingu-%1$d se-%2$d"</string>
<string name="workspace_new_page" msgid="257366611030256142">"Ikhasi elisha lesikrini sasekhaya"</string>
@@ -73,19 +81,17 @@
<string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Izilungiselelo zasekhaya"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
- <string name="accessibility_action_overview" msgid="6257665857640347026">"Ukubuka konke"</string>
- <string name="allow_rotation_title" msgid="7728578836261442095">"Vumela ukuphendukiswa kwesikrini sasekhaya"</string>
- <string name="allow_rotation_desc" msgid="8662546029078692509">"Uma ifoni iphendukiswa"</string>
- <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Isilungiselelo sesiboniso samanje asivumeli ukuzungezisa"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Amachashazi esaziso"</string>
<string name="icon_badging_desc_on" msgid="2627952638544674079">"Kuvuliwe"</string>
<string name="icon_badging_desc_off" msgid="5503319969924580241">"Kuvaliwe"</string>
<string name="title_missing_notification_access" msgid="7503287056163941064">"Ukufinyelela izaziso kuyadingeka"</string>
<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="icon_badging_service_title" msgid="2309733118428242174">"Bonisa amacashazi esaziso"</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="icon_shape_override_label" msgid="2977264953998281004">"Shintsha isimo sesithonjana"</string>
+ <string name="icon_shape_override_label_location" msgid="3841607380657692863">"kusikrini sasekhaya"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sebenzisa okuzenzakalelayo kwesistimu"</string>
<string name="icon_shape_square" msgid="633575066111622774">"Isikwele"</string>
<string name="icon_shape_squircle" msgid="5658049910802669495">"I-Squircle"</string>
@@ -115,9 +121,6 @@
<string name="create_folder_with" msgid="4050141361160214248">"Dala ifolda nge-: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_created" msgid="6409794597405184510">"Ifolda idaliwe"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Hambisa kusikrini sasekhaya"</string>
- <string name="action_move_screen_left" msgid="8854216831569401665">"Hambisa isikrini kwesokunxele"</string>
- <string name="action_move_screen_right" msgid="329334910274311123">"Hambisa isikrini kwesokudla"</string>
- <string name="screen_moved" msgid="266230079505650577">"Isikrini sihanjisiwe"</string>
<string name="action_resize" msgid="1802976324781771067">"Shintsha usayizi"</string>
<string name="action_increase_width" msgid="8773715375078513326">"Khuphula ububanzi"</string>
<string name="action_increase_height" msgid="459390020612501122">"Khuphula ubude"</string>
@@ -133,7 +136,9 @@
<string name="all_apps_work_tab" msgid="4884822796154055118">"Umsebenzi"</string>
<string name="work_profile_toggle_label" msgid="3081029915775481146">"Iphrofayela yomsebenzi"</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="1485375451542813426">"Uhlo lokusebenza ngalunye lomsebenzi linebheji ewolintshi futhi igcinwa iphephile inhlangano yakho. Hambisa izinhlelo zokusebenza esikrinini sakho sasekhaya ngokufinyelela okulula."</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>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 2033d46..64ca05e 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -44,7 +44,6 @@
<enum name="widget_section" value="3" />
<enum name="shortcut_popup" value="4" />
</attr>
- <attr name="deferShadowGeneration" format="boolean" />
<attr name="centerVertically" format="boolean" />
</declare-styleable>
@@ -101,8 +100,6 @@
<!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
<attr name="numFolderRows" format="integer" />
<attr name="numFolderColumns" format="integer" />
- <!-- minAllAppsPredictionColumns defaults to numColumns, if not specified -->
- <attr name="minAllAppsPredictionColumns" format="integer" />
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 3dddac2..a40afe1 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -101,6 +101,9 @@
<!-- Name of a subclass of com.android.launcher3.util.InstantAppResolver. Can be empty. -->
<string name="instant_app_resolver_class" translatable="false"></string>
+ <!-- Name of a main process initializer class. -->
+ <string name="main_process_initializer_class" translatable="false"></string>
+
<!-- Package name of the default wallpaper picker. -->
<string name="wallpaper_picker_package" translatable="false"></string>
@@ -113,6 +116,13 @@
<!-- View ID used by cell layout to jail its content -->
<item type="id" name="cell_layout_jail_id" />
+ <!-- Tag id used for view scrim -->
+ <item type="id" name="view_scrim" />
+
+ <!-- View IDs to store item highlight information -->
+ <item type="id" name="view_unhighlight_background" />
+ <item type="id" name="view_highlighted" />
+
<!-- Popup items -->
<integer name="config_popupOpenCloseDuration">150</integer>
<integer name="config_popupArrowOpenDuration">80</integer>
@@ -121,7 +131,7 @@
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
<item type="id" name="action_uninstall" />
- <item type="id" name="action_info" />
+ <item type="id" name="action_reconfigure" />
<item type="id" name="action_add_to_workspace" />
<item type="id" name="action_move" />
<item type="id" name="action_move_to_workspace" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2aff936..b1ad11e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -23,10 +23,6 @@
<dimen name="dynamic_grid_min_page_indicator_size">24dp</dimen>
<dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
- <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
- <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
- <dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
- <dimen name="dynamic_grid_overview_bar_spacer_width">25dp</dimen>
<dimen name="dynamic_grid_workspace_top_padding">8dp</dimen>
<dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
<!-- Minimum space between workspace and hotseat in spring loaded mode -->
@@ -79,7 +75,7 @@
<!-- All Apps -->
<dimen name="all_apps_button_scale_down">0dp</dimen>
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
- <dimen name="all_apps_search_bar_height">60dp</dimen>
+ <dimen name="all_apps_search_bar_bottom_padding">30dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
<dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
<dimen name="all_apps_background_canvas_width">700dp</dimen>
@@ -87,7 +83,6 @@
<dimen name="all_apps_header_tab_height">50dp</dimen>
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
<dimen name="all_apps_header_top_padding">36dp</dimen>
- <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<dimen name="all_apps_tabs_side_padding">12dp</dimen>
@@ -142,8 +137,6 @@
<dimen name="spring_loaded_panel_border">1dp</dimen>
<!-- Folders -->
- <!-- The size of the padding on the preview background drawable -->
- <dimen name="folder_preview_padding">10dp</dimen>
<dimen name="page_indicator_dot_size">8dp</dimen>
<dimen name="folder_cell_x_padding">9dp</dimen>
@@ -228,5 +221,6 @@
<dimen name="swipe_helper_falsing_threshold">70dp</dimen>
<!-- Overview -->
- <dimen name="options_menu_icon_size">48dp</dimen>
+ <dimen name="options_menu_icon_size">24dp</dimen>
+ <dimen name="options_menu_thumb_size">32dp</dimen>
</resources>
diff --git a/res/values/drawables.xml b/res/values/drawables.xml
index fea17b1..1367174 100644
--- a/res/values/drawables.xml
+++ b/res/values/drawables.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<resources>
- <drawable name="ic_info_shadow">@drawable/ic_info_no_shadow</drawable>
+ <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>
</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ee09946..bcb90e3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -72,6 +72,11 @@
<string name="notifications_header">Notifications</string>
<!-- Drag and drop -->
+ <!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
+ <string name="long_press_shortcut_to_add">Touch & hold to pick up a shortcut.</string>
+ <!-- Accessibility spoken hint message in deep shortcut menu, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=200] -->
+ <string name="long_accessible_way_to_add_shortcut">Double-tap & hold to pick up a shortcut or use custom actions.</string>
+
<skip />
<!-- Error message when user has filled a home screen -->
<string name="out_of_space">No more room on this Home screen.</string>
@@ -80,6 +85,9 @@
<!-- All applications label -->
<string name="all_apps_button_label">Apps list</string>
+ <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>
@@ -132,6 +140,11 @@
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
<string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
+ <!-- The format string for when an app has a notification dot (meaning it has associated notifications). -->
+ <plurals name="badged_app_label">
+ <item quantity="one"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="1">%2$d</xliff:g> notification</item>
+ <item quantity="other"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="3">%2$d</xliff:g> notifications</item>
+ </plurals>
<skip />
<!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
@@ -164,16 +177,8 @@
<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>
- <!-- Text for custom accessibility action to go to the overview mode, where users can look and change the overall UI of the launcher. -->
- <string name="accessibility_action_overview">Overview</string>
<!-- Strings for settings -->
- <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
- <string name="allow_rotation_title">Allow Home screen rotation</string>
- <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
- <string name="allow_rotation_desc">When phone is rotated</string>
- <!-- Text explaining that rotation is disabled in Display settings. 'Display' refers to the Display section in system settings [CHAR LIMIT=100] -->
- <string name="allow_rotation_blocked_desc">Current Display setting doesn\'t permit rotation</string>
<!-- Title for Notification dots setting. Tapping this will link to the system Notifications settings screen where the user can turn off notification dots globally. [CHAR LIMIT=50] -->
<string name="icon_badging_title">Notification dots</string>
<!-- Text to indicate that the system icon badging setting is on [CHAR LIMIT=100] -->
@@ -186,6 +191,8 @@
<string name="msg_missing_notification_access">To show Notification Dots, turn on app notifications for <xliff:g id="name" example="My App">%1$s</xliff:g></string>
<!-- Button text in the confirmation dialog which would take the user to the system settings [CHAR LIMIT=50] -->
<string name="title_change_settings">Change settings</string>
+ <!-- Summary for Notification dots setting. Tapping this will link enable/disable notification dots feature on the home screen. [CHAR LIMIT=50] -->
+ <string name="icon_badging_service_title">Show notification dots</string>
<!-- Label for the setting that allows the automatic placement of launcher shortcuts for applications and games installed on the device [CHAR LIMIT=40] -->
<string name="auto_add_shortcuts_label">Add icon to Home screen</string>
@@ -194,6 +201,8 @@
<!-- Developer setting to change the shape of icons on home screen. [CHAR LIMIT=50] -->
<string name="icon_shape_override_label">Change icon shape</string>
+ <!-- Subtext explaining that the icons will only be affected on the home screen. This text follows the actual icon action: Change icon shape, on Home screen [CHAR LIMIT=100] -->
+ <string name="icon_shape_override_label_location">on Home screen</string>
<!-- Option to not change the icon shape on home screen and use the system default setting instead. [CHAR LIMIT=50] -->
<string name="icon_shape_system_default">Use system default</string>
<!-- Option to change the shape of the home screen icons to a square. [CHAR LIMIT=50] -->
@@ -277,15 +286,6 @@
<!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
<string name="action_move_to_workspace">Move to Home screen</string>
- <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
- <string name="action_move_screen_left">Move screen to left</string>
-
- <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
- <string name="action_move_screen_right">Move screen to right</string>
-
- <!-- Accessibility confirmation when a screen was moved. -->
- <string name="screen_moved">Screen moved</string>
-
<!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
<string name="action_resize">Resize</string>
@@ -329,11 +329,12 @@
<!-- Title of an overlay in All Apps. This overlay is letting a user know about their work profile, which is managed by their employer. "Work apps" are apps in a user's work profile.-->
<string name="bottom_work_tab_user_education_title">Find work apps here</string>
<!-- Text in an overlay in All Apps. This overlay is letting a user know about their work profile, which is managed by their employer.-->
- <string name="bottom_work_tab_user_education_body">Each work app has an orange badge and is kept secure by your organization. Move apps to your Home screen for easier access.</string>
+ <string name="bottom_work_tab_user_education_body">Each work app has a badge and is kept secure by your organization. Move apps to your Home screen for easier access.</string>
<!-- This string is in the work profile tab when a user has All Apps open on their phone. It describes the label of a toggle, "Work profile," as being managed by the user's employer.
"Organization" is used to represent a variety of businesses, non-profits, and educational institutions).-->
<string name="work_mode_on_label">Managed by your organization</string>
<!-- This string appears under a the label of a toggle in the work profile tab on a user's phone. It describes the status of the toggle, "Work profile," when it's turned off. "Work profile" means a separate profile on a user's phone that's speficially for their work apps and is managed by their company.-->
<string name="work_mode_off_label">Notifications and apps are off</string>
-
+ <string name="bottom_work_tab_user_education_close_button">Close</string>
+ <string name="bottom_work_tab_user_education_closed">Closed</string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8cc4743..38b5dae 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -25,7 +25,6 @@
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorEdgeEffect">#FF757575</item>
- <item name="android:keyboardLayout">@layout/search_container_all_apps</item>
</style>
<style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
@@ -113,9 +112,9 @@
<item name="android:focusable">true</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:singleLine">true</item>
- <item name="android:ellipsize">marquee</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
<item name="android:fontFamily">sans-serif-condensed</item>
+ <item name="android:defaultFocusHighlightEnabled">false</item>
<!-- No shadows in the base theme -->
<item name="android:shadowRadius">0</item>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 30c1c54..a34f225 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -25,7 +25,6 @@
launcher:numColumns="3"
launcher:numFolderRows="2"
launcher:numFolderColumns="3"
- launcher:minAllAppsPredictionColumns="3"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="3"
@@ -40,7 +39,6 @@
launcher:numColumns="3"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
- launcher:minAllAppsPredictionColumns="3"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="3"
@@ -55,7 +53,6 @@
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -70,7 +67,6 @@
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -85,7 +81,6 @@
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -100,7 +95,6 @@
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -115,7 +109,6 @@
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -130,7 +123,6 @@
launcher:numColumns="5"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="56"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="5"
@@ -145,7 +137,6 @@
launcher:numColumns="6"
launcher:numFolderRows="4"
launcher:numFolderColumns="5"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="64"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="7"
@@ -160,7 +151,6 @@
launcher:numColumns="6"
launcher:numFolderRows="4"
launcher:numFolderColumns="5"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="76"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="7"
@@ -175,7 +165,6 @@
launcher:numColumns="7"
launcher:numFolderRows="6"
launcher:numFolderColumns="6"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="100"
launcher:iconTextSize="20.0"
launcher:numHotseatIcons="7"
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 28a35b8..7bb19f3 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -37,13 +37,6 @@
android:persistent="true"
/>
- <SwitchPreference
- android:key="pref_allowRotation"
- android:title="@string/allow_rotation_title"
- android:defaultValue="@bool/allow_rotation"
- android:persistent="true"
- />
-
<ListPreference
android:key="pref_override_icon_shape"
android:title="@string/icon_shape_override_label"
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 12f022f..f34cf0d 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -24,9 +24,9 @@
import android.view.View;
import android.widget.LinearLayout;
-import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -92,7 +92,8 @@
public final void close(boolean animate) {
animate &= !Utilities.isPowerSaverOn(getContext());
handleClose(animate);
- Launcher.getLauncher(getContext()).getUserEventDispatcher().resetElapsedContainerMillis();
+ BaseActivity.fromContext(getContext()).getUserEventDispatcher()
+ .resetElapsedContainerMillis("container closed");
}
protected abstract void handleClose(boolean animate);
@@ -119,8 +120,8 @@
}
protected static <T extends AbstractFloatingView> T getOpenView(
- Launcher launcher, @FloatingViewType int type) {
- DragLayer dragLayer = launcher.getDragLayer();
+ BaseDraggingActivity activity, @FloatingViewType int type) {
+ BaseDragLayer dragLayer = activity.getDragLayer();
// Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
// and will be one of the last views.
for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
@@ -135,16 +136,17 @@
return null;
}
- public static void closeOpenContainer(Launcher launcher, @FloatingViewType int type) {
- AbstractFloatingView view = getOpenView(launcher, type);
+ public static void closeOpenContainer(BaseDraggingActivity activity,
+ @FloatingViewType int type) {
+ AbstractFloatingView view = getOpenView(activity, type);
if (view != null) {
view.close(true);
}
}
- public static void closeOpenViews(Launcher launcher, boolean animate,
+ public static void closeOpenViews(BaseDraggingActivity activity, boolean animate,
@FloatingViewType int type) {
- DragLayer dragLayer = launcher.getDragLayer();
+ BaseDragLayer dragLayer = activity.getDragLayer();
// Iterate in reverse order. AbstractFloatingView is added later to the dragLayer,
// and will be one of the last views.
for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
@@ -158,16 +160,16 @@
}
}
- public static void closeAllOpenViews(Launcher launcher, boolean animate) {
- closeOpenViews(launcher, animate, TYPE_ALL);
- launcher.finishAutoCancelActionMode();
+ public static void closeAllOpenViews(BaseDraggingActivity activity, boolean animate) {
+ closeOpenViews(activity, animate, TYPE_ALL);
+ activity.finishAutoCancelActionMode();
}
- public static void closeAllOpenViews(Launcher launcher) {
- closeAllOpenViews(launcher, true);
+ public static void closeAllOpenViews(BaseDraggingActivity activity) {
+ closeAllOpenViews(activity, true);
}
- public static AbstractFloatingView getTopOpenView(Launcher launcher) {
- return getOpenView(launcher, TYPE_ALL);
+ public static AbstractFloatingView getTopOpenView(BaseDraggingActivity activity) {
+ return getOpenView(activity, TYPE_ALL);
}
}
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 4a0f52d..ae631a4 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.graphics.Point;
+import android.view.Display;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
@@ -37,6 +39,10 @@
protected SystemUiController mSystemUiController;
private boolean mStarted;
+ // When the recents animation is running, the visibility of the Launcher is managed by the
+ // animation
+ private boolean mForceInvisible;
+ private boolean mUserActive;
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
@@ -48,8 +54,7 @@
public final UserEventDispatcher getUserEventDispatcher() {
if (mUserEventDispatcher == null) {
- mUserEventDispatcher = UserEventDispatcher.newInstance(this,
- mDeviceProfile.isLandscape, isInMultiWindowModeCompat());
+ mUserEventDispatcher = UserEventDispatcher.newInstance(this, mDeviceProfile);
}
return mUserEventDispatcher;
}
@@ -84,8 +89,21 @@
}
@Override
+ protected void onResume() {
+ mUserActive = true;
+ super.onResume();
+ }
+
+ @Override
+ protected void onUserLeaveHint() {
+ mUserActive = false;
+ super.onUserLeaveHint();
+ }
+
+ @Override
protected void onStop() {
mStarted = false;
+ mForceInvisible = false;
super.onStop();
}
@@ -93,14 +111,50 @@
return mStarted;
}
+ public boolean isUserActive() {
+ return mUserActive;
+ }
+
public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
mDPChangeListeners.add(listener);
}
+ public void removeOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+ mDPChangeListeners.remove(listener);
+ }
+
protected void dispatchDeviceProfileChanged() {
- int count = mDPChangeListeners.size();
- for (int i = 0; i < count; i++) {
+ for (int i = mDPChangeListeners.size() - 1; i >= 0; i--) {
mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
}
}
+
+ /**
+ * Used to set the override visibility state, used only to handle the transition home with the
+ * recents animation.
+ * @see LauncherAppTransitionManagerImpl.getWallpaperOpenRunner()
+ */
+ public void setForceInvisible(boolean invisible) {
+ mForceInvisible = invisible;
+ }
+
+ /**
+ * @return Wether this activity should be considered invisible regardless of actual visibility.
+ */
+ public boolean isForceInvisible() {
+ return mForceInvisible;
+ }
+
+ /**
+ * Sets the device profile, adjusting it accordingly in case of multi-window
+ */
+ protected void setDeviceProfile(DeviceProfile dp) {
+ mDeviceProfile = dp;
+ if (isInMultiWindowModeCompat()) {
+ Display display = getWindowManager().getDefaultDisplay();
+ Point mwSize = new Point();
+ display.getSize(mwSize);
+ mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
+ }
+ }
}
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
new file mode 100644
index 0000000..bde9ad3
--- /dev/null
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -0,0 +1,253 @@
+/*
+ * 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 android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.StrictMode;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.badge.BadgeInfo;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.views.BaseDragLayer;
+
+/**
+ * Extension of BaseActivity allowing support for drag-n-drop
+ */
+public abstract class BaseDraggingActivity extends BaseActivity
+ implements WallpaperColorInfo.OnChangeListener {
+
+ private static final String TAG = "BaseDraggingActivity";
+
+ // The Intent extra that defines whether to ignore the launch animation
+ public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
+ "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
+
+ // When starting an action mode, setting this tag will cause the action mode to be cancelled
+ // automatically when user interacts with the launcher.
+ public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
+ private ActionMode mCurrentActionMode;
+ protected boolean mIsSafeModeEnabled;
+
+ private OnStartCallback mOnStartCallback;
+
+ private int mThemeRes = R.style.LauncherTheme;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIsSafeModeEnabled = getPackageManager().isSafeMode();
+
+ // Update theme
+ WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
+ wallpaperColorInfo.addOnChangeListener(this);
+ int themeRes = getThemeRes(wallpaperColorInfo);
+ if (themeRes != mThemeRes) {
+ mThemeRes = themeRes;
+ setTheme(themeRes);
+ }
+ }
+
+ @Override
+ public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+ if (mThemeRes != getThemeRes(wallpaperColorInfo)) {
+ recreate();
+ }
+ }
+
+ protected int getThemeRes(WallpaperColorInfo wallpaperColorInfo) {
+ if (wallpaperColorInfo.isDark()) {
+ return R.style.LauncherThemeDark;
+ } else if (wallpaperColorInfo.supportsDarkText()) {
+ return R.style.LauncherThemeDarkText;
+ } else {
+ return R.style.LauncherTheme;
+ }
+ }
+
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ mCurrentActionMode = mode;
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ super.onActionModeFinished(mode);
+ mCurrentActionMode = null;
+ }
+
+ public boolean finishAutoCancelActionMode() {
+ if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
+ mCurrentActionMode.finish();
+ return true;
+ }
+ return false;
+ }
+
+ public abstract BaseDragLayer getDragLayer();
+
+ public abstract <T extends View> T getOverviewPanel();
+
+ public abstract View getRootView();
+
+ public abstract BadgeInfo getBadgeInfoForItem(ItemInfo info);
+
+ public abstract void invalidateParent(ItemInfo info);
+
+ public static BaseDraggingActivity fromContext(Context context) {
+ if (context instanceof BaseDraggingActivity) {
+ return (BaseDraggingActivity) context;
+ }
+ return ((BaseDraggingActivity) ((ContextWrapper) context).getBaseContext());
+ }
+
+ public Rect getViewBounds(View v) {
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+ }
+
+ public final Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) {
+ ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions);
+ return activityOptions == null ? null : activityOptions.toBundle();
+ }
+
+ public abstract ActivityOptions getActivityLaunchOptions(
+ View v, boolean useDefaultLaunchOptions);
+
+ public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+ if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+ Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ // Only launch using the new animation if the shortcut has not opted out (this is a
+ // private contract between launcher and may be ignored in the future).
+ boolean useLaunchAnimation = (v != null) &&
+ !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+ Bundle optsBundle = useLaunchAnimation
+ ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
+ : null;
+
+ UserHandle user = item == null ? null : item.user;
+
+ // Prepare intent
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (v != null) {
+ intent.setSourceBounds(getViewBounds(v));
+ }
+ try {
+ boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
+ && (item instanceof ShortcutInfo)
+ && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
+ || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && !((ShortcutInfo) item).isPromise();
+ if (isShortcut) {
+ // Shortcuts need some special checks due to legacy reasons.
+ startShortcutIntentSafely(intent, optsBundle, item);
+ } else if (user == null || user.equals(Process.myUserHandle())) {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
+ } else {
+ LauncherAppsCompat.getInstance(this).startActivityForProfile(
+ intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+ }
+ getUserEventDispatcher().logAppLaunch(v, intent);
+ return true;
+ } catch (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);
+ }
+ return false;
+ }
+
+ private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+ try {
+ StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
+ try {
+ // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
+ // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
+ // is enabled by default on NYC.
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
+ .penaltyLog().build());
+
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ String id = ((ShortcutInfo) info).getDeepShortcutId();
+ String packageName = intent.getPackage();
+ DeepShortcutManager.getInstance(this).startShortcut(
+ packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+ } else {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
+ }
+ } finally {
+ StrictMode.setVmPolicy(oldPolicy);
+ }
+ } catch (SecurityException e) {
+ if (!onErrorStartingShortcut(intent, info)) {
+ throw e;
+ }
+ }
+ }
+
+ protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+ return false;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ if (mOnStartCallback != null) {
+ mOnStartCallback.onActivityStart(this);
+ mOnStartCallback = null;
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ WallpaperColorInfo.getInstance(this).removeOnChangeListener(this);
+ }
+
+ public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
+ mOnStartCallback = callback;
+ }
+
+ /**
+ * Callback for listening for onStart
+ */
+ public interface OnStartCallback<T extends BaseDraggingActivity> {
+
+ void onActivityStart(T activity);
+ }
+}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index cc13263..74b9cfa 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -33,8 +33,7 @@
* <li> Enable fast scroller.
* </ul>
*/
-public abstract class BaseRecyclerView extends RecyclerView
- implements RecyclerView.OnItemTouchListener {
+public abstract class BaseRecyclerView extends RecyclerView {
protected RecyclerViewFastScroller mScrollbar;
@@ -51,12 +50,6 @@
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- addOnItemTouchListener(this);
- }
-
- @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
bindFastScrollbar();
@@ -69,40 +62,8 @@
onUpdateScrollbar(0);
}
- /**
- * We intercept the touch handling only to support fast scrolling when initiated from the
- * scroll bar. Otherwise, we fall back to the default RecyclerView touch handling.
- */
- @Override
- public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
- return handleTouchEvent(ev);
- }
-
- @Override
- public void onTouchEvent(RecyclerView rv, MotionEvent ev) {
- handleTouchEvent(ev);
- }
-
- /**
- * Handles the touch event and determines whether to show the fast scroller (or updates it if
- * it is already showing).
- */
- private boolean handleTouchEvent(MotionEvent ev) {
- // Move to mScrollbar's coordinate system.
- // We need to take parent into account (view pager's location)
- ViewGroup parent = (ViewGroup) getParent();
- int left = parent.getLeft() - mScrollbar.getLeft();
- int top = parent.getTop() + getTop() - mScrollbar.getTop() - getScrollBarTop();
- ev.offsetLocation(left, top);
- try {
- return mScrollbar.handleTouchEvent(ev);
- } finally {
- ev.offsetLocation(-left, -top);
- }
- }
-
- public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
- // DO NOT REMOVE, NEEDED IMPLEMENTATION FOR M BUILDS
+ public RecyclerViewFastScroller getScrollbar() {
+ return mScrollbar;
}
public int getScrollBarTop() {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 46d7227..41bfcb7 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
@@ -29,6 +28,7 @@
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.graphics.ColorUtils;
+import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.Property;
import android.util.TypedValue;
@@ -37,7 +37,6 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
-import android.view.ViewParent;
import android.widget.TextView;
import com.android.launcher3.IconCache.IconLoadRequest;
@@ -46,9 +45,7 @@
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.DrawableFactory;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.model.PackageItemInfo;
@@ -68,18 +65,14 @@
private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
- private final Launcher mLauncher;
+ private final BaseDraggingActivity mActivity;
private Drawable mIcon;
private final boolean mCenterVertically;
private final CheckLongPressHelper mLongPressHelper;
- private final HolographicOutlineHelper mOutlineHelper;
private final StylusEventHelper mStylusEventHelper;
private final float mSlop;
- private Bitmap mPressedBackground;
-
- private final boolean mDeferShadowGenerationOnTouch;
private final boolean mLayoutHorizontal;
private final int mIconSize;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -140,15 +133,13 @@
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mLauncher = Launcher.getLauncher(context);
- DeviceProfile grid = mLauncher.getDeviceProfile();
+ mActivity = BaseDraggingActivity.fromContext(context);
+ DeviceProfile grid = mActivity.getDeviceProfile();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
- mDeferShadowGenerationOnTouch =
- a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
int display = a.getInteger(R.styleable.BubbleTextView_iconDisplay, DISPLAY_WORKSPACE);
int defaultIconSize = grid.iconSizePx;
@@ -173,11 +164,18 @@
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
- mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
- setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+ setEllipsize(TruncateAt.END);
+ setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
}
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ // Disable marques when not focused to that, so that updating text does not cause relayout.
+ setEllipsize(focused ? TruncateAt.MARQUEE : TruncateAt.END);
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ }
+
/**
* Resets the view so it can be recycled.
*/
@@ -231,7 +229,6 @@
FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
- iconDrawable.setIsDisabled(info.isDisabled());
setIcon(iconDrawable);
setText(info.title);
if (info.contentDescription != null) {
@@ -291,13 +288,6 @@
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- // So that the pressed outline is visible immediately on setStayPressed(),
- // we pre-create it on ACTION_DOWN (it takes a small but perceptible amount of time
- // to create it)
- if (!mDeferShadowGenerationOnTouch && mPressedBackground == null) {
- mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
- }
-
// If we're in a stylus button press, don't check for long press.
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
@@ -305,12 +295,6 @@
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
- // If we've touched down and up on an item, and it's still not "pressed", then
- // destroy the pressed outline
- if (!isPressed()) {
- mPressedBackground = null;
- }
-
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
@@ -324,22 +308,6 @@
void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed;
- if (!stayPressed) {
- HolographicOutlineHelper.getInstance(getContext()).recycleShadowBitmap(mPressedBackground);
- mPressedBackground = null;
- } else {
- if (mPressedBackground == null) {
- mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
- }
- }
-
- // Only show the shadow effect when persistent pressed state is set.
- ViewParent parent = getParent();
- if (parent != null && parent.getParent() instanceof BubbleTextShadowHandler) {
- ((BubbleTextShadowHandler) parent.getParent()).setPressedIcon(
- this, mPressedBackground);
- }
-
refreshDrawableState();
}
@@ -356,26 +324,12 @@
}
@Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (super.onKeyDown(keyCode, event)) {
- // Pre-create shadow so show immediately on click.
- if (mPressedBackground == null) {
- mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
- }
- return true;
- }
- return false;
- }
-
- @Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
// Unlike touch events, keypress event propagate pressed state change immediately,
// without waiting for onClickHandler to execute. Disable pressed state changes here
// to avoid flickering.
mIgnorePressedStateChange = true;
boolean result = super.onKeyUp(keyCode, event);
-
- mPressedBackground = null;
mIgnorePressedStateChange = false;
refreshDrawableState();
return result;
@@ -474,7 +428,7 @@
}
}
- private void setTextAlpha(int alpha) {
+ public void setTextAlpha(int alpha) {
super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
}
@@ -547,10 +501,10 @@
public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
if (mIcon instanceof FastBitmapDrawable) {
boolean wasBadged = mBadgeInfo != null;
- mBadgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
+ mBadgeInfo = mActivity.getBadgeInfoForItem(itemInfo);
boolean isBadged = mBadgeInfo != null;
float newBadgeScale = isBadged ? 1f : 0;
- mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
+ mBadgeRenderer = mActivity.getDeviceProfile().mBadgeRenderer;
if (wasBadged || isBadged) {
// Animate when a badge is first added or when it is removed.
if (animate && (wasBadged ^ isBadged) && isShown()) {
@@ -560,6 +514,15 @@
invalidate();
}
}
+ if (itemInfo.contentDescription != null) {
+ if (hasBadge()) {
+ int count = mBadgeInfo.getNotificationCount();
+ setContentDescription(getContext().getResources().getQuantityString(
+ R.plurals.badged_app_label, count, itemInfo.contentDescription, count));
+ } else {
+ setContentDescription(itemInfo.contentDescription);
+ }
+ }
}
}
@@ -567,31 +530,30 @@
* Sets the icon for this view based on the layout direction.
*/
private void setIcon(Drawable icon) {
- mIcon = icon;
- mIcon.setBounds(0, 0, mIconSize, mIconSize);
if (mIsIconVisible) {
- applyCompoundDrawables(mIcon);
+ applyCompoundDrawables(icon);
}
+ mIcon = icon;
}
public void setIconVisible(boolean visible) {
mIsIconVisible = visible;
- mDisableRelayout = true;
- Drawable icon = mIcon;
- if (!visible) {
- icon = new ColorDrawable(Color.TRANSPARENT);
- icon.setBounds(0, 0, mIconSize, mIconSize);
- }
+ Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
applyCompoundDrawables(icon);
- mDisableRelayout = false;
}
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);
}
+ mDisableRelayout = false;
}
@Override
@@ -617,15 +579,7 @@
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof ShortcutInfo) {
applyFromShortcutInfo((ShortcutInfo) info);
- FolderIconPreviewVerifier verifier =
- new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
- if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
- View folderIcon =
- mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
- if (folderIcon != null) {
- folderIcon.invalidate();
- }
- }
+ mActivity.invalidateParent(info);
} else if (info instanceof PackageItemInfo) {
applyFromPackageItemInfo((PackageItemInfo) info);
}
@@ -654,11 +608,4 @@
public int getIconSize() {
return mIconSize;
}
-
- /**
- * Interface to be implemented by the grand parent to allow click shadow effect.
- */
- public interface BubbleTextShadowHandler {
- void setPressedIcon(BubbleTextView icon, Bitmap background);
- }
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index a9cf8cc..c866880 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -108,6 +108,12 @@
setContentDescription(mText);
}
+ protected void updateText(int resId) {
+ setText(resId);
+ mText = getText();
+ setContentDescription(mText);
+ }
+
protected void setDrawable(int resId) {
// We do not set the drawable in the xml as that inflates two drawables corresponding to
// drawableLeft and drawableStart.
@@ -236,9 +242,7 @@
protected abstract boolean supportsDrop(ItemInfo info);
- public boolean supportsAccessibilityDrop(ItemInfo info) {
- return supportsDrop(info);
- }
+ public abstract boolean supportsAccessibilityDrop(ItemInfo info, View view);
@Override
public boolean isDropEnabled() {
@@ -344,9 +348,10 @@
}
public void setTextVisible(boolean isVisible) {
- if (mTextVisible != isVisible) {
+ CharSequence newText = isVisible ? mText : "";
+ if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
mTextVisible = isVisible;
- setText(isVisible ? mText : "");
+ setText(newText);
if (mTextVisible) {
setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
} else {
@@ -367,4 +372,6 @@
TextUtils.TruncateAt.END);
return !mText.equals(displayedText);
}
+
+ public abstract int getControlTypeForLogging();
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 5e4f670..7979082 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -21,6 +21,7 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -45,7 +46,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
-import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -70,7 +70,7 @@
import java.util.Comparator;
import java.util.Stack;
-public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
+public class CellLayout extends ViewGroup {
public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
@@ -128,8 +128,6 @@
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
- private final ClickShadowView mTouchFeedbackView;
-
@Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
@Thunk final ArrayMap<View, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
@@ -285,9 +283,6 @@
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
-
- mTouchFeedbackView = new ClickShadowView(context);
- addView(mTouchFeedbackView);
addView(mShortcutsAndWidgets);
}
@@ -297,7 +292,7 @@
ViewCompat.setAccessibilityDelegate(this, null);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
getShortcutsAndWidgets().setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- setOnClickListener(mLauncher);
+ setOnClickListener(null);
} else {
if (dragType == WORKSPACE_ACCESSIBILITY_DRAG &&
!(mTouchHelper instanceof WorkspaceAccessibilityHelper)) {
@@ -384,11 +379,6 @@
return mDropPending;
}
- @Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- mTouchFeedbackView.setPressedIcon(icon, background);
- }
-
void setIsDragOverlapping(boolean isDragOverlapping) {
if (mIsDragOverlapping != isDragOverlapping) {
mIsDragOverlapping = isDragOverlapping;
@@ -785,13 +775,6 @@
throw new RuntimeException("CellLayout cannot have UNSPECIFIED dimensions");
}
- // Make the feedback view large enough to hold the blur bitmap.
- mTouchFeedbackView.measure(
- MeasureSpec.makeMeasureSpec(mCellWidth + mTouchFeedbackView.getExtraSize(),
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(mCellHeight + mTouchFeedbackView.getExtraSize(),
- MeasureSpec.EXACTLY));
-
mShortcutsAndWidgets.measure(
MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
@@ -815,11 +798,7 @@
int top = getPaddingTop();
int bottom = b - t - getPaddingBottom();
- mTouchFeedbackView.layout(left, top,
- left + mTouchFeedbackView.getMeasuredWidth(),
- top + mTouchFeedbackView.getMeasuredHeight());
mShortcutsAndWidgets.layout(left, top, right, bottom);
-
// Expand the background drawing bounds by the padding baked into the background drawable
mBackground.getPadding(mTempRect);
mBackground.setBounds(
@@ -1012,6 +991,7 @@
}
}
+ @SuppressLint("StringFormatMatches")
public String getItemMoveDescription(int cellX, int cellY) {
if (mContainerType == HOTSEAT) {
return getContext().getString(R.string.move_to_hotseat_position,
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
deleted file mode 100644
index 5391b4d..0000000
--- a/src/com/android/launcher3/ClickShadowView.java
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_DURATION;
-import static com.android.launcher3.FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR;
-import static com.android.launcher3.LauncherAnimUtils.ELEVATION;
-import static com.android.launcher3.graphics.HolographicOutlineHelper.ADAPTIVE_ICON_SHADOW_BITMAP;
-
-import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.util.Property;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-
-public class ClickShadowView extends View {
-
- private static final int SHADOW_SIZE_FACTOR = 3;
- private static final int SHADOW_LOW_ALPHA = 30;
- private static final int SHADOW_HIGH_ALPHA = 60;
-
- private static float sAdaptiveIconScaleFactor = 1f;
-
- private final Paint mPaint;
-
- @ViewDebug.ExportedProperty(category = "launcher")
- private final float mShadowOffset;
- @ViewDebug.ExportedProperty(category = "launcher")
- private final float mShadowPadding;
-
- private Bitmap mBitmap;
- private ObjectAnimator mAnim;
-
- private Drawable mAdaptiveIcon;
- private ViewOutlineProvider mOutlineProvider;
-
- public ClickShadowView(Context context) {
- super(context);
- mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
- mPaint.setColor(Color.BLACK);
-
- mShadowPadding = getResources().getDimension(R.dimen.blur_size_click_shadow);
- mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift);
- }
-
- public static void setAdaptiveIconScaleFactor(float factor) {
- sAdaptiveIconScaleFactor = factor;
- }
-
- /**
- * @return extra space required by the view to show the shadow.
- */
- public int getExtraSize() {
- return (int) (SHADOW_SIZE_FACTOR * mShadowPadding);
- }
-
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- if (icon == null) {
- setBitmap(null);
- cancelAnim();
- return;
- }
- if (background == null) {
- if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) {
- // clear animation shadow
- }
- setBitmap(null);
- cancelAnim();
- icon.setOutlineProvider(null);
- } else if (setBitmap(background)) {
- if (mBitmap == ADAPTIVE_ICON_SHADOW_BITMAP) {
- setupAdaptiveShadow(icon);
- cancelAnim();
- startAnim(icon, ELEVATION,
- getResources().getDimension(R.dimen.click_shadow_elevation));
- } else {
- alignWithIconView(icon);
- startAnim(this, ALPHA, 1);
- }
- }
- }
-
- @TargetApi(Build.VERSION_CODES.O)
- private void setupAdaptiveShadow(final BubbleTextView view) {
- if (mAdaptiveIcon == null) {
- mAdaptiveIcon = new AdaptiveIconDrawable(null, null);
- mOutlineProvider = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- mAdaptiveIcon.getOutline(outline);
- }
- };
- }
-
- int iconWidth = view.getRight() - view.getLeft();
- int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
- int drawableWidth = view.getIcon().getBounds().width();
-
- Rect bounds = new Rect();
- bounds.left = view.getCompoundPaddingLeft() + (iconHSpace - drawableWidth) / 2;
- bounds.right = bounds.left + drawableWidth;
- bounds.top = view.getPaddingTop();
- bounds.bottom = bounds.top + view.getIcon().getBounds().height();
- Utilities.scaleRectAboutCenter(bounds, sAdaptiveIconScaleFactor);
-
- mAdaptiveIcon.setBounds(bounds);
- view.setOutlineProvider(mOutlineProvider);
- }
-
- /**
- * Applies the new bitmap.
- * @return true if the view was invalidated.
- */
- private boolean setBitmap(Bitmap b) {
- if (b != mBitmap){
- mBitmap = b;
- invalidate();
- return true;
- }
- return false;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mBitmap != null) {
- mPaint.setAlpha(SHADOW_LOW_ALPHA);
- canvas.drawBitmap(mBitmap, 0, 0, mPaint);
- mPaint.setAlpha(SHADOW_HIGH_ALPHA);
- canvas.drawBitmap(mBitmap, 0, mShadowOffset, mPaint);
- }
- }
-
- private void cancelAnim() {
- if (mAnim != null) {
- mAnim.cancel();
- mAnim.setCurrentPlayTime(0);
- mAnim = null;
- }
- }
-
- private void startAnim(View target, Property<View, Float> property, float endValue) {
- cancelAnim();
- property.set(target, 0f);
- mAnim = ObjectAnimator.ofFloat(target, property, endValue);
- mAnim.setDuration(CLICK_FEEDBACK_DURATION)
- .setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
- mAnim.start();
- }
-
- /**
- * Aligns the shadow with {@param view}
- * Note: {@param view} must be a descendant of my parent.
- */
- private void alignWithIconView(BubbleTextView view) {
- int[] coords = new int[] {0, 0};
- Utilities.getDescendantCoordRelativeToAncestor(
- (ViewGroup) view.getParent(), (View) getParent(), coords, false);
-
- float leftShift = view.getLeft() + coords[0] - getLeft();
- float topShift = view.getTop() + coords[1] - getTop();
- int iconWidth = view.getRight() - view.getLeft();
- int iconHeight = view.getBottom() - view.getTop();
- int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
- float drawableWidth = view.getIcon().getBounds().width();
-
- // Set the bounds to clip against
- int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding);
- int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ;
- setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight));
-
- setTranslationX(leftShift
- + view.getCompoundPaddingLeft() * view.getScaleX()
- + (iconHSpace - drawableWidth) * view.getScaleX() / 2 /* drawable gap */
- + iconWidth * (1 - view.getScaleX()) / 2 /* gap due to scale */
- - mShadowPadding /* extra shadow size */
- );
- setTranslationY(topShift
- + view.getPaddingTop() * view.getScaleY() /* drawable gap */
- + view.getHeight() * (1 - view.getScaleY()) / 2 /* gap due to scale */
- - mShadowPadding /* extra shadow size */
- );
- }
-}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index c12ea57..28d1129 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -24,6 +24,7 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
public class DeleteDropTarget extends ButtonDropTarget {
@@ -54,7 +55,7 @@
* @return true for items that should have a "Remove" action in accessibility.
*/
@Override
- public boolean supportsAccessibilityDrop(ItemInfo info) {
+ public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
return (info instanceof ShortcutInfo)
|| (info instanceof LauncherAppWidgetInfo)
|| (info instanceof FolderInfo);
@@ -103,4 +104,9 @@
mLauncher.getDragLayer()
.announceForAccessibility(getContext().getString(R.string.item_removed));
}
+
+ @Override
+ public int getControlTypeForLogging() {
+ return ControlType.REMOVE_TARGET;
+ }
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 2227bfd..6f35752 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -28,6 +28,7 @@
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.badge.BadgeRenderer;
+import com.android.launcher3.graphics.IconNormalizer;
public class DeviceProfile {
@@ -40,7 +41,9 @@
public final boolean transposeLayoutWithOrientation;
// Device properties in current orientation
- public final boolean isLandscape;
+ private final boolean isLandscape;
+ public final boolean isMultiWindowMode;
+
public final int widthPx;
public final int heightPx;
public final int availableWidthPx;
@@ -79,9 +82,8 @@
public int workspaceCellPaddingXPx;
// Folder
- public int folderBackgroundOffset;
public int folderIconSizePx;
- public int folderIconPreviewPadding;
+ public int folderIconOffsetYPx;
// Folder cell
public int folderCellWidthPx;
@@ -115,16 +117,18 @@
// Insets
private final Rect mInsets = new Rect();
public final Rect workspacePadding = new Rect();
+ private final Rect mHotseatPadding = new Rect();
// Icon badges
public BadgeRenderer mBadgeRenderer;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
Point minSize, Point maxSize,
- int width, int height, boolean isLandscape) {
+ int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
this.inv = inv;
this.isLandscape = isLandscape;
+ this.isMultiWindowMode = isMultiWindowMode;
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
@@ -214,10 +218,11 @@
public DeviceProfile copy(Context context) {
Point size = new Point(availableWidthPx, availableHeightPx);
- return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape);
+ return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape,
+ isMultiWindowMode);
}
- DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+ public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
// We take the minimum sizes of this profile and it's multi-window variant to ensure that
// the system decor is always excluded.
mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
@@ -226,7 +231,7 @@
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
- isLandscape);
+ isLandscape, true);
// If there isn't enough vertical cell padding with the labels displayed, hide the labels.
float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
@@ -246,6 +251,14 @@
}
/**
+ * Inverse of {@link #getMultiWindowProfile(Context, Point)}
+ * @return device profile corresponding to the current orientation in non multi-window mode.
+ */
+ public DeviceProfile getFullScreenProfile() {
+ return isLandscape ? inv.landscapeProfile : inv.portraitProfile;
+ }
+
+ /**
* Adjusts the profile so that the labels on the Workspace are hidden.
* It is important to call this method after the All Apps variables have been set.
*/
@@ -288,7 +301,7 @@
+ Utilities.calculateTextHeight(iconTextSizePx);
int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
if (iconDrawablePaddingPx > cellYPadding && !isVerticalLayout
- && !inMultiWindowMode()) {
+ && !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}.
@@ -327,9 +340,8 @@
}
// Folder icon
- folderBackgroundOffset = -iconDrawablePaddingPx;
- folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
- folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
+ folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
+ folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
}
private void updateAvailableFolderCellDimensions(DisplayMetrics dm, Resources res) {
@@ -444,6 +456,33 @@
}
}
+ public Rect getHotseatLayoutPadding() {
+ if (isVerticalBarLayout()) {
+ if (isSeascape()) {
+ mHotseatPadding.set(
+ mInsets.left, mInsets.top, hotseatBarSidePaddingPx, mInsets.bottom);
+ } else {
+ mHotseatPadding.set(
+ hotseatBarSidePaddingPx, mInsets.top, mInsets.right, mInsets.bottom);
+ }
+ } else {
+
+ // We want the edges of the hotseat to line up with the edges of the workspace, but the
+ // icons in the hotseat are a different size, and so don't line up perfectly. To account
+ // for this, we pad the left and right of the hotseat with half of the difference of a
+ // workspace cell vs a hotseat cell.
+ float workspaceCellWidth = (float) widthPx / inv.numColumns;
+ float hotseatCellWidth = (float) widthPx / inv.numHotseatIcons;
+ int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
+ mHotseatPadding.set(
+ hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx,
+ hotseatBarTopPaddingPx,
+ hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
+ hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
+ }
+ return mHotseatPadding;
+ }
+
/**
* @return the bounds for which the open folders should be contained within
*/
@@ -503,16 +542,6 @@
}
}
- public boolean inMultiWindowMode() {
- return this != inv.landscapeProfile && this != inv.portraitProfile;
- }
-
- public boolean shouldIgnoreLongPressToOverview(float touchX) {
- boolean touchedLhsEdge = mInsets.left == 0 && touchX < edgeMarginPx;
- boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
- return !inMultiWindowMode() && (touchedLhsEdge || touchedRhsEdge);
- }
-
private static Context getContext(Context c, int orientation) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index a3fe89a..d025a9b 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -16,11 +16,10 @@
package com.android.launcher3;
-import static com.android.launcher3.AlphaUpdateListener.updateVisibility;
import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
+import static com.android.launcher3.anim.AlphaUpdateListener.updateVisibility;
import android.animation.TimeInterpolator;
import android.content.Context;
@@ -47,7 +46,7 @@
protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
private final Runnable mFadeAnimationEndRunnable =
- () -> updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
+ () -> updateVisibility(DropTargetBar.this);
@ViewDebug.ExportedProperty(category = "launcher")
protected boolean mDeferOnDragEnd;
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index bd19dfa..3873a81 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -16,8 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+
import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -28,6 +29,7 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Property;
import android.util.SparseArray;
@@ -36,14 +38,12 @@
public class FastBitmapDrawable extends Drawable {
- private static final float PRESSED_BRIGHTNESS = 100f / 255f;
+ private static final float PRESSED_SCALE = 1.1f;
+
private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f;
- public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = (input) ->
- (input < 0.05f) ? (input / 0.05f) : ((input < 0.3f) ? 1 : (1 - input) / 0.7f);
-
- public static final int CLICK_FEEDBACK_DURATION = 2000;
+ public static final int CLICK_FEEDBACK_DURATION = 200;
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we
// reduce the value space to a smaller value V, which reduces the number of cached
@@ -58,24 +58,29 @@
private static final ColorMatrix sTempFilterMatrix = new ColorMatrix();
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
- private final Bitmap mBitmap;
+ protected Bitmap mBitmap;
protected final int mIconColor;
private boolean mIsPressed;
private boolean mIsDisabled;
- private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
- = new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
+ // Animator and properties for the fast bitmap drawable's scale
+ private static final Property<FastBitmapDrawable, Float> SCALE
+ = new Property<FastBitmapDrawable, Float>(Float.TYPE, "scale") {
@Override
public Float get(FastBitmapDrawable fastBitmapDrawable) {
- return fastBitmapDrawable.getBrightness();
+ return fastBitmapDrawable.mScale;
}
@Override
public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
- fastBitmapDrawable.setBrightness(value);
+ fastBitmapDrawable.mScale = value;
+ fastBitmapDrawable.invalidateSelf();
}
};
+ private ObjectAnimator mScaleAnimation;
+ private float mScale = 1;
+
// The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
// as a result, can be used to compose the key for the cached ColorMatrixColorFilters
@@ -84,9 +89,6 @@
private int mAlpha = 255;
private int mPrevUpdateKey = Integer.MAX_VALUE;
- // Animators for the fast bitmap drawable's brightness
- private ObjectAnimator mBrightnessAnimator;
-
public FastBitmapDrawable(Bitmap b) {
this(b, Color.TRANSPARENT);
}
@@ -106,8 +108,20 @@
}
@Override
- public void draw(Canvas canvas) {
- canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
+ public final void draw(Canvas canvas) {
+ if (mScaleAnimation != null) {
+ int count = canvas.save();
+ Rect bounds = getBounds();
+ canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
+ drawInternal(canvas, bounds);
+ canvas.restoreToCount(count);
+ } else {
+ drawInternal(canvas, getBounds());
+ }
+ }
+
+ protected void drawInternal(Canvas canvas, Rect bounds) {
+ canvas.drawBitmap(mBitmap, null, bounds, mPaint);
}
@Override
@@ -136,6 +150,10 @@
return mAlpha;
}
+ public float getAnimatedScale() {
+ return mScaleAnimation == null ? 1 : mScale;
+ }
+
@Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
@@ -182,19 +200,20 @@
if (mIsPressed != isPressed) {
mIsPressed = isPressed;
- if (mBrightnessAnimator != null) {
- mBrightnessAnimator.cancel();
+ if (mScaleAnimation != null) {
+ mScaleAnimation.cancel();
+ mScaleAnimation = null;
}
if (mIsPressed) {
// Animate when going to pressed state
- mBrightnessAnimator = ObjectAnimator.ofFloat(
- this, BRIGHTNESS, getExpectedBrightness());
- mBrightnessAnimator.setDuration(CLICK_FEEDBACK_DURATION);
- mBrightnessAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
- mBrightnessAnimator.start();
+ mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, PRESSED_SCALE);
+ mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
+ mScaleAnimation.setInterpolator(ACCEL);
+ mScaleAnimation.start();
} else {
- setBrightness(getExpectedBrightness());
+ mScale = 1f;
+ invalidateSelf();
}
return true;
}
@@ -203,12 +222,7 @@
private void invalidateDesaturationAndBrightness() {
setDesaturation(mIsDisabled ? DISABLED_DESATURATION : 0);
- setBrightness(getExpectedBrightness());
- }
-
- private float getExpectedBrightness() {
- return mIsDisabled ? DISABLED_BRIGHTNESS :
- (mIsPressed ? PRESSED_BRIGHTNESS : 0);
+ setBrightness(mIsDisabled ? DISABLED_BRIGHTNESS : 0);
}
public void setIsDisabled(boolean isDisabled) {
@@ -304,4 +318,29 @@
}
invalidateSelf();
}
+
+ @Override
+ public ConstantState getConstantState() {
+ return new MyConstantState(mBitmap, mIconColor);
+ }
+
+ protected static class MyConstantState extends ConstantState {
+ protected final Bitmap mBitmap;
+ protected final int mIconColor;
+
+ public MyConstantState(Bitmap bitmap, int color) {
+ mBitmap = bitmap;
+ mIconColor = color;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new FastBitmapDrawable(mBitmap, mIconColor);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return 0;
+ }
+ }
}
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index cea7e43..4eac4a4 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -73,13 +73,7 @@
view.getViewTreeObserver().removeOnDrawListener(sGlobalDrawListener);
}
- TraceHelper.beginSection("TICK");
- sGlobalDrawListener = new ViewTreeObserver.OnDrawListener() {
- public void onDraw() {
- sGlobalFrameCounter++;
- TraceHelper.partitionSection("TICK", "Frame drawn");
- }
- };
+ sGlobalDrawListener = () -> sGlobalFrameCounter++;
view.getViewTreeObserver().addOnDrawListener(sGlobalDrawListener);
sVisible = true;
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 03043f2..c6025fe 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -63,14 +63,6 @@
return mContent;
}
- /**
- * Registers the specified listener on the cell layout of the hotseat.
- */
- @Override
- public void setOnLongClickListener(OnLongClickListener l) {
- mContent.setOnLongClickListener(l);
- }
-
/* Get the orientation invariant order of the item in the hotseat for persistence. */
int getOrderInHotseat(int x, int y) {
return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x;
@@ -89,12 +81,17 @@
protected void onFinishInflate() {
super.onFinishInflate();
mContent = findViewById(R.id.layout);
-
- resetLayout();
}
- void resetLayout() {
+ void resetLayout(boolean hasVerticalHotseat) {
mContent.removeAllViewsInLayout();
+ mHasVerticalHotseat = hasVerticalHotseat;
+ InvariantDeviceProfile idp = mLauncher.getDeviceProfile().inv;
+ if (hasVerticalHotseat) {
+ mContent.setGridSize(1, idp.numHotseatIcons);
+ } else {
+ mContent.setGridSize(idp.numHotseatIcons, 1);
+ }
if (!FeatureFlags.NO_ALL_APPS_ICON) {
// Add the Apps button
@@ -156,46 +153,24 @@
public void setInsets(Rect insets) {
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
DeviceProfile grid = mLauncher.getDeviceProfile();
- mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
- if (mHasVerticalHotseat) {
- mContent.setGridSize(1, grid.inv.numHotseatIcons);
-
+ if (grid.isVerticalBarLayout()) {
lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
if (grid.isSeascape()) {
lp.gravity = Gravity.LEFT;
lp.width = grid.hotseatBarSizePx + insets.left + grid.hotseatBarSidePaddingPx;
- getLayout().setPadding(
- insets.left, insets.top, grid.hotseatBarSidePaddingPx, insets.bottom);
-
} else {
lp.gravity = Gravity.RIGHT;
lp.width = grid.hotseatBarSizePx + insets.right + grid.hotseatBarSidePaddingPx;
- getLayout().setPadding(
- grid.hotseatBarSidePaddingPx, insets.top, insets.right, insets.bottom);
}
} else {
- mContent.setGridSize(grid.inv.numHotseatIcons, 1);
-
lp.gravity = Gravity.BOTTOM;
lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
lp.height = grid.hotseatBarSizePx + insets.bottom;
-
- // We want the edges of the hotseat to line up with the edges of the workspace, but the
- // icons in the hotseat are a different size, and so don't line up perfectly. To account for
- // this, we pad the left and right of the hotseat with half of the difference of a workspace
- // cell vs a hotseat cell.
- float workspaceCellWidth = (float) grid.widthPx / grid.inv.numColumns;
- float hotseatCellWidth = (float) grid.widthPx / grid.inv.numHotseatIcons;
- int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
- Rect workspacePadding = grid.workspacePadding;
-
- getLayout().setPadding(
- hotseatAdjustment + workspacePadding.left + grid.cellLayoutPaddingLeftRightPx,
- grid.hotseatBarTopPaddingPx,
- hotseatAdjustment + workspacePadding.right + grid.cellLayoutPaddingLeftRightPx,
- grid.hotseatBarBottomPaddingPx + insets.bottom + grid.cellLayoutBottomPaddingPx);
}
+ Rect padding = grid.getHotseatLayoutPadding();
+ getLayout().setPadding(padding.left, padding.top, padding.right, padding.bottom);
+
setLayoutParams(lp);
InsettableFrameLayout.dispatchInsets(this, insets);
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 957a5e5..ab73074 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -45,12 +45,10 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.BitmapInfo;
-import com.android.launcher3.graphics.ColorExtractor;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
@@ -126,7 +124,7 @@
// automatically be loaded as ALPHA_8888.
mLowResOptions.inPreferredConfig = Bitmap.Config.RGB_565;
- if (UiFactory.USE_HARDWARE_BITMAP) {
+ if (BitmapRenderer.USE_HARDWARE_BITMAP) {
mHighResOptions = new BitmapFactory.Options();
mHighResOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
} else {
@@ -643,11 +641,8 @@
// Load the full res icon for the application, but if useLowResIcon is set, then
// only keep the low resolution icon instead of the larger full-sized icon
BitmapInfo iconInfo = li.createBadgedIconBitmap(
- appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion);
- if (mInstantAppResolver.isInstantApp(appInfo)) {
- li.badgeWithDrawable(iconInfo.icon,
- mContext.getDrawable(R.drawable.ic_instant_app_badge));
- }
+ appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion,
+ mInstantAppResolver.isInstantApp(appInfo));
li.recycle();
Bitmap lowResIcon = generateLowResIcon(iconInfo.icon);
@@ -785,7 +780,7 @@
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int RELEASE_VERSION = 20;
+ private final static int RELEASE_VERSION = 21;
private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid";
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
deleted file mode 100644
index e52fd76..0000000
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF 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.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.util.Themes;
-
-public class InfoDropTarget extends UninstallDropTarget {
-
- private static final String TAG = "InfoDropTarget";
-
- public InfoDropTarget(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public InfoDropTarget(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void setupUi() {
- // Get the hover color
- mHoverColor = Themes.getColorAccent(getContext());
- setDrawable(R.drawable.ic_info_shadow);
- }
-
- @Override
- protected ComponentName performDropAction(ItemInfo item) {
- return performDropAction(mLauncher, item, null, null);
- }
-
- /**
- * @return Whether the activity was started.
- */
- public static boolean startDetailsActivityForInfo(
- ItemInfo info, Launcher launcher, Rect sourceBounds, Bundle opts) {
- return performDropAction(launcher, info, sourceBounds, opts) != null;
- }
-
- /**
- * Performs the drop action and returns the target component for the dragObject or null if
- * the action was not performed.
- */
- private static ComponentName performDropAction(Context context, ItemInfo info,
- Rect sourceBounds, Bundle opts) {
- if (info instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
- context.startActivity(promiseAppInfo.getMarketIntent(context));
- return null;
- }
- ComponentName componentName = null;
- if (info instanceof AppInfo) {
- componentName = ((AppInfo) info).componentName;
- } else if (info instanceof ShortcutInfo) {
- componentName = info.getTargetComponent();
- } else if (info instanceof PendingAddItemInfo) {
- componentName = ((PendingAddItemInfo) info).componentName;
- } else if (info instanceof LauncherAppWidgetInfo) {
- componentName = ((LauncherAppWidgetInfo) info).providerName;
- }
- if (componentName != null) {
- try {
- LauncherAppsCompat.getInstance(context)
- .showAppDetailsForProfile(componentName, info.user, sourceBounds, opts);
- return componentName;
- } catch (SecurityException | ActivityNotFoundException e) {
- Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Unable to launch settings", e);
- }
- }
- return null;
- }
-
- @Override
- public int getAccessibilityAction() {
- return LauncherAccessibilityDelegate.INFO;
- }
-
- @Override
- protected boolean supportsDrop(ItemInfo info) {
- // Only show the App Info drop target if developer settings are enabled.
- boolean developmentSettingsEnabled = Settings.Global.getInt(
- getContext().getContentResolver(),
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
- if (!developmentSettingsEnabled) {
- return false;
- }
- return info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT &&
- (info instanceof AppInfo ||
- (info instanceof ShortcutInfo && !((ShortcutInfo) info).isPromise()) ||
- (info instanceof LauncherAppWidgetInfo &&
- ((LauncherAppWidgetInfo) info).restoreStatus == 0) ||
- info instanceof PendingAddItemInfo);
- }
-}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 7a43198..f63cce5 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -22,6 +22,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Point;
+import android.support.annotation.VisibleForTesting;
import android.util.DisplayMetrics;
import android.util.Xml;
import android.view.Display;
@@ -65,12 +66,6 @@
public int numColumns;
/**
- * The minimum number of predicted apps in all apps.
- */
- @Deprecated
- int minAllAppsPredictionColumns;
-
- /**
* Number of icons per row and column in the folder.
*/
public int numFolderRows;
@@ -94,17 +89,18 @@
public Point defaultWallpaperSize;
+ @VisibleForTesting
public InvariantDeviceProfile() {
}
- public InvariantDeviceProfile(InvariantDeviceProfile p) {
+ private InvariantDeviceProfile(InvariantDeviceProfile p) {
this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
- p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
+ p.numFolderRows, p.numFolderColumns,
p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
p.defaultLayoutId, p.demoModeLayoutId);
}
- InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
+ private InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc,
float is, float lis, float its, int hs, int dlId, int dmlId) {
name = n;
minWidthDps = w;
@@ -113,7 +109,6 @@
numColumns = c;
numFolderRows = fr;
numFolderColumns = fc;
- minAllAppsPredictionColumns = maapc;
iconSize = is;
landscapeIconSize = lis;
iconTextSize = its;
@@ -123,7 +118,7 @@
}
@TargetApi(23)
- InvariantDeviceProfile(Context context) {
+ public InvariantDeviceProfile(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
@@ -150,7 +145,6 @@
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
- minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
iconSize = interpolatedDeviceProfileOut.iconSize;
landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
@@ -170,9 +164,9 @@
int largeSide = Math.max(realSize.x, realSize.y);
landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
- largeSide, smallSide, true /* isLandscape */);
+ largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
- smallSide, largeSide, false /* isLandscape */);
+ smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
@@ -207,7 +201,6 @@
numColumns,
a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
- a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
iconSize,
a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fcf36ca..b410f4f 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,30 +18,19 @@
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
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.LoggerUtils.newContainerTarget;
-import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
-import android.app.AlertDialog;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
@@ -49,20 +38,13 @@
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.ContextWrapper;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -75,40 +57,33 @@
import android.text.method.TextKeyListener;
import android.util.Log;
import android.util.SparseArray;
-import android.view.ActionMode;
-import android.view.Display;
-import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.config.FeatureFlags;
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.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.keyboard.CustomActionsPopup;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.FileLog;
@@ -119,8 +94,9 @@
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.states.RotationHelper;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.UiFactory;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -137,6 +113,7 @@
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.views.OptionsPopupView;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -154,15 +131,12 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
* Default launcher application.
*/
-public class Launcher extends BaseActivity
- implements LauncherExterns, View.OnClickListener, OnLongClickListener,
- LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
- WallpaperColorInfo.OnThemeChangeListener {
+public class Launcher extends BaseDraggingActivity
+ implements LauncherExterns, LauncherModel.Callbacks, LauncherProviderChangeListener {
public static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -172,11 +146,10 @@
private static final int REQUEST_CREATE_APPWIDGET = 5;
private static final int REQUEST_PICK_APPWIDGET = 9;
- private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
- private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
- private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
+ public static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+ public static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
@@ -188,10 +161,6 @@
*/
protected static final int REQUEST_LAST = 100;
- // The Intent extra that defines whether to ignore the launch animation
- static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
- "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
-
// Type: int
private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
// Type: int
@@ -203,14 +172,8 @@
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
- // When starting an action mode, setting this tag will cause the action mode to be cancelled
- // automatically when user interacts with the launcher.
- public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
-
private LauncherStateManager mStateManager;
- private boolean mIsSafeModeEnabled;
-
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
// How long to wait before the new-shortcut animation automatically pans the workspace
@@ -232,6 +195,8 @@
private final int[] mTmpAddItemCellCoordinates = new int[2];
@Thunk Hotseat mHotseat;
+ private View mDragHandleIndicator;
+ @Nullable private View mHotseatSearchBox;
private DropTargetBar mDropTargetBar;
@@ -240,7 +205,7 @@
AllAppsTransitionController mAllAppsController;
// UI and state for the overview panel
- private ViewGroup mOverviewPanel;
+ private View mOverviewPanel;
@Thunk boolean mWorkspaceLoading = true;
@@ -272,14 +237,16 @@
*/
private PendingRequestArgs mPendingRequestArgs;
- private final PointF mLastDispatchTouchEvent = new PointF();
-
public ViewGroupFocusHelper mFocusHandler;
- private boolean mRotationEnabled = false;
private boolean mAppLaunchSuccess;
- private RotationPrefChangeHandler mRotationPrefChangeHandler;
- private ActionMode mCurrentActionMode;
+ private RotationHelper mRotationHelper;
+
+ // Used to keep track of the swipe up state
+ private SharedPreferences.OnSharedPreferenceChangeListener mSharedPrefsListener =
+ (sharedPreferences, s) -> {
+ mDragLayer.setup(mDragController);
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -299,25 +266,16 @@
}
TraceHelper.beginSection("Launcher-onCreate");
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.preOnCreate();
- }
-
- WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
- wallpaperColorInfo.setOnThemeChangeListener(this);
- overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
-
super.onCreate(savedInstanceState);
TraceHelper.partitionSection("Launcher-onCreate", "super call");
LauncherAppState app = LauncherAppState.getInstance(this);
mOldConfig = new Configuration(getResources().getConfiguration());
+ mModel = app.setLauncher(this);
initDeviceProfile(app.getInvariantDeviceProfile());
mSharedPrefs = Utilities.getPrefs(this);
- mIsSafeModeEnabled = getPackageManager().isSafeMode();
- mModel = app.setLauncher(this);
- mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
+ mSharedPrefs.registerOnSharedPreferenceChangeListener(mSharedPrefsListener);
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -335,20 +293,10 @@
setupViews();
mPopupDataProvider = new PopupDataProvider(this);
- mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
- // In case we are on a device with locked rotation, we should look at preferences to check
- // if the user has specifically allowed rotation.
- if (!mRotationEnabled) {
- mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
- mRotationPrefChangeHandler = new RotationPrefChangeHandler();
- mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
- }
+ mRotationHelper = new RotationHelper(this);
boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
if (internalStateHandled) {
- // Temporarily enable the rotation
- mRotationEnabled = true;
-
if (savedInstanceState != null) {
// InternalStateHandler has already set the appropriate state.
// We dont need to do anything.
@@ -380,11 +328,6 @@
// For handling default keys
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
- // On large interfaces, or on devices that a user has specifically enabled screen rotation,
- // we want the screen to auto-rotate based on the current orientation
- setRequestedOrientation(mRotationEnabled
- ? SCREEN_ORIENTATION_UNSPECIFIED : SCREEN_ORIENTATION_NOSENSOR);
-
setContentView(mLauncherView);
getRootView().dispatchInsets();
@@ -403,6 +346,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
+ mRotationHelper.initialize();
TraceHelper.endSection("Launcher-onCreate");
}
@@ -418,46 +362,41 @@
getRootView().dispatchInsets();
getStateManager().reapplyState();
+ // Recreate touch controllers
+ mDragLayer.setup(mDragController);
+
// TODO: We can probably avoid rebind when only screen size changed.
- int currentPage = mWorkspace.getNextPage();
- if (mModel.startLoader(currentPage)) {
- mWorkspace.setCurrentPage(currentPage);
- setWorkspaceLoading(true);
- }
+ rebindModel();
}
mOldConfig.setTo(newConfig);
+ UiFactory.onLauncherStateOrResumeChanged(this);
super.onConfigurationChanged(newConfig);
}
+ @Override
+ public void rebindModel() {
+ int currentPage = mWorkspace.getNextPage();
+ if (mModel.startLoader(currentPage)) {
+ mWorkspace.setCurrentPage(currentPage);
+ setWorkspaceLoading(true);
+ }
+ }
+
private void initDeviceProfile(InvariantDeviceProfile idp) {
// Load configuration-specific DeviceProfile
- mDeviceProfile = idp.getDeviceProfile(this);
- if (isInMultiWindowModeCompat()) {
- Display display = getWindowManager().getDefaultDisplay();
- Point mwSize = new Point();
- display.getSize(mwSize);
- mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
- }
+ setDeviceProfile(idp.getDeviceProfile(this));
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout(), true);
}
- @Override
- public void onThemeChanged() {
- recreate();
+ public RotationHelper getRotationHelper() {
+ return mRotationHelper;
}
public LauncherStateManager getStateManager() {
return mStateManager;
}
- protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
- if (isDark) {
- setTheme(R.style.LauncherThemeDark);
- } else if (supportsDarkText) {
- setTheme(R.style.LauncherThemeDarkText);
- }
- }
-
@Override
public <T extends View> T findViewById(int id) {
return mLauncherView.findViewById(id);
@@ -512,6 +451,22 @@
return mPopupDataProvider;
}
+ @Override
+ public BadgeInfo getBadgeInfoForItem(ItemInfo info) {
+ return mPopupDataProvider.getBadgeInfoForItem(info);
+ }
+
+ @Override
+ public void invalidateParent(ItemInfo info) {
+ FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(getDeviceProfile().inv);
+ if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
+ View folderIcon = getWorkspace().getHomescreenIconByItemId(info.container);
+ if (folderIcon != null) {
+ folderIcon.invalidate();
+ }
+ }
+ }
+
/**
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
@@ -595,14 +550,6 @@
ON_ACTIVITY_RESULT_ANIMATION_DELAY);
}
return;
- } else if (requestCode == REQUEST_PICK_WALLPAPER) {
- if (resultCode == RESULT_OK && isInState(OVERVIEW)) {
- // User could have free-scrolled between pages before picking a wallpaper; make sure
- // we move to the closest one now.
- mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
- mStateManager.goToState(NORMAL, false);
- }
- return;
}
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
@@ -785,9 +732,11 @@
if (!mAppLaunchSuccess) {
getUserEventDispatcher().logActionCommand(Action.Command.STOP,
- mStateManager.getState().containerType);
+ mStateManager.getState().containerType, -1);
}
NotificationListener.removeNotificationsChangedListener();
+ getStateManager().moveToRestState();
+
}
@Override
@@ -819,6 +768,7 @@
mScrimAnimator.start();
}
mShouldFadeInScrim = false;
+ UiFactory.onStart(this);
}
@Override
@@ -841,6 +791,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onResume();
}
+ UiFactory.onLauncherStateOrResumeChanged(this);
TraceHelper.endSection("ON_RESUME");
}
@@ -859,6 +810,18 @@
}
}
+ @Override
+ protected void onUserLeaveHint() {
+ super.onUserLeaveHint();
+ UiFactory.onLauncherStateOrResumeChanged(this);
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ mStateManager.onWindowFocusChanged();
+ }
+
public interface LauncherOverlay {
/**
@@ -952,23 +915,17 @@
mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mOverviewPanel = findViewById(R.id.overview_panel);
+ mHotseat = findViewById(R.id.hotseat);
+ mDragHandleIndicator = findViewById(R.id.drag_indicator);
+ mHotseatSearchBox = findViewById(R.id.search_container_hotseat);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
// Setup the drag layer
- mDragLayer.setup(this, mDragController);
+ mDragLayer.setup(mDragController);
- // Setup the hotseat
- mHotseat = (Hotseat) findViewById(R.id.hotseat);
- if (mHotseat != null) {
- mHotseat.setOnLongClickListener(this);
- }
-
- // Setup the workspace
- mWorkspace.setHapticFeedbackEnabled(false);
- mWorkspace.setOnLongClickListener(this);
mWorkspace.setup(mDragController);
// Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
// default state, otherwise we will update to the wrong offsets in RTL
@@ -986,7 +943,7 @@
mDragController.setMoveTarget(mWorkspace);
mDropTargetBar.setup(mDragController);
- mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
+ mAllAppsController.setupViews(mAppsView);
}
/**
@@ -1010,7 +967,7 @@
BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
.inflate(R.layout.app_icon, parent, false);
favorite.applyFromShortcutInfo(info);
- favorite.setOnClickListener(this);
+ favorite.setOnClickListener(ItemClickHandler.INSTANCE);
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
@@ -1165,7 +1122,7 @@
public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
mWorkspace.updateIconBadges(updatedBadges);
- mAppsView.updateIconBadges(updatedBadges);
+ mAppsView.getAppsStore().updateIconBadges(updatedBadges);
PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
if (popup != null) {
@@ -1192,14 +1149,22 @@
}
}
+ public void onQuickstepGestureStarted(boolean isVisible) {
+ if (mLauncherCallbacks != null) {
+ mLauncherCallbacks.onQuickstepGestureStarted(isVisible);
+ }
+ }
+
public AllAppsTransitionController getAllAppsController() {
return mAllAppsController;
}
+ @Override
public LauncherRootView getRootView() {
return (LauncherRootView) mLauncherView;
}
+ @Override
public DragLayer getDragLayer() {
return mDragLayer;
}
@@ -1216,7 +1181,15 @@
return mHotseat;
}
- public <T extends ViewGroup> T getOverviewPanel() {
+ public View getDragHandleIndicator() {
+ return mDragHandleIndicator;
+ }
+
+ public View getHotseatSearchBox() {
+ return mHotseatSearchBox;
+ }
+
+ public <T extends View> T getOverviewPanel() {
return (T) mOverviewPanel;
}
@@ -1269,17 +1242,22 @@
} else if (alreadyOnHome) {
Target target = newContainerTarget(mStateManager.getState().containerType);
target.pageIndex = mWorkspace.getCurrentPage();
- ued.logActionCommand(Action.Command.HOME_INTENT, target);
+ ued.logActionCommand(Action.Command.HOME_INTENT, target,
+ newContainerTarget(ContainerType.WORKSPACE));
}
// In all these cases, only animate if we're already on home
AbstractFloatingView.closeAllOpenViews(this, isStarted());
- mStateManager.goToState(NORMAL);
+ if (!isInState(NORMAL)) {
+ // Only change state, if not already the same. This prevents cancelling any
+ // animations running as part of resume
+ mStateManager.goToState(NORMAL);
+ }
// Reset the apps view
if (!alreadyOnHome && mAppsView != null) {
- mAppsView.reset();
+ mAppsView.reset(isStarted() /* animate */);
}
if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()) {
@@ -1357,10 +1335,8 @@
mModel.stopLoader();
LauncherAppState.getInstance(this).setLauncher(null);
}
-
- if (mRotationPrefChangeHandler != null) {
- mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
- }
+ mRotationHelper.destroy();
+ mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mSharedPrefsListener);
try {
mAppWidgetHost.stopListening();
@@ -1369,7 +1345,6 @@
}
TextKeyListener.getInstance().release();
- WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
LauncherAnimUtils.onDestroyActivity();
@@ -1645,7 +1620,8 @@
topView.onBackPressed();
} else if (!isInState(NORMAL)) {
LauncherState lastState = mStateManager.getLastState();
- ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType);
+ ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType,
+ lastState.containerType);
mStateManager.goToState(lastState);
} else {
// Back button is a no-op here, but give at least some feedback for the button press
@@ -1653,449 +1629,53 @@
}
}
- /**
- * Launches the intent referred by the clicked shortcut.
- *
- * @param v The view representing the clicked shortcut.
- */
- public 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;
- }
-
- if (!mWorkspace.isFinishedSwitchingState()) {
- return;
- }
-
- if (v instanceof Workspace) {
- if (isInState(OVERVIEW)) {
- getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
- LauncherLogProto.Action.Direction.NONE,
- LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
- mStateManager.goToState(NORMAL);
- }
- return;
- }
-
- if (v instanceof CellLayout) {
- if (isInState(OVERVIEW)) {
- int page = mWorkspace.indexOfChild(v);
- getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
- LauncherLogProto.Action.Direction.NONE,
- LauncherLogProto.ContainerType.OVERVIEW, page);
- mWorkspace.snapToPageFromOverView(page);
- mStateManager.goToState(NORMAL);
- }
- return;
- }
-
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- onClickAppShortcut(v);
- } else if (tag instanceof FolderInfo) {
- if (v instanceof FolderIcon) {
- onClickFolderIcon(v);
- }
- } else if (tag instanceof AppInfo) {
- startAppShortcutOrInfoActivity(v);
- } else if (tag instanceof LauncherAppWidgetInfo) {
- if (v instanceof PendingAppWidgetHostView) {
- onClickPendingWidget((PendingAppWidgetHostView) v);
- }
- }
- }
-
- @SuppressLint("ClickableViewAccessibility")
- public boolean onTouch(View v, MotionEvent event) {
- return false;
- }
-
- /**
- * Event handler for the app widget view which has not fully restored.
- */
- public void onClickPendingWidget(final PendingAppWidgetHostView v) {
- if (mIsSafeModeEnabled) {
- Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- return;
- }
-
- final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
- if (v.isReadyForClickSetup()) {
- LauncherAppWidgetProviderInfo appWidgetInfo =
- mAppWidgetManager.findProvider(info.providerName, info.user);
- if (appWidgetInfo == null) {
- return;
- }
- WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
-
- if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
- if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
- // This should not happen, as we make sure that an Id is allocated during bind.
- return;
- }
- addFlowHandler.startBindFlow(this, info.appWidgetId, info,
- REQUEST_BIND_PENDING_APPWIDGET);
- } else {
- addFlowHandler.startConfigActivity(this, info, REQUEST_RECONFIGURE_APPWIDGET);
- }
- } else {
- final String packageName = info.providerName.getPackageName();
- onClickPendingAppItem(v, packageName, info.installProgress >= 0);
- }
- }
-
- private void onClickPendingAppItem(final View v, final String packageName,
- boolean downloadStarted) {
- if (downloadStarted) {
- // If the download has started, simply direct to the market app.
- startMarketIntentForPackage(v, packageName);
- return;
- }
- new AlertDialog.Builder(this)
- .setTitle(R.string.abandoned_promises_title)
- .setMessage(R.string.abandoned_promise_explanation)
- .setPositiveButton(R.string.abandoned_search, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- startMarketIntentForPackage(v, packageName);
- }
- })
- .setNeutralButton(R.string.abandoned_clean_this,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- final UserHandle user = Process.myUserHandle();
- mWorkspace.removeAbandonedPromise(packageName, user);
- }
- })
- .create().show();
- }
-
- private void startMarketIntentForPackage(View v, String packageName) {
- ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = new PackageManagerHelper(v.getContext()).getMarketIntent(packageName);
- startActivitySafely(v, intent, item);
- }
-
- /**
- * Event handler for an app shortcut click.
- *
- * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
- */
- protected void onClickAppShortcut(final View v) {
- if (LOGD) Log.d(TAG, "onClickAppShortcut");
- Object tag = v.getTag();
- if (!(tag instanceof ShortcutInfo)) {
- throw new IllegalArgumentException("Input must be a Shortcut");
- }
-
- // Open shortcut
- final ShortcutInfo shortcut = (ShortcutInfo) tag;
-
- if (shortcut.isDisabled()) {
- final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
- if ((disabledFlags &
- ~FLAG_DISABLED_SUSPENDED &
- ~FLAG_DISABLED_QUIET_USER) == 0) {
- // If the app is only disabled because of the above flags, launch activity anyway.
- // Framework will tell the user why the app is suspended.
- } else {
- if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
- // Use a message specific to this shortcut, if it has one.
- Toast.makeText(this, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
- return;
- }
- // Otherwise just use a generic error message.
- int error = R.string.activity_not_available;
- if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
- error = R.string.safemode_shortcut_error;
- } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
- (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
- error = R.string.shortcut_not_available;
- }
- Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
- return;
- }
- }
-
- // Check for abandoned promise
- if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
- String packageName = shortcut.intent.getComponent() != null ?
- shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
- if (!TextUtils.isEmpty(packageName)) {
- onClickPendingAppItem(v, packageName,
- shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
- return;
- }
- }
-
- // Start activities
- startAppShortcutOrInfoActivity(v);
- }
-
- private void startAppShortcutOrInfoActivity(View v) {
- ItemInfo item = (ItemInfo) v.getTag();
- Intent intent;
- if (item instanceof PromiseAppInfo) {
- PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
- intent = promiseAppInfo.getMarketIntent(this);
- } else {
- intent = item.getIntent();
- }
- if (intent == null) {
- throw new IllegalArgumentException("Input must have a valid intent");
- }
- startActivitySafely(v, intent, item);
- }
-
- /**
- * Event handler for a folder icon click.
- *
- * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
- */
- protected void onClickFolderIcon(View v) {
- if (LOGD) Log.d(TAG, "onClickFolder");
- if (!(v instanceof FolderIcon)){
- throw new IllegalArgumentException("Input must be a FolderIcon");
- }
-
- Folder folder = ((FolderIcon) v).getFolder();
- if (!folder.isOpen() && !folder.isDestroyed()) {
- // Open the requested folder
- folder.animateOpen();
- }
- }
-
- /**
- * Event handler for the wallpaper picker button that appears after a long press
- * on the home screen.
- */
- public void onClickWallpaperPicker(View v) {
- if (!Utilities.isWallpaperAllowed(this)) {
- Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
- return;
- }
-
- int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
- float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
- setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
- Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
- .putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
-
- String pickerPackage = getString(R.string.wallpaper_picker_package);
- boolean hasTargetPackage = !TextUtils.isEmpty(pickerPackage);
- if (hasTargetPackage) {
- intent.setPackage(pickerPackage);
- }
-
- final Bundle launchOptions;
- if (v != null) {
- intent.setSourceBounds(getViewBounds(v));
- // If there is no target package, use the default intent chooser animation
- launchOptions = hasTargetPackage
- ? getActivityLaunchOptions(v, isInMultiWindowModeCompat())
- : null;
- } else {
- launchOptions = null;
- }
- try {
- startActivityForResult(intent, REQUEST_PICK_WALLPAPER, launchOptions);
- } catch (ActivityNotFoundException e) {
- setWaitingForResult(null);
- Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- }
- }
-
- private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
- try {
- StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
- try {
- // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
- // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
- // is enabled by default on NYC.
- StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
- .penaltyLog().build());
-
- if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- String id = ((ShortcutInfo) info).getDeepShortcutId();
- String packageName = intent.getPackage();
- DeepShortcutManager.getInstance(this).startShortcut(
- packageName, id, intent.getSourceBounds(), optsBundle, info.user);
- } else {
- // Could be launching some bookkeeping activity
- startActivity(intent, optsBundle);
- }
- } finally {
- StrictMode.setVmPolicy(oldPolicy);
- }
- } catch (SecurityException e) {
- // Due to legacy reasons, direct call shortcuts require Launchers to have the
- // corresponding permission. Show the appropriate permission prompt if that
- // is the case.
- if (intent.getComponent() == null
- && Intent.ACTION_CALL.equals(intent.getAction())
- && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
- PackageManager.PERMISSION_GRANTED) {
-
- setWaitingForResult(PendingRequestArgs
- .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
- requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
- REQUEST_PERMISSION_CALL_PHONE);
- } else {
- // No idea why this was thrown.
- throw e;
- }
- }
- }
-
@TargetApi(Build.VERSION_CODES.M)
- public Bundle getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
+ @Override
+ public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
return useDefaultLaunchOptions
? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v)
: mAppTransitionManager.getActivityLaunchOptions(this, v);
}
- public Rect getViewBounds(View v) {
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+ public LauncherAppTransitionManager getAppTransitionManager() {
+ return mAppTransitionManager;
+ }
+
+ @TargetApi(Build.VERSION_CODES.M)
+ @Override
+ protected boolean onErrorStartingShortcut(Intent intent, ItemInfo info) {
+ // Due to legacy reasons, direct call shortcuts require Launchers to have the
+ // corresponding permission. Show the appropriate permission prompt if that
+ // is the case.
+ if (intent.getComponent() == null
+ && Intent.ACTION_CALL.equals(intent.getAction())
+ && checkSelfPermission(android.Manifest.permission.CALL_PHONE) !=
+ PackageManager.PERMISSION_GRANTED) {
+
+ setWaitingForResult(PendingRequestArgs
+ .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
+ requestPermissions(new String[]{android.Manifest.permission.CALL_PHONE},
+ REQUEST_PERMISSION_CALL_PHONE);
+ return true;
+ } else {
+ return false;
+ }
}
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
- mAppLaunchSuccess = false;
- if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
- Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
- return mAppLaunchSuccess;
- }
-
- boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
- && (item instanceof ShortcutInfo)
- && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && !((ShortcutInfo) item).isPromise();
-
- // Only launch using the new animation if the shortcut has not opted out (this is a
- // private contract between launcher and may be ignored in the future).
- boolean useLaunchAnimation = (v != null) &&
- !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
- Bundle optsBundle = useLaunchAnimation
- ? getActivityLaunchOptions(v, isShortcut || isInMultiWindowModeCompat())
- : null;
-
- UserHandle user = item == null ? null : item.user;
-
- // Prepare intent
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (v != null) {
- intent.setSourceBounds(getViewBounds(v));
- }
- try {
- if (isShortcut) {
- // Shortcuts need some special checks due to legacy reasons.
- startShortcutIntentSafely(intent, optsBundle, item);
- } else if (user == null || user.equals(Process.myUserHandle())) {
- // Could be launching some bookkeeping activity
- startActivity(intent, optsBundle);
- } else {
- LauncherAppsCompat.getInstance(this).startActivityForProfile(
- intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
- }
-
- if (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
- // launching, enable the press state and keep this reference to reset the press
- // state when we return to launcher.
- BubbleTextView btv = (BubbleTextView) v;
- btv.setStayPressed(true);
- setOnResumeCallback(btv);
- }
- mAppLaunchSuccess = true;
- getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
- } catch (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);
+ mAppLaunchSuccess = super.startActivitySafely(v, intent, item);
+ if (mAppLaunchSuccess && 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
+ // launching, enable the press state and keep this reference to reset the press
+ // state when we return to launcher.
+ BubbleTextView btv = (BubbleTextView) v;
+ btv.setStayPressed(true);
+ setOnResumeCallback(btv);
}
return mAppLaunchSuccess;
}
- @Override
- public boolean dispatchTouchEvent(MotionEvent ev) {
- mLastDispatchTouchEvent.set(ev.getX(), ev.getY());
- return super.dispatchTouchEvent(ev);
- }
-
- @Override
- public boolean onLongClick(View v) {
- if (!isDraggingEnabled()) return false;
- if (isWorkspaceLocked()) return false;
- if (!isInState(NORMAL) && !isInState(OVERVIEW)) return false;
-
- boolean ignoreLongPressToOverview =
- mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEvent.x);
-
- if (v instanceof Workspace) {
- if (!isInState(OVERVIEW)) {
- if (!mWorkspace.isTouchActive() && !ignoreLongPressToOverview) {
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.WORKSPACE,
- mWorkspace.getCurrentPage());
- UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
- mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- CellLayout.CellInfo longClickCellInfo = null;
- View itemUnderLongClick = null;
- if (v.getTag() instanceof ItemInfo) {
- ItemInfo info = (ItemInfo) v.getTag();
- longClickCellInfo = new CellLayout.CellInfo(v, info);
- itemUnderLongClick = longClickCellInfo.cell;
- mPendingRequestArgs = null;
- }
-
- // The hotseat touch handling does not go through Workspace, and we always allow long press
- // on hotseat items.
- if (!mDragController.isDragging()) {
- if (itemUnderLongClick == null) {
- // User long pressed on empty space
- if (mWorkspace.isPageRearrangeEnabled()) {
- mWorkspace.startReordering(v);
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.OVERVIEW);
- } else {
- if (ignoreLongPressToOverview) {
- return false;
- }
- getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
- Action.Direction.NONE, ContainerType.WORKSPACE,
- mWorkspace.getCurrentPage());
- UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
- }
- mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- } else {
- final boolean isAllAppsButton =
- !FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
- mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
- longClickCellInfo.cellX, longClickCellInfo.cellY));
- if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
- // User long pressed on an item
- mWorkspace.startDrag(longClickCellInfo, new DragOptions());
- }
- }
- }
- return true;
- }
-
boolean isHotseatLayout(View layout) {
// TODO: Remove this method
return mHotseat != null && layout != null &&
@@ -2131,6 +1711,7 @@
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onTrimMemory(level);
}
+ UiFactory.onTrimMemory(this, level);
}
@Override
@@ -2195,9 +1776,10 @@
// Clear the workspace because it's going to be rebound
mWorkspace.clearDropTargets();
mWorkspace.removeAllWorkspaceScreens();
+ mAppWidgetHost.clearViews();
if (mHotseat != null) {
- mHotseat.resetLayout();
+ mHotseat.resetLayout(mDeviceProfile.isVerticalBarLayout());
}
TraceHelper.endSection("startBinding");
}
@@ -2351,6 +1933,8 @@
mWorkspace.postDelayed(new Runnable() {
public void run() {
if (mWorkspace != null) {
+ AbstractFloatingView.closeAllOpenViews(Launcher.this, false);
+
mWorkspace.snapToPage(newScreenIndex);
mWorkspace.postDelayed(startBounceAnimRunnable,
NEW_APPS_ANIMATION_DELAY);
@@ -2520,6 +2104,11 @@
mPendingExecutor.markCompleted();
}
mPendingExecutor = executor;
+ if (!isInState(ALL_APPS)) {
+ mAppsView.getAppsStore().setDeferUpdates(true);
+ mPendingExecutor.execute(() -> mAppsView.getAppsStore().setDeferUpdates(false));
+ }
+
executor.attachTo(this);
}
@@ -2581,30 +2170,14 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindAllApplications(ArrayList<AppInfo> apps) {
- if (mAppsView != null) {
- Executor pendingExecutor = getPendingExecutor();
- if (pendingExecutor != null && !isInState(ALL_APPS)) {
- // Wait until the fade in animation has finished before setting all apps list.
- pendingExecutor.execute(() -> bindAllApplications(apps));
- return;
- }
+ mAppsView.getAppsStore().setApps(apps);
- mAppsView.setApps(apps);
- }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
/**
- * Returns an Executor that will run after the launcher is first drawn (including after the
- * initial fade in animation). Returns null if the first draw has already occurred.
- */
- public @Nullable Executor getPendingExecutor() {
- return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
- }
-
- /**
* Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
* because LauncherModel's map is updated in the background, while Launcher runs on the UI.
*/
@@ -2620,16 +2193,12 @@
*/
@Override
public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
- if (mAppsView != null) {
- mAppsView.addOrUpdateApps(apps);
- }
+ mAppsView.getAppsStore().addOrUpdateApps(apps);
}
@Override
public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
- if (mAppsView != null) {
- mAppsView.updatePromiseAppProgress(app);
- }
+ mAppsView.getAppsStore().updatePromiseAppProgress(app);
}
@Override
@@ -2675,10 +2244,7 @@
@Override
public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
- // Update AllApps
- if (mAppsView != null) {
- mAppsView.removeApps(appInfos);
- }
+ mAppsView.getAppsStore().removeApps(appInfos);
}
@Override
@@ -2698,10 +2264,6 @@
mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
}
- public boolean isRotationEnabled () {
- return mRotationEnabled;
- }
-
/**
* $ adb shell dumpsys activity com.android.launcher3.Launcher [--all]
*/
@@ -2731,18 +2293,20 @@
writer.println(prefix + " " + tag.toString());
}
}
-
- try {
- FileLog.flushAll(writer);
- } catch (Exception e) {
- // Ignore
- }
}
writer.println(prefix + "Misc:");
writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
writer.println(" mPendingActivityResult=" + mPendingActivityResult);
+ writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
+ writer.println(" orientation=" + getResources().getConfiguration().orientation);
+
+ try {
+ FileLog.flushAll(writer);
+ } catch (Exception e) {
+ // Ignore
+ }
mModel.dumpState(prefix, fd, writer, args);
@@ -2810,6 +2374,25 @@
return super.onKeyShortcut(keyCode, event);
}
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
+ // KEYCODE_MENU is sent by some tests, for example
+ // LauncherJankTests#testWidgetsContainerFling. Don't just remove its handling.
+ if (!mDragController.isDragging() && !mWorkspace.isSwitchingState() &&
+ isInState(NORMAL)) {
+ // Close any open floating views.
+ AbstractFloatingView.closeAllOpenViews(this);
+
+ // Setting the touch point to (-1, -1) will show the options popup in the center of
+ // the screen.
+ OptionsPopupView.showDefaultOptions(this, -1, -1);
+ }
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
public static Launcher getLauncher(Context context) {
if (context instanceof Launcher) {
return (Launcher) context;
@@ -2817,38 +2400,6 @@
return ((Launcher) ((ContextWrapper) context).getBaseContext());
}
- private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener {
-
- @Override
- public void onSharedPreferenceChanged(
- SharedPreferences sharedPreferences, String key) {
- if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
- // Recreate the activity so that it initializes the rotation preference again.
- recreate();
- }
- }
- }
-
- @Override
- public void onActionModeStarted(ActionMode mode) {
- super.onActionModeStarted(mode);
- mCurrentActionMode = mode;
- }
-
- @Override
- public void onActionModeFinished(ActionMode mode) {
- super.onActionModeFinished(mode);
- mCurrentActionMode = null;
- }
-
- public boolean finishAutoCancelActionMode() {
- if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
- mCurrentActionMode.finish();
- return true;
- }
- return false;
- }
-
/**
* Callback for listening for onResume
*/
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index be2dc54..04f9b3a 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.view.View;
/**
@@ -34,7 +33,7 @@
context, R.string.app_transition_manager_class);
}
- public Bundle getDefaultActivityLaunchOptions(Launcher launcher, View v) {
+ public ActivityOptions getDefaultActivityLaunchOptions(Launcher launcher, View v) {
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
@@ -49,19 +48,18 @@
height = bounds.height();
}
}
- return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height)
- .toBundle();
+ return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
} else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
// On L devices, we use the device default slide-up transition.
// On L MR1 devices, we use a custom version of the slide-up transition which
// doesn't have the delay present in the device default.
return ActivityOptions.makeCustomAnimation(launcher, R.anim.task_open_enter,
- R.anim.no_anim).toBundle();
+ R.anim.no_anim);
}
return null;
}
- public Bundle getActivityLaunchOptions(Launcher launcher, View v) {
+ public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
return getDefaultActivityLaunchOptions(launcher, v);
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7bc7139..56671a1 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -236,7 +236,7 @@
}
@Override
- protected void clearViews() {
+ public void clearViews() {
super.clearViews();
mViews.clear();
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index c713992..80758c9 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -94,4 +94,12 @@
public boolean isCustomWidget() {
return provider.getClassName().startsWith(CLS_CUSTOM_WIDGET_PREFIX);
}
+
+ public int getWidgetFeatures() {
+ if (Utilities.ATLEAST_P) {
+ return widgetFeatures;
+ } else {
+ return 0;
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 914d9eb..35faaea 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -37,7 +37,6 @@
* Activity life-cycle methods. These methods are triggered after
* the code in the corresponding Launcher method is executed.
*/
- void preOnCreate();
void onCreate(Bundle savedInstanceState);
void onResume();
void onStart();
@@ -71,4 +70,12 @@
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
boolean hasSettings();
+
+ /**
+ * Called when launcher integrated quickstep and some quickstep gesture started. It can be
+ * called multiple times for a single gesture an UI or background thread.
+ *
+ * @param isVisible if Launcher was visible when the gesture started.
+ */
+ void onQuickstepGestureStarted(boolean isVisible);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 929606e..04a32f7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -135,6 +135,8 @@
};
public interface Callbacks {
+ public void rebindModel();
+
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
public void startBinding();
@@ -196,8 +198,9 @@
enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
}
- public ModelWriter getWriter(boolean hasVerticalHotseat) {
- return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
+ public ModelWriter getWriter(boolean hasVerticalHotseat, boolean verifyChanges) {
+ return new ModelWriter(mApp.getContext(), this, sBgDataModel,
+ hasVerticalHotseat, verifyChanges);
}
static void checkItemInfoLocked(
@@ -444,11 +447,7 @@
if (mCallbacks != null && mCallbacks.get() != null) {
final Callbacks oldCallbacks = mCallbacks.get();
// Clear any pending bind-runnables from the synchronized load process.
- mUiExecutor.execute(new Runnable() {
- public void run() {
- oldCallbacks.clearPendingBinds();
- }
- });
+ mUiExecutor.execute(oldCallbacks::clearPendingBinds);
// If there is already one running, tell it to stop.
stopLoader();
@@ -493,6 +492,15 @@
}
}
+ public void startLoaderForResultsIfNotLoaded(LoaderResults results) {
+ synchronized (mLock) {
+ if (!isModelLoaded()) {
+ Log.d(TAG, "Workspace not loaded, loading now");
+ startLoaderForResults(results);
+ }
+ }
+ }
+
/**
* Loads the workspace screen ids in an ordered list.
*/
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 98568e4..7d208d4 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -34,7 +34,6 @@
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
@@ -54,14 +53,12 @@
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.DbDowngradeHelper;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.provider.RestoreDbTask;
-import com.android.launcher3.util.ManagedProfileHeuristic;
-import com.android.launcher3.util.NoLocaleSqliteContext;
+import com.android.launcher3.util.NoLocaleSQLiteHelper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
@@ -116,11 +113,8 @@
mListenerHandler = new Handler(mListenerWrapper);
// The content provider exists for the entire duration of the launcher main process and
- // is the first component to get created. Initializing FileLog here ensures that it's
- // always available in the main process.
- FileLog.setDir(getContext().getApplicationContext().getFilesDir());
- IconShapeOverride.apply(getContext());
- SessionCommitReceiver.applyDefaultUserPrefs(getContext());
+ // is the first component to get created.
+ MainProcessInitializer.initialize(getContext().getApplicationContext());
return true;
}
@@ -545,7 +539,7 @@
/**
* The class is subclassed in tests to create an in-memory db.
*/
- public static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
+ public static class DatabaseHelper extends NoLocaleSQLiteHelper implements LayoutParserCallback {
private final Handler mWidgetHostResetHandler;
private final Context mContext;
private long mMaxItemId = -1;
@@ -571,7 +565,7 @@
*/
public DatabaseHelper(
Context context, Handler widgetHostResetHandler, String tableName) {
- super(new NoLocaleSqliteContext(context), tableName, null, SCHEMA_VERSION);
+ super(context, tableName, SCHEMA_VERSION);
mContext = context;
mWidgetHostResetHandler = widgetHostResetHandler;
}
@@ -627,10 +621,6 @@
// Set the flag for empty DB
Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
-
- // When a new DB is created, remove all previously stored managed profile information.
- ManagedProfileHeuristic.processAllUsers(Collections.<UserHandle>emptyList(),
- mContext);
}
public long getDefaultUserSerial() {
@@ -794,7 +784,7 @@
case 23:
// No-op
case 24:
- ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
+ // No-op
case 25:
convertShortcutsToLauncherActivities(db);
case 26:
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index fc4de2d..b1273b6 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -29,6 +29,7 @@
private int mRightInsetBarWidth;
private View mAlignedView;
+ private WindowStateListener mWindowStateListener;
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -117,4 +118,31 @@
}
}
}
+
+ public void setWindowStateListener(WindowStateListener listener) {
+ mWindowStateListener = listener;
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasWindowFocus) {
+ super.onWindowFocusChanged(hasWindowFocus);
+ if (mWindowStateListener != null) {
+ mWindowStateListener.onWindowFocusChanged(hasWindowFocus);
+ }
+ }
+
+ @Override
+ protected void onWindowVisibilityChanged(int visibility) {
+ super.onWindowVisibilityChanged(visibility);
+ if (mWindowStateListener != null) {
+ mWindowStateListener.onWindowVisibilityChanged(visibility);
+ }
+ }
+
+ public interface WindowStateListener {
+
+ void onWindowFocusChanged(boolean hasFocus);
+
+ void onWindowVisibilityChanged(int visibility);
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 472a5a9..4697b82 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -18,14 +18,15 @@
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.view.View;
import android.view.animation.Interpolator;
-import com.android.launcher3.uioverrides.AllAppsState;
import com.android.launcher3.states.SpringLoadedState;
+import com.android.launcher3.uioverrides.AllAppsState;
+import com.android.launcher3.uioverrides.FastOverviewState;
import com.android.launcher3.uioverrides.OverviewState;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -38,12 +39,30 @@
*/
public class LauncherState {
+
+ /**
+ * Set of elements indicating various workspace elements which change visibility across states
+ * Note that workspace is not included here as in that case, we animate individual pages
+ */
+ public static final int NONE = 0;
+ public static final int HOTSEAT_ICONS = 1 << 0;
+ public static final int HOTSEAT_SEARCH_BOX = 1 << 1;
+ public static final int ALL_APPS_HEADER = 1 << 2;
+ 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 DRAG_HANDLE_INDICATOR = 1 << 5;
+
protected static final int FLAG_SHOW_SCRIM = 1 << 0;
protected static final int FLAG_MULTI_PAGE = 1 << 1;
protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 2;
protected static final int FLAG_DISABLE_RESTORE = 1 << 3;
protected static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = 1 << 4;
protected static final int FLAG_DISABLE_PAGE_CLIPPING = 1 << 5;
+ protected static final int FLAG_PAGE_BACKGROUNDS = 1 << 6;
+ protected static final int FLAG_ALL_APPS_SCRIM = 1 << 7;
+ protected static final int FLAG_DISABLE_INTERACTION = 1 << 8;
+ protected static final int FLAG_OVERVIEW_UI = 1 << 9;
+ protected static final int FLAG_HIDE_BACK_BUTTON = 1 << 10;
protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER =
new PageAlphaProvider(ACCEL_2) {
@@ -53,19 +72,21 @@
}
};
- private static final LauncherState[] sAllStates = new LauncherState[4];
+ private static final LauncherState[] sAllStates = new LauncherState[5];
/**
* TODO: Create a separate class for NORMAL state.
*/
public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
- 0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED);
+ 0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON);
- public static final LauncherState ALL_APPS = new AllAppsState(1);
-
- public static final LauncherState SPRING_LOADED = new SpringLoadedState(2);
-
- public static final LauncherState OVERVIEW = new OverviewState(3);
+ /**
+ * Various Launcher states arranged in the increasing order of UI layers
+ */
+ public static final LauncherState SPRING_LOADED = new SpringLoadedState(1);
+ public static final LauncherState OVERVIEW = new OverviewState(2);
+ public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3);
+ public static final LauncherState ALL_APPS = new AllAppsState(4);
public final int ordinal;
@@ -96,6 +117,9 @@
* @see WorkspaceStateTransitionAnimation
*/
public final boolean hasScrim;
+ public final boolean hasWorkspacePageBackground;
+ public final boolean hasAllAppsScrim;
+
public final int transitionDuration;
/**
@@ -109,11 +133,30 @@
*/
public final boolean disablePageClipping;
+ /**
+ * True if launcher can not be directly interacted in this state;
+ */
+ public final boolean disableInteraction;
+
+ /**
+ * True if the state has overview panel visible.
+ */
+ public final boolean overviewUi;
+
+ /**
+ * True if the back button should be hidden when in this state (assuming no floating views are
+ * open, launcher has window focus, etc).
+ */
+ public final boolean hideBackButton;
+
public LauncherState(int id, int containerType, int transitionDuration, int flags) {
this.containerType = containerType;
this.transitionDuration = transitionDuration;
this.hasScrim = (flags & FLAG_SHOW_SCRIM) != 0;
+ this.hasWorkspacePageBackground = (flags & FLAG_PAGE_BACKGROUNDS) != 0;
+ this.hasAllAppsScrim = (flags & FLAG_ALL_APPS_SCRIM) != 0;
+
this.hasMultipleVisiblePages = (flags & FLAG_MULTI_PAGE) != 0;
this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
@@ -121,6 +164,9 @@
this.disableRestore = (flags & FLAG_DISABLE_RESTORE) != 0;
this.workspaceIconsCanBeDragged = (flags & FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED) != 0;
this.disablePageClipping = (flags & FLAG_DISABLE_PAGE_CLIPPING) != 0;
+ this.disableInteraction = (flags & FLAG_DISABLE_INTERACTION) != 0;
+ this.overviewUi = (flags & FLAG_OVERVIEW_UI) != 0;
+ this.hideBackButton = (flags & FLAG_HIDE_BACK_BUTTON) != 0;
this.ordinal = id;
sAllStates[id] = this;
@@ -134,8 +180,13 @@
return new float[] {1, 0, 0};
}
- public float getHoseatAlpha(Launcher launcher) {
- return 1f;
+ /**
+ * Returns 2 floats designating how to transition overview:
+ * scale for the current and adjacent pages
+ * translationY factor where 0 is top aligned and 0.5 is centered vertically
+ */
+ public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+ return new float[] {1.2f, 0.2f};
}
public void onStateEnabled(Launcher launcher) {
@@ -148,6 +199,13 @@
return launcher.getWorkspace();
}
+ public int getVisibleElements(Launcher launcher) {
+ if (launcher.getDeviceProfile().isVerticalBarLayout()) {
+ return HOTSEAT_ICONS | DRAG_HANDLE_INDICATOR;
+ }
+ return HOTSEAT_ICONS | DRAG_HANDLE_INDICATOR | HOTSEAT_SEARCH_BOX;
+ }
+
/**
* Fraction shift in the vertical translation UI and related properties
*
@@ -185,6 +243,8 @@
public void onStateTransitionEnd(Launcher launcher) {
if (this == NORMAL) {
UiFactory.resetOverview(launcher);
+ // Clear any rotation locks when going to normal state
+ launcher.getRotationHelper().setCurrentStateRequest(REQUEST_NONE);
}
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 8eeeec3..534c8ae 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -28,8 +29,12 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.anim.PropertySetter.AnimatedPropertySetter;
import com.android.launcher3.uioverrides.UiFactory;
+import java.util.ArrayList;
+
/**
* TODO: figure out what kind of tests we can write for this
*
@@ -78,6 +83,7 @@
private final AnimationConfig mConfig = new AnimationConfig();
private final Handler mUiHandler;
private final Launcher mLauncher;
+ private final ArrayList<StateListener> mListeners = new ArrayList<>();
private StateHandler[] mStateHandlers;
private LauncherState mState = NORMAL;
@@ -85,7 +91,7 @@
private LauncherState mLastStableState = NORMAL;
private LauncherState mCurrentStableState = NORMAL;
- private StateListener mStateListener;
+ private LauncherState mRestState;
public LauncherStateManager(Launcher l) {
mUiHandler = new Handler(Looper.getMainLooper());
@@ -103,8 +109,12 @@
return mStateHandlers;
}
- public void setStateListener(StateListener stateListener) {
- mStateListener = stateListener;
+ public void addStateListener(StateListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeStateListener(StateListener listener) {
+ mListeners.remove(listener);
}
/**
@@ -155,13 +165,26 @@
}
private void goToState(LauncherState state, boolean animated, long delay,
- Runnable onCompleteRunnable) {
- if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null) {
- // Run any queued runnable
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
+ final Runnable onCompleteRunnable) {
+ if (mLauncher.isInState(state)) {
+ if (mConfig.mCurrentAnimation == null) {
+ // Run any queued runnable
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ return;
+ } else if (!mConfig.userControlled && animated) {
+ // We are running the same animation as requested
+ if (onCompleteRunnable != null) {
+ mConfig.mCurrentAnimation.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ onCompleteRunnable.run();
+ }
+ });
+ }
+ return;
}
- return;
}
// Cancel the current animation
@@ -172,8 +195,9 @@
for (StateHandler handler : getStateHandlers()) {
handler.setState(state);
}
- if (mStateListener != null) {
- mStateListener.onStateSetImmediately(state);
+
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onStateSetImmediately(state);
}
onStateTransitionEnd(state);
@@ -221,6 +245,7 @@
protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
+
for (StateHandler handler : getStateHandlers()) {
builder.startTag(handler);
handler.setStateWithAnimation(state, builder, mConfig);
@@ -233,16 +258,16 @@
public void onAnimationStart(Animator animation) {
// Change the internal state only when the transition actually starts
onStateTransitionStart(state);
- if (mStateListener != null) {
- mStateListener.onStateTransitionStart(state);
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onStateTransitionStart(state);
}
}
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- if (mStateListener != null) {
- mStateListener.onStateTransitionComplete(mState);
+ for (int i = mListeners.size() - 1; i >= 0; i--) {
+ mListeners.get(i).onStateTransitionComplete(state);
}
}
@@ -269,6 +294,7 @@
// Only disable clipping if needed, otherwise leave it as previous value.
mLauncher.getWorkspace().setClipChildren(false);
}
+ UiFactory.onLauncherStateOrResumeChanged(mLauncher);
}
private void onStateTransitionEnd(LauncherState state) {
@@ -280,14 +306,44 @@
state.onStateTransitionEnd(mLauncher);
mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
mLauncher.finishAutoCancelActionMode();
+
+ if (state == NORMAL) {
+ setRestState(null);
+ }
+
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ UiFactory.onLauncherStateOrResumeChanged(mLauncher);
+ }
+
+ public void onWindowFocusChanged() {
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
}
public LauncherState getLastState() {
return mLastStableState;
}
+ public void moveToRestState() {
+ if (mConfig.mCurrentAnimation != null && mConfig.userControlled) {
+ // The user is doing something. Lets not mess it up
+ return;
+ }
+ if (mState.disableRestore) {
+ goToState(getRestState());
+ // Reset history
+ mLastStableState = NORMAL;
+ }
+ }
+
+ public LauncherState getRestState() {
+ return mRestState == null ? NORMAL : mRestState;
+ }
+
+ public void setRestState(LauncherState restState) {
+ mRestState = restState;
+ }
+
/**
* Cancels the current animation.
*/
@@ -295,6 +351,19 @@
mConfig.reset();
}
+ /**
+ * Sets the animation as the current state animation, i.e., canceled when
+ * starting another animation and may block some launcher interactions while running.
+ */
+ public void setCurrentAnimation(AnimatorSet anim) {
+ boolean reapplyNeeded = mConfig.mCurrentAnimation != null;
+ cancelAnimation();
+ if (reapplyNeeded) {
+ reapplyState();
+ }
+ mConfig.setAnimation(anim);
+ }
+
private class StartAnimRunnable implements Runnable {
private final AnimatorSet mAnim;
@@ -320,12 +389,14 @@
public static class AnimationConfig extends AnimatorListenerAdapter {
public long duration;
public boolean userControlled;
+ private PropertySetter mProperSetter;
private AnimatorSet mCurrentAnimation;
public void reset() {
duration = 0;
userControlled = false;
+ mProperSetter = null;
if (mCurrentAnimation != null) {
mCurrentAnimation.setDuration(0);
@@ -334,6 +405,14 @@
}
}
+ public PropertySetter getProperSetter(AnimatorSetBuilder builder) {
+ if (mProperSetter == null) {
+ mProperSetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
+ : new AnimatedPropertySetter(duration, builder);
+ }
+ return mProperSetter;
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
if (mCurrentAnimation == animation) {
diff --git a/src/com/android/launcher3/MainProcessInitializer.java b/src/com/android/launcher3/MainProcessInitializer.java
new file mode 100644
index 0000000..462eadb
--- /dev/null
+++ b/src/com/android/launcher3/MainProcessInitializer.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.content.Context;
+
+import com.android.launcher3.graphics.IconShapeOverride;
+import com.android.launcher3.logging.FileLog;
+
+/**
+ * Utility class to handle one time initializations of the main process
+ */
+public class MainProcessInitializer {
+
+ public static void initialize(Context context) {
+ Utilities.getOverrideObject(
+ MainProcessInitializer.class, context, R.string.main_process_initializer_class)
+ .init(context);
+ }
+
+ protected void init(Context context) {
+ FileLog.setDir(context.getApplicationContext().getFilesDir());
+ IconShapeOverride.apply(context);
+ SessionCommitReceiver.applyDefaultUserPrefs(context);
+ }
+}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 0ebae81..15bf76d 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -19,10 +19,7 @@
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
-import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -30,8 +27,6 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputDevice;
@@ -46,9 +41,9 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
+import android.widget.ScrollView;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.Thunk;
@@ -59,13 +54,12 @@
* An abstraction of the original Workspace which supports browsing through a
* sequential list of "pages"
*/
-public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarchyChangeListener {
+public abstract class PagedView<T extends View & PageIndicator> extends ViewGroup {
private static final String TAG = "PagedView";
private static final boolean DEBUG = false;
- protected static final int INVALID_PAGE = -1;
- // the min drag distance for a fling to register, to prevent random page shifts
- private static final int MIN_LENGTH_FOR_FLING = 25;
+ protected static final int INVALID_PAGE = -1;
+ protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
@@ -108,31 +102,21 @@
private VelocityTracker mVelocityTracker;
protected int mPageSpacing = 0;
- private float mParentDownMotionX;
- private float mParentDownMotionY;
private float mDownMotionX;
private float mDownMotionY;
- private float mDownScrollX;
- private float mDragViewBaselineLeft;
private float mLastMotionX;
private float mLastMotionXRemainder;
- private float mLastMotionY;
private float mTotalMotionX;
- private boolean mCancelTap;
-
private int[] mPageScrolls;
protected final static int TOUCH_STATE_REST = 0;
protected final static int TOUCH_STATE_SCROLLING = 1;
protected final static int TOUCH_STATE_PREV_PAGE = 2;
protected final static int TOUCH_STATE_NEXT_PAGE = 3;
- protected final static int TOUCH_STATE_REORDERING = 4;
protected int mTouchState = TOUCH_STATE_REST;
- protected OnLongClickListener mLongClickListener;
-
protected int mTouchSlop;
private int mMaximumVelocity;
protected boolean mAllowOverScroll = true;
@@ -154,27 +138,7 @@
// Page Indicator
@Thunk int mPageIndicatorViewId;
- protected PageIndicator mPageIndicator;
-
- // Reordering
- // We use the min scale to determine how much to expand the actually PagedView measured
- // dimensions such that when we are zoomed out, the view is not clipped
- private static int REORDERING_DROP_REPOSITION_DURATION = 200;
- @Thunk static int REORDERING_REORDER_REPOSITION_DURATION = 300;
- private static int REORDERING_SIDE_PAGE_HOVER_TIMEOUT = 80;
-
- @Thunk View mDragView;
- private Runnable mSidePageHoverRunnable;
- @Thunk int mSidePageHoverIndex = -1;
- // This variable's scope is only for the duration of startReordering() and endReordering()
- private boolean mReorderingStarted = false;
- // This variable's scope is for the duration of startReordering() and after the zoomIn()
- // animation after endReordering()
- private boolean mIsReordering;
- // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
- private static final int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
- private int mPostReorderingPreZoomInRemainingAnimationCount;
- private Runnable mPostReorderingPreZoomInRunnable;
+ protected T mPageIndicator;
// Convenience/caching
private static final Matrix sTmpInvMatrix = new Matrix();
@@ -182,7 +146,7 @@
private static final Rect sTmpRect = new Rect();
protected final Rect mInsets = new Rect();
- protected final boolean mIsRtl;
+ protected boolean mIsRtl;
// Similar to the platform implementation of isLayoutValid();
protected boolean mIsLayoutValid;
@@ -224,8 +188,10 @@
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
- setOnHierarchyChangeListener(this);
- setWillNotDraw(false);
+
+ if (Utilities.ATLEAST_OREO) {
+ setDefaultFocusHighlightEnabled(false);
+ }
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -235,54 +201,13 @@
public void initParentViews(View parent) {
if (mPageIndicatorViewId > -1) {
- mPageIndicator = (PageIndicator) parent.findViewById(mPageIndicatorViewId);
+ mPageIndicator = parent.findViewById(mPageIndicatorViewId);
mPageIndicator.setMarkersCount(getChildCount());
- mPageIndicator.setContentDescription(getPageIndicatorDescription());
+ mPageIndicator.setPageDescription(getPageIndicatorDescription());
}
}
- // Convenience methods to map points from self to parent and vice versa
- private float[] mapPointFromViewToParent(View v, float x, float y) {
- sTmpPoint[0] = x;
- sTmpPoint[1] = y;
- v.getMatrix().mapPoints(sTmpPoint);
- sTmpPoint[0] += v.getLeft();
- sTmpPoint[1] += v.getTop();
- return sTmpPoint;
- }
- private float[] mapPointFromParentToView(View v, float x, float y) {
- sTmpPoint[0] = x - v.getLeft();
- sTmpPoint[1] = y - v.getTop();
- v.getMatrix().invert(sTmpInvMatrix);
- sTmpInvMatrix.mapPoints(sTmpPoint);
- return sTmpPoint;
- }
-
- private void updateDragViewTranslationDuringDrag() {
- if (mDragView != null) {
- float x = (mLastMotionX - mDownMotionX) + (getScrollX() - mDownScrollX) +
- (mDragViewBaselineLeft - mDragView.getLeft());
- float y = mLastMotionY - mDownMotionY;
- mDragView.setTranslationX(x);
- mDragView.setTranslationY(y);
-
- if (DEBUG) Log.d(TAG, "PagedView.updateDragViewTranslationDuringDrag(): "
- + x + ", " + y);
- }
- }
-
- @Override
- public void setScaleX(float scaleX) {
- super.setScaleX(scaleX);
- if (isReordering(true)) {
- float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
- mLastMotionX = p[0];
- mLastMotionY = p[1];
- updateDragViewTranslationDuringDrag();
- }
- }
-
- public PageIndicator getPageIndicator() {
+ public T getPageIndicator() {
return mPageIndicator;
}
@@ -337,6 +262,7 @@
// updating current page on the pass.
if (resetNextPage) {
mNextPage = INVALID_PAGE;
+ pageEndTransition();
}
}
@@ -346,6 +272,7 @@
// updating current page on the pass.
if (resetNextPage) {
mNextPage = INVALID_PAGE;
+ pageEndTransition();
}
}
@@ -382,12 +309,9 @@
}
private void updatePageIndicator() {
- // Update the page indicator (when we aren't reordering)
if (mPageIndicator != null) {
- mPageIndicator.setContentDescription(getPageIndicatorDescription());
- if (!isReordering(false)) {
- mPageIndicator.setActiveMarker(getNextPage());
- }
+ mPageIndicator.setPageDescription(getPageIndicatorDescription());
+ mPageIndicator.setActiveMarker(getNextPage());
}
}
protected void pageBeginTransition() {
@@ -423,21 +347,6 @@
mWasInOverscroll = false;
}
- /**
- * Registers the specified listener on each page contained in this workspace.
- *
- * @param l The listener used to respond to long clicks.
- */
- @Override
- public void setOnLongClickListener(OnLongClickListener l) {
- mLongClickListener = l;
- final int count = getPageCount();
- for (int i = 0; i < count; i++) {
- getPageAt(i).setOnLongClickListener(l);
- }
- super.setOnLongClickListener(l);
- }
-
protected int getUnboundedScrollX() {
return mUnboundedScrollX;
}
@@ -492,14 +401,6 @@
mOverScrollX = x;
super.scrollTo(x, y);
}
-
- // Update the last motion events when scrolling
- if (isReordering(true)) {
- float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
- mLastMotionX = p[0];
- mLastMotionY = p[1];
- updateDragViewTranslationDuringDrag();
- }
}
private void sendScrollAccessibilityEvent() {
@@ -523,6 +424,13 @@
return computeScrollHelper(true);
}
+ protected void announcePageForAccessibility() {
+ if (isAccessibilityEnabled(getContext())) {
+ // Notify the user when the page changes
+ announceForAccessibility(getCurrentPageDescription());
+ }
+ }
+
protected boolean computeScrollHelper(boolean shouldInvalidate) {
if (mScroller.computeScrollOffset()) {
// Don't bother scrolling if the page does not need to be moved
@@ -551,12 +459,9 @@
pageEndTransition();
}
- onPostReorderingAnimationCompleted();
- if (isAccessibilityEnabled(getContext())) {
- // Notify the user when the page changes
- announceForAccessibility(getCurrentPageDescription());
+ if (canAnnouncePageDescription()) {
+ announcePageForAccessibility();
}
- return true;
}
return false;
}
@@ -647,43 +552,13 @@
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
final int childCount = getChildCount();
- final int startIndex = mIsRtl ? childCount - 1 : 0;
- final int endIndex = mIsRtl ? -1 : childCount;
- final int delta = mIsRtl ? -1 : 1;
-
- int verticalPadding = getPaddingTop() + getPaddingBottom();
-
- int scrollOffsetLeft = mInsets.left + getPaddingLeft();
- int childLeft = scrollOffsetLeft;
-
boolean pageScrollChanged = false;
if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
mPageScrolls = new int[childCount];
pageScrollChanged = true;
}
-
- for (int i = startIndex; i != endIndex; i += delta) {
- final View child = getPageAt(i);
- if (child.getVisibility() != View.GONE) {
- int childTop = getPaddingTop() + mInsets.top;
- childTop += (getMeasuredHeight() - mInsets.top - mInsets.bottom - verticalPadding
- - child.getMeasuredHeight()) / 2;
-
- final int childWidth = child.getMeasuredWidth();
- final int childHeight = child.getMeasuredHeight();
-
- if (DEBUG) Log.d(TAG, "\tlayout-child" + i + ": " + childLeft + ", " + childTop);
- child.layout(childLeft, childTop,
- childLeft + child.getMeasuredWidth(), childTop + childHeight);
-
- final int pageScroll = childLeft - scrollOffsetLeft;
- if (mPageScrolls[i] != pageScroll) {
- pageScrollChanged = true;
- mPageScrolls[i] = pageScroll;
- }
-
- childLeft += childWidth + mPageSpacing + getChildGap();
- }
+ if (getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC)) {
+ pageScrollChanged = true;
}
final LayoutTransition transition = getLayoutTransition();
@@ -719,10 +594,51 @@
setCurrentPage(getNextPage());
}
mChildCountOnLastLayout = childCount;
+ }
- if (isReordering(true)) {
- updateDragViewTranslationDuringDrag();
+ /**
+ * Initializes {@code outPageScrolls} with scroll positions for view at that index. The length
+ * of {@code outPageScrolls} should be same as the the childCount
+ *
+ */
+ protected boolean getPageScrolls(int[] outPageScrolls, boolean layoutChildren,
+ ComputePageScrollsLogic scrollLogic) {
+ final int childCount = getChildCount();
+
+ final int startIndex = mIsRtl ? childCount - 1 : 0;
+ final int endIndex = mIsRtl ? -1 : childCount;
+ final int delta = mIsRtl ? -1 : 1;
+
+ int verticalPadding = getPaddingTop() + getPaddingBottom();
+
+ int scrollOffsetLeft = mInsets.left + getPaddingLeft();
+ int childLeft = scrollOffsetLeft;
+ boolean pageScrollChanged = false;
+
+ for (int i = startIndex; i != endIndex; i += delta) {
+ final View child = getPageAt(i);
+ if (scrollLogic.shouldIncludeView(child)) {
+ int childTop = getPaddingTop() + mInsets.top;
+ childTop += (getMeasuredHeight() - mInsets.top - mInsets.bottom - verticalPadding
+ - child.getMeasuredHeight()) / 2;
+ final int childWidth = child.getMeasuredWidth();
+
+ if (layoutChildren) {
+ final int childHeight = child.getMeasuredHeight();
+ child.layout(childLeft, childTop,
+ childLeft + child.getMeasuredWidth(), childTop + childHeight);
+ }
+
+ final int pageScroll = childLeft - scrollOffsetLeft;
+ if (outPageScrolls[i] != pageScroll) {
+ pageScrollChanged = true;
+ outPageScrolls[i] = pageScroll;
+ }
+
+ childLeft += childWidth + mPageSpacing + getChildGap();
+ }
}
+ return pageScrollChanged;
}
protected int getChildGap() {
@@ -748,63 +664,26 @@
requestLayout();
}
- @Override
- public void onChildViewAdded(View parent, View child) {
- // Update the page indicator, we don't update the page indicator as we
- // add/remove pages
- if (mPageIndicator != null && !isReordering(false)) {
- mPageIndicator.addMarker();
+ private void dispatchPageCountChanged() {
+ if (mPageIndicator != null) {
+ mPageIndicator.setMarkersCount(getChildCount());
}
-
// This ensures that when children are added, they get the correct transforms / alphas
// in accordance with any scroll effects.
invalidate();
}
@Override
- public void onChildViewRemoved(View parent, View child) {
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ dispatchPageCountChanged();
+ }
+
+ @Override
+ public void onViewRemoved(View child) {
+ super.onViewRemoved(child);
mCurrentPage = validateNewPage(mCurrentPage);
- invalidate();
- }
-
- private void removeMarkerForView() {
- // Update the page indicator, we don't update the page indicator as we
- // add/remove pages
- if (mPageIndicator != null && !isReordering(false)) {
- mPageIndicator.removeMarker();
- }
- }
-
- @Override
- public void removeView(View v) {
- // XXX: We should find a better way to hook into this before the view
- // gets removed form its parent...
- removeMarkerForView();
- super.removeView(v);
- }
- @Override
- public void removeViewInLayout(View v) {
- // XXX: We should find a better way to hook into this before the view
- // gets removed form its parent...
- removeMarkerForView();
- super.removeViewInLayout(v);
- }
- @Override
- public void removeViewAt(int index) {
- // XXX: We should find a better way to hook into this before the view
- // gets removed form its parent...
- removeMarkerForView();
- super.removeViewAt(index);
- }
- @Override
- public void removeAllViewsInLayout() {
- // Update the page indicator, we don't update the page indicator as we
- // add/remove pages
- if (mPageIndicator != null) {
- mPageIndicator.setMarkersCount(0);
- }
-
- super.removeAllViewsInLayout();
+ dispatchPageCountChanged();
}
protected int getChildOffset(int index) {
@@ -979,12 +858,7 @@
// Remember location of down touch
mDownMotionX = x;
mDownMotionY = y;
- mDownScrollX = getScrollX();
mLastMotionX = x;
- mLastMotionY = y;
- float[] p = mapPointFromViewToParent(this, x, y);
- mParentDownMotionX = p[0];
- mParentDownMotionY = p[1];
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
@@ -1032,6 +906,10 @@
return mTouchState != TOUCH_STATE_REST;
}
+ public boolean isHandlingTouch() {
+ return mTouchState != TOUCH_STATE_REST;
+ }
+
protected void determineScrollingStart(MotionEvent ev) {
determineScrollingStart(ev, 1.0f);
}
@@ -1143,22 +1021,12 @@
dampedOverScroll(amount);
}
- /**
- * return true if freescroll has been enabled, false otherwise
- */
- protected void enableFreeScroll() {
- enableFreeScroll(false);
- }
protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
setEnableFreeScroll(true);
mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
}
- protected void disableFreeScroll() {
- setEnableFreeScroll(false);
- }
-
private void setEnableFreeScroll(boolean freeScroll) {
boolean wasFreeScroll = mFreeScroll;
mFreeScroll = freeScroll;
@@ -1176,27 +1044,6 @@
mAllowOverScroll = enable;
}
- private int getNearestHoverOverPageIndex() {
- if (mDragView != null) {
- int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2)
- + mDragView.getTranslationX());
- int minDistance = Integer.MAX_VALUE;
- int minIndex = indexOfChild(mDragView);
- int maxPageNo = getChildCount() - 1;
- for (int i = 0; i <= maxPageNo; i++) {
- View page = getPageAt(i);
- int pageX = (page.getLeft() + page.getMeasuredWidth() / 2);
- int d = Math.abs(dragX - pageX);
- if (d < minDistance) {
- minIndex = i;
- minDistance = d;
- }
- }
- return minIndex;
- }
- return -1;
- }
-
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
@@ -1220,11 +1067,7 @@
// Remember where the motion event started
mDownMotionX = mLastMotionX = ev.getX();
- mDownMotionY = mLastMotionY = ev.getY();
- mDownScrollX = getScrollX();
- float[] p = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
- mParentDownMotionX = p[0];
- mParentDownMotionY = p[1];
+ mDownMotionY = ev.getY();
mLastMotionXRemainder = 0;
mTotalMotionX = 0;
mActivePointerId = ev.getPointerId(0);
@@ -1257,82 +1100,6 @@
} else {
awakenScrollBars();
}
- } else if (mTouchState == TOUCH_STATE_REORDERING) {
- // Update the last motion position
- mLastMotionX = ev.getX();
- mLastMotionY = ev.getY();
-
- // Update the parent down so that our zoom animations take this new movement into
- // account
- float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
- mParentDownMotionX = pt[0];
- mParentDownMotionY = pt[1];
- updateDragViewTranslationDuringDrag();
-
- // Find the closest page to the touch point
- final int dragViewIndex = indexOfChild(mDragView);
-
- if (DEBUG) Log.d(TAG, "mLastMotionX: " + mLastMotionX);
- if (DEBUG) Log.d(TAG, "mLastMotionY: " + mLastMotionY);
- if (DEBUG) Log.d(TAG, "mParentDownMotionX: " + mParentDownMotionX);
- if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
-
- final int pageUnderPointIndex = getNearestHoverOverPageIndex();
- // Do not allow any page to be moved to 0th position.
- if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) {
- if (0 <= pageUnderPointIndex && pageUnderPointIndex <= getPageCount() - 1 &&
- pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
- mSidePageHoverIndex = pageUnderPointIndex;
- mSidePageHoverRunnable = new Runnable() {
- @Override
- public void run() {
- // Setup the scroll to the correct page before we swap the views
- snapToPage(pageUnderPointIndex);
-
- // For each of the pages between the paged view and the drag view,
- // animate them from the previous position to the new position in
- // the layout (as a result of the drag view moving in the layout)
- int shiftDelta = (dragViewIndex < pageUnderPointIndex) ? -1 : 1;
- int lowerIndex = (dragViewIndex < pageUnderPointIndex) ?
- dragViewIndex + 1 : pageUnderPointIndex;
- int upperIndex = (dragViewIndex > pageUnderPointIndex) ?
- dragViewIndex - 1 : pageUnderPointIndex;
- for (int i = lowerIndex; i <= upperIndex; ++i) {
- View v = getChildAt(i);
- // dragViewIndex < pageUnderPointIndex, so after we remove the
- // drag view all subsequent views to pageUnderPointIndex will
- // shift down.
- int oldX = getChildOffset(i);
- int newX = getChildOffset(i + shiftDelta);
-
- // Animate the view translation from its old position to its new
- // position
- ObjectAnimator anim = (ObjectAnimator) v.getTag();
- if (anim != null) {
- anim.cancel();
- }
-
- v.setTranslationX(oldX - newX);
- anim = LauncherAnimUtils.ofFloat(v, View.TRANSLATION_X, 0);
- anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
- anim.start();
- v.setTag(anim);
- }
-
- removeView(mDragView);
- addView(mDragView, pageUnderPointIndex);
- mSidePageHoverIndex = -1;
- if (mPageIndicator != null) {
- mPageIndicator.setActiveMarker(getNextPage());
- }
- }
- };
- postDelayed(mSidePageHoverRunnable, REORDERING_SIDE_PAGE_HOVER_TIMEOUT);
- }
- } else {
- removeCallbacks(mSidePageHoverRunnable);
- mSidePageHoverIndex = -1;
- }
} else {
determineScrollingStart(ev);
}
@@ -1352,9 +1119,7 @@
SIGNIFICANT_MOVE_THRESHOLD;
mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
-
- boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
- shouldFlingForVelocity(velocityX);
+ boolean isFling = mTotalMotionX > mTouchSlop && shouldFlingForVelocity(velocityX);
if (!mFreeScroll) {
// In the case that the page is moved far to one direction and then is flung
@@ -1435,25 +1200,8 @@
} else {
snapToDestination();
}
- } else if (mTouchState == TOUCH_STATE_REORDERING) {
- // Update the last motion position
- mLastMotionX = ev.getX();
- mLastMotionY = ev.getY();
-
- // Update the parent down so that our zoom animations take this new movement into
- // account
- float[] pt = mapPointFromViewToParent(this, mLastMotionX, mLastMotionY);
- mParentDownMotionX = pt[0];
- mParentDownMotionY = pt[1];
- updateDragViewTranslationDuringDrag();
- } else {
- if (!mCancelTap) {
- onUnhandledTap(ev);
- }
}
- // Remove the callback to wait for the side page hover timeout
- removeCallbacks(mSidePageHoverRunnable);
// End any intermediate reordering states
resetTouchState();
break;
@@ -1481,8 +1229,6 @@
private void resetTouchState() {
releaseVelocityTracker();
- endReordering();
- mCancelTap = false;
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
}
@@ -1496,10 +1242,6 @@
protected void onScrollInteractionEnd() {
}
- protected void onUnhandledTap(MotionEvent ev) {
- Launcher.getLauncher(getContext()).onClick(this);
- }
-
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -1556,7 +1298,6 @@
// TODO: Make this decision more intelligent.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionX = mDownMotionX = ev.getX(newPointerIndex);
- mLastMotionY = ev.getY(newPointerIndex);
mLastMotionXRemainder = 0;
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
@@ -1622,7 +1363,7 @@
return (float) Math.sin(f);
}
- protected void snapToPageWithVelocity(int whichPage, int velocity) {
+ protected boolean snapToPageWithVelocity(int whichPage, int velocity) {
whichPage = validateNewPage(whichPage);
int halfScreenSize = getMeasuredWidth() / 2;
@@ -1633,8 +1374,7 @@
if (Math.abs(velocity) < mMinFlingVelocity) {
// If the velocity is low enough, then treat this more as an automatic page advance
// as opposed to an apparent physical response to flinging
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
- return;
+ return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
}
// Here we compute a "distance" that will be used in the computation of the overall
@@ -1653,39 +1393,39 @@
// interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- snapToPage(whichPage, delta, duration);
+ return snapToPage(whichPage, delta, duration);
}
- public void snapToPage(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ public boolean snapToPage(int whichPage) {
+ return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
}
- public void snapToPageImmediately(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
+ public boolean snapToPageImmediately(int whichPage) {
+ return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
}
- public void snapToPage(int whichPage, int duration) {
- snapToPage(whichPage, duration, false, null);
+ public boolean snapToPage(int whichPage, int duration) {
+ return snapToPage(whichPage, duration, false, null);
}
- protected void snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
- snapToPage(whichPage, duration, false, interpolator);
+ protected boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
+ return snapToPage(whichPage, duration, false, interpolator);
}
- protected void snapToPage(int whichPage, int duration, boolean immediate,
+ protected boolean snapToPage(int whichPage, int duration, boolean immediate,
TimeInterpolator interpolator) {
whichPage = validateNewPage(whichPage);
int newX = getScrollForPage(whichPage);
final int delta = newX - getUnboundedScrollX();
- snapToPage(whichPage, delta, duration, immediate, interpolator);
+ return snapToPage(whichPage, delta, duration, immediate, interpolator);
}
- protected void snapToPage(int whichPage, int delta, int duration) {
- snapToPage(whichPage, delta, duration, false, null);
+ protected boolean snapToPage(int whichPage, int delta, int duration) {
+ return snapToPage(whichPage, delta, duration, false, null);
}
- protected void snapToPage(int whichPage, int delta, int duration, boolean immediate,
+ protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
TimeInterpolator interpolator) {
whichPage = validateNewPage(whichPage);
@@ -1723,6 +1463,7 @@
}
invalidate();
+ return Math.abs(delta) > 0;
}
public void scrollLeft() {
@@ -1734,136 +1475,10 @@
}
@Override
- public boolean performLongClick() {
- mCancelTap = true;
- return super.performLongClick();
- }
-
- public static class SavedState extends BaseSavedState {
- int currentPage = -1;
-
- SavedState(Parcelable superState) {
- super(superState);
- }
-
- @Thunk SavedState(Parcel in) {
- super(in);
- currentPage = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- super.writeToParcel(out, flags);
- out.writeInt(currentPage);
- }
-
- public static final Parcelable.Creator<SavedState> CREATOR =
- new Parcelable.Creator<SavedState>() {
- public SavedState createFromParcel(Parcel in) {
- return new SavedState(in);
- }
-
- public SavedState[] newArray(int size) {
- return new SavedState[size];
- }
- };
- }
-
- // Animate the drag view back to the original position
- private void animateDragViewToOriginalPosition() {
- if (mDragView != null) {
- Animator anim = LauncherAnimUtils.ofPropertyValuesHolder(mDragView,
- new PropertyListBuilder()
- .scale(1)
- .translationX(0)
- .translationY(0)
- .build())
- .setDuration(REORDERING_DROP_REPOSITION_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onPostReorderingAnimationCompleted();
- }
- });
- anim.start();
- }
- }
-
- public void onStartReordering() {
- // Set the touch state to reordering (allows snapping to pages, dragging a child, etc.)
- mTouchState = TOUCH_STATE_REORDERING;
- mIsReordering = true;
-
- // We must invalidate to trigger a redraw to update the layers such that the drag view
- // is always drawn on top
- invalidate();
- }
-
- @Thunk void onPostReorderingAnimationCompleted() {
- // Trigger the callback when reordering has settled
- --mPostReorderingPreZoomInRemainingAnimationCount;
- if (mPostReorderingPreZoomInRunnable != null &&
- mPostReorderingPreZoomInRemainingAnimationCount == 0) {
- mPostReorderingPreZoomInRunnable.run();
- mPostReorderingPreZoomInRunnable = null;
- }
- }
-
- public void onEndReordering() {
- mIsReordering = false;
- }
-
- public boolean startReordering(View v) {
- int dragViewIndex = indexOfChild(v);
-
- // Do not allow the first page to be moved around
- if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false;
-
- // Check if we are within the reordering range
- if (0 <= dragViewIndex && dragViewIndex <= getPageCount() - 1) {
- // Find the drag view under the pointer
- mDragView = getChildAt(dragViewIndex);
- mDragView.animate().scaleX(1.15f).scaleY(1.15f).setDuration(100).start();
- mDragViewBaselineLeft = mDragView.getLeft();
- mReorderingStarted = true;
-
- snapToPage(getPageNearestToCenterOfScreen());
- disableFreeScroll();
- onStartReordering();
- return true;
- }
- return false;
- }
-
- boolean isReordering(boolean testTouchState) {
- boolean state = mIsReordering;
- if (testTouchState) {
- state &= (mTouchState == TOUCH_STATE_REORDERING);
- }
- return state;
- }
- void endReordering() {
- // For simplicity, we call endReordering sometimes even if reordering was never started.
- // In that case, we don't want to do anything.
- if (!mReorderingStarted) return;
- mReorderingStarted = false;
-
- mPostReorderingPreZoomInRunnable = new Runnable() {
- public void run() {
- // If we haven't flung-to-delete the current child,
- // then we just animate the drag view back into position
- onEndReordering();
-
- enableFreeScroll();
- }
- };
-
- mPostReorderingPreZoomInRemainingAnimationCount =
- NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT;
- // Snap to the current page
- snapToPage(indexOfChild(mDragView), 0);
- // Animate the drag view back to the front position
- animateDragViewToOriginalPosition();
+ public CharSequence getAccessibilityClassName() {
+ // Some accessibility services have special logic for ScrollView. Since we provide same
+ // accessibility info as ScrollView, inform the service to handle use the same way.
+ return ScrollView.class.getName();
}
/* Accessibility */
@@ -1878,7 +1493,6 @@
if (getCurrentPage() > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
- info.setClassName(getClass().getName());
// Accessibility-wise, PagedView doesn't support long click, so disabling it.
// Besides disabling the accessibility long-click, this also prevents this view from getting
@@ -1927,13 +1541,30 @@
return getCurrentPageDescription();
}
+ protected boolean canAnnouncePageDescription() {
+ return true;
+ }
+
protected String getCurrentPageDescription() {
return getContext().getString(R.string.default_scroll_format,
getNextPage() + 1, getChildCount());
}
+ protected float getDownMotionX() {
+ return mDownMotionX;
+ }
+
+ protected float getDownMotionY() {
+ return mDownMotionY;
+ }
+
@Override
public boolean onHoverEvent(android.view.MotionEvent event) {
return true;
}
+
+ protected interface ComputePageScrollsLogic {
+
+ boolean shouldIncludeView(View view);
+ }
}
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java
similarity index 61%
rename from src/com/android/launcher3/UninstallDropTarget.java
rename to src/com/android/launcher3/SecondaryDropTarget.java
index 68a441a..024b4eb 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/SecondaryDropTarget.java
@@ -1,8 +1,19 @@
package com.android.launcher3;
+import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_RECONFIGURABLE;
+
import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_MASK;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_SYSTEM_NO;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.RECONFIGURE;
+import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.UNINSTALL;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.SETTINGS_BUTTON;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.UNINSTALL_TARGET;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -20,27 +31,33 @@
import android.widget.Toast;
import com.android.launcher3.Launcher.OnResumeCallback;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.Themes;
import java.net.URISyntaxException;
-public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmListener {
+/**
+ * Drop target which provides a secondary option for an item.
+ * For app targets: shows as uninstall
+ * For configurable widgets: shows as setup
+ */
+public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmListener {
- private static final String TAG = "UninstallDropTarget";
+ private static final String TAG = "SecondaryDropTarget";
private static final long CACHE_EXPIRE_TIMEOUT = 5000;
private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
private final Alarm mCacheExpireAlarm;
- public UninstallDropTarget(Context context, AttributeSet attrs) {
+ private int mCurrentAccessibilityAction = -1;
+ public SecondaryDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
+ public SecondaryDropTarget(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mCacheExpireAlarm = new Alarm();
@@ -50,13 +67,24 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- setupUi();
+ setupUi(UNINSTALL);
}
- protected void setupUi() {
- // Get the hover color
- mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
- setDrawable(R.drawable.ic_uninstall_shadow);
+ private void setupUi(int action) {
+ if (action == mCurrentAccessibilityAction) {
+ return;
+ }
+ mCurrentAccessibilityAction = action;
+
+ if (action == UNINSTALL) {
+ mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
+ setDrawable(R.drawable.ic_uninstall_shadow);
+ updateText(R.string.uninstall_drop_target_label);
+ } else {
+ mHoverColor = Themes.getColorAccent(getContext());
+ setDrawable(R.drawable.ic_setup_shadow);
+ updateText(R.string.gadget_setup_text);
+ }
}
@Override
@@ -66,11 +94,30 @@
@Override
public int getAccessibilityAction() {
- return LauncherAccessibilityDelegate.UNINSTALL;
+ return mCurrentAccessibilityAction;
+ }
+
+ @Override
+ public int getControlTypeForLogging() {
+ return mCurrentAccessibilityAction == UNINSTALL ? UNINSTALL_TARGET : SETTINGS_BUTTON;
}
@Override
protected boolean supportsDrop(ItemInfo info) {
+ return supportsAccessibilityDrop(info, getViewUnderDrag(info));
+ }
+
+ @Override
+ public boolean supportsAccessibilityDrop(ItemInfo info, View view) {
+ if (view instanceof AppWidgetHostView) {
+ if (getReconfigurableWidgetId(view) != INVALID_APPWIDGET_ID) {
+ setupUi(RECONFIGURE);
+ return true;
+ }
+ return false;
+ }
+
+ setupUi(UNINSTALL);
Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user);
if (uninstallDisabled == null) {
UserManager userManager =
@@ -126,7 +173,7 @@
@Override
public void completeDrop(final DragObject d) {
- ComponentName target = performDropAction(d.dragInfo);
+ ComponentName target = performDropAction(getViewUnderDrag(d.dragInfo), d.dragInfo);
if (d.dragSource instanceof DeferredOnComplete) {
DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
if (target != null) {
@@ -138,11 +185,48 @@
}
}
+ private View getViewUnderDrag(ItemInfo info) {
+ if (info instanceof LauncherAppWidgetInfo && info.container == CONTAINER_DESKTOP &&
+ mLauncher.getWorkspace().getDragInfo() != null) {
+ return mLauncher.getWorkspace().getDragInfo().cell;
+ }
+ return null;
+ }
+
+ /**
+ * Verifies that the view is an reconfigurable widget and returns the corresponding widget Id,
+ * otherwise return {@code INVALID_APPWIDGET_ID}
+ */
+ private int getReconfigurableWidgetId(View view) {
+ if (!(view instanceof AppWidgetHostView)) {
+ return INVALID_APPWIDGET_ID;
+ }
+ AppWidgetHostView hostView = (AppWidgetHostView) view;
+ AppWidgetProviderInfo widgetInfo = hostView.getAppWidgetInfo();
+ if (widgetInfo == null || widgetInfo.configure == null) {
+ return INVALID_APPWIDGET_ID;
+ }
+ if ( (LauncherAppWidgetProviderInfo.fromProviderInfo(getContext(), widgetInfo)
+ .getWidgetFeatures() & WIDGET_FEATURE_RECONFIGURABLE) == 0) {
+ return INVALID_APPWIDGET_ID;
+ }
+ return hostView.getAppWidgetId();
+ }
+
/**
* Performs the drop action and returns the target component for the dragObject or null if
* the action was not performed.
*/
- protected ComponentName performDropAction(ItemInfo info) {
+ protected ComponentName performDropAction(View view, ItemInfo info) {
+ if (mCurrentAccessibilityAction == RECONFIGURE) {
+ int widgetId = getReconfigurableWidgetId(view);
+ if (widgetId != INVALID_APPWIDGET_ID) {
+ mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId, -1);
+ }
+ return null;
+ }
+ // else: mCurrentAccessibilityAction == UNINSTALL
+
ComponentName cn = getUninstallTarget(info);
if (cn == null) {
// System applications cannot be installed. For now, show a toast explaining that.
@@ -164,7 +248,7 @@
@Override
public void onAccessibilityDrop(View view, ItemInfo item) {
- performDropAction(item);
+ performDropAction(view, item);
}
/**
@@ -203,7 +287,7 @@
.getApplicationInfo(mPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES,
mDragObject.dragInfo.user) == null) {
mDragObject.dragSource = mOriginal;
- mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, true);
+ mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, true);
} else {
sendFailure();
}
@@ -212,7 +296,7 @@
public void sendFailure() {
mDragObject.dragSource = mOriginal;
mDragObject.cancelled = true;
- mOriginal.onDropCompleted(UninstallDropTarget.this, mDragObject, false);
+ mOriginal.onDropCompleted(SecondaryDropTarget.this, mDragObject, false);
}
}
}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index edb7ff5..b0da6b9 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -67,11 +67,9 @@
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- if (Process.myUserHandle().equals(user)) {
- if (TextUtils.isEmpty(info.getAppPackageName()) ||
- info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
- return;
- }
+ if (TextUtils.isEmpty(info.getAppPackageName()) ||
+ info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
+ return;
}
queueAppIconAddition(context, info.getAppPackageName(), user);
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index d40ac8f..c9bd32b 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@@ -26,17 +27,26 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.Adapter;
+import android.widget.ListView;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.notification.NotificationListener;
+import com.android.launcher3.util.ListViewHighlighter;
import com.android.launcher3.util.SettingsObserver;
import com.android.launcher3.views.ButtonPreference;
+import java.util.Objects;
+
/**
* Settings activity for Launcher. Currently implements the following setting: Allow rotation
*/
@@ -48,6 +58,10 @@
/** Hidden field Settings.Secure.ENABLED_NOTIFICATION_LISTENERS */
private static final String NOTIFICATION_ENABLED_LISTENERS = "enabled_notification_listeners";
+ private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
+ private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
+ private static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -55,43 +69,37 @@
if (savedInstanceState == null) {
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
- .replace(android.R.id.content, new LauncherSettingsFragment())
+ .replace(android.R.id.content, getNewFragment())
.commit();
}
}
+ protected PreferenceFragment getNewFragment() {
+ return new LauncherSettingsFragment();
+ }
+
/**
* This fragment shows the launcher preferences.
*/
public static class LauncherSettingsFragment extends PreferenceFragment {
- private SystemDisplayRotationLockObserver mRotationLockObserver;
private IconBadgingObserver mIconBadgingObserver;
+ private String mPreferenceKey;
+ private boolean mPreferenceHighlighted = false;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (savedInstanceState != null) {
+ mPreferenceHighlighted = savedInstanceState.getBoolean(SAVE_HIGHLIGHTED_KEY);
+ }
+
getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
addPreferencesFromResource(R.xml.launcher_preferences);
ContentResolver resolver = getActivity().getContentResolver();
- // Setup allow rotation preference
- Preference rotationPref = findPreference(Utilities.ALLOW_ROTATION_PREFERENCE_KEY);
- if (getResources().getBoolean(R.bool.allow_rotation)) {
- // Launcher supports rotation by default. No need to show this setting.
- getPreferenceScreen().removePreference(rotationPref);
- } else {
- mRotationLockObserver = new SystemDisplayRotationLockObserver(rotationPref, resolver);
-
- // Register a content observer to listen for system setting changes while
- // this UI is active.
- mRotationLockObserver.register(Settings.System.ACCELEROMETER_ROTATION);
-
- // Initialize the UI once
- rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
- }
-
ButtonPreference iconBadgingPref =
(ButtonPreference) findPreference(ICON_BADGING_PREFERENCE_KEY);
if (!Utilities.ATLEAST_OREO) {
@@ -118,38 +126,81 @@
}
@Override
- public void onDestroy() {
- if (mRotationLockObserver != null) {
- mRotationLockObserver.unregister();
- mRotationLockObserver = null;
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVE_HIGHLIGHTED_KEY, mPreferenceHighlighted);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ Intent intent = getActivity().getIntent();
+ mPreferenceKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+ if (isAdded() && !mPreferenceHighlighted && !TextUtils.isEmpty(mPreferenceKey)) {
+ getView().postDelayed(this::highlightPreference, DELAY_HIGHLIGHT_DURATION_MILLIS);
}
+ }
+
+ private void highlightPreference() {
+ Preference pref = findPreference(mPreferenceKey);
+ if (pref == null || getPreferenceScreen() == null) {
+ return;
+ }
+ PreferenceScreen screen = getPreferenceScreen();
+ if (Utilities.ATLEAST_OREO) {
+ screen = selectPreferenceRecursive(pref, screen);
+ }
+ if (screen == null) {
+ return;
+ }
+
+ View root = screen.getDialog() != null
+ ? screen.getDialog().getWindow().getDecorView() : getView();
+ ListView list = root.findViewById(android.R.id.list);
+ if (list == null || list.getAdapter() == null) {
+ return;
+ }
+ Adapter adapter = list.getAdapter();
+
+ // Find the position
+ int position = -1;
+ for (int i = adapter.getCount() - 1; i >= 0; i--) {
+ if (pref == adapter.getItem(i)) {
+ position = i;
+ break;
+ }
+ }
+ new ListViewHighlighter(list, position);
+ mPreferenceHighlighted = true;
+ }
+
+ @Override
+ public void onDestroy() {
if (mIconBadgingObserver != null) {
mIconBadgingObserver.unregister();
mIconBadgingObserver = null;
}
super.onDestroy();
}
- }
- /**
- * Content observer which listens for system auto-rotate setting changes, and enables/disables
- * the launcher rotation setting accordingly.
- */
- private static class SystemDisplayRotationLockObserver extends SettingsObserver.System {
+ @TargetApi(Build.VERSION_CODES.O)
+ private PreferenceScreen selectPreferenceRecursive(
+ Preference pref, PreferenceScreen topParent) {
+ if (!(pref.getParent() instanceof PreferenceScreen)) {
+ return null;
+ }
- private final Preference mRotationPref;
-
- public SystemDisplayRotationLockObserver(
- Preference rotationPref, ContentResolver resolver) {
- super(resolver);
- mRotationPref = rotationPref;
- }
-
- @Override
- public void onSettingChanged(boolean enabled) {
- mRotationPref.setEnabled(enabled);
- mRotationPref.setSummary(enabled
- ? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc);
+ PreferenceScreen parent = (PreferenceScreen) pref.getParent();
+ if (Objects.equals(parent.getKey(), topParent.getKey())) {
+ return parent;
+ } else if (selectPreferenceRecursive(parent, topParent) != null) {
+ ((PreferenceScreen) parent.getParent())
+ .onItemClick(null, null, parent.getOrder(), 0);
+ return parent;
+ } else {
+ return null;
+ }
}
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 1a63326..baf6d87 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -16,9 +16,12 @@
package com.android.launcher3;
+import static android.view.MotionEvent.ACTION_DOWN;
+
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Rect;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -174,6 +177,15 @@
}
@Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == ACTION_DOWN && getAlpha() == 0) {
+ // Dont let children handle touch, if we are not visible.
+ return true;
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
public boolean shouldDelayChildPressedState() {
return false;
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index ec608ca..8588c7a 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -79,7 +79,7 @@
* A message to display when the user tries to start a disabled shortcut.
* This is currently only used for deep shortcuts.
*/
- CharSequence disabledMessage;
+ public CharSequence disabledMessage;
public int status;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 158c540..cabccbf 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -31,6 +31,7 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -124,29 +125,10 @@
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
- public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
-
public static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
- public static boolean isAllowRotationPrefEnabled(Context context) {
- return getPrefs(context).getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
- getAllowRotationDefaultValue(context));
- }
-
- public static boolean getAllowRotationDefaultValue(Context context) {
- if (ATLEAST_NOUGAT) {
- // If the device was scaled, used the original dimensions to determine if rotation
- // is allowed of not.
- Resources res = context.getResources();
- int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
- * res.getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEVICE_STABLE;
- return originalSmallestWidth >= 600;
- }
- return false;
- }
-
/**
* Given a coordinate relative to the descendant, find the coordinate in a parent view's
* coordinates.
@@ -232,21 +214,45 @@
return new int[] {sLoc1[0] - sLoc0[0], sLoc1[1] - sLoc0[1]};
}
+ public static void scaleRectFAboutCenter(RectF r, float scale) {
+ if (scale != 1.0f) {
+ float cx = r.centerX();
+ float cy = r.centerY();
+ r.offset(-cx, -cy);
+ r.left = r.left * scale;
+ r.top = r.top * scale ;
+ r.right = r.right * scale;
+ r.bottom = r.bottom * scale;
+ r.offset(cx, cy);
+ }
+ }
+
public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) {
int cx = r.centerX();
int cy = r.centerY();
r.offset(-cx, -cy);
+ scaleRect(r, scale);
+ r.offset(cx, cy);
+ }
+ }
+ public static void scaleRect(Rect r, float scale) {
+ if (scale != 1.0f) {
r.left = (int) (r.left * scale + 0.5f);
r.top = (int) (r.top * scale + 0.5f);
r.right = (int) (r.right * scale + 0.5f);
r.bottom = (int) (r.bottom * scale + 0.5f);
-
- r.offset(cx, cy);
}
}
+ public static void insetRect(Rect r, Rect insets) {
+ r.left = Math.min(r.right, r.left + insets.left);
+ r.top = Math.min(r.bottom, r.top + insets.top);
+ r.right = Math.max(r.left, r.right - insets.right);
+ r.bottom = Math.max(r.top, r.bottom - insets.bottom);
+ }
+
public static float shrinkRect(Rect r, float scaleX, float scaleY) {
float scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
if (scale < 1.0f) {
@@ -261,6 +267,10 @@
return scale;
}
+ public static float mapRange(float value, float min, float max) {
+ return min + (value * (max - min));
+ }
+
public static boolean isSystemApp(Context context, Intent intent) {
PackageManager pm = context.getPackageManager();
ComponentName cn = intent.getComponent();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8fb0e1c..1e2e3b1 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,13 +52,13 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
import android.widget.Toast;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
-import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
@@ -75,8 +75,13 @@
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.graphics.ViewScrim;
+import com.android.launcher3.graphics.WorkspaceAndHotseatScrim;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemLongClickListener;
+import com.android.launcher3.touch.WorkspaceTouchListener;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -100,10 +105,9 @@
* Each page contains a number of icons, folders or widgets the user can
* interact with. A workspace is meant to be used with a fixed width only.
*/
-public class Workspace extends PagedView
+public class Workspace extends PagedView<WorkspacePageIndicator>
implements DropTarget, DragSource, View.OnTouchListener,
- DragController.DragListener, ViewGroup.OnHierarchyChangeListener,
- Insettable, LauncherStateManager.StateHandler {
+ DragController.DragListener, Insettable, LauncherStateManager.StateHandler {
private static final String TAG = "Launcher.Workspace";
/** The value that {@link #mTransitionProgress} must be greater than for
@@ -235,9 +239,9 @@
boolean mStartedSendingScrollEvents;
float mLastOverlayScroll = 0;
boolean mOverlayShown = false;
+ private Runnable mOnOverlayHiddenCallback;
private boolean mForceDrawAdjacentPages = false;
- private boolean mPageRearrangeEnabled = false;
// Total over scrollX in the overlay direction.
private float mOverlayTranslation;
@@ -245,8 +249,6 @@
// Handles workspace state transitions
private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
- private AccessibilityDelegate mPagesAccessibilityDelegate;
-
/**
* Used to inflate the Workspace from XML.
*
@@ -273,13 +275,15 @@
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
- setOnHierarchyChangeListener(this);
setHapticFeedbackEnabled(false);
-
initWorkspace();
// Disable multitouch across the workspace/all apps/customize tray
setMotionEventSplittingEnabled(true);
+
+ // Attach a scrim
+ new WorkspaceAndHotseatScrim(this).attach();
+ setOnTouchListener(new WorkspaceTouchListener(mLauncher, this));
}
@Override
@@ -345,6 +349,11 @@
}
}
+ public float getWallpaperOffsetForCenterPage() {
+ int pageScroll = getScrollForPage(getPageNearestToCenterOfScreen());
+ return mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
+ }
+
public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
Rect r = new Rect();
cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
@@ -421,6 +430,7 @@
}
updateChildrenLayersEnabled();
+ mDragInfo = null;
mOutlineProvider = null;
mDragSourceInternal = null;
}
@@ -438,12 +448,6 @@
setWallpaperDimension();
}
- @Override
- public void initParentViews(View parent) {
- super.initParentViews(parent);
- mPageIndicator.setAccessibilityDelegate(UiFactory.newPageIndicatorAccessibilityDelegate());
- }
-
private void setupLayoutTransition() {
// We want to show layout transitions when pages are deleted, to close the gap.
mLayoutTransition = new LayoutTransition();
@@ -462,15 +466,14 @@
}
@Override
- public void onChildViewAdded(View parent, View child) {
+ public void onViewAdded(View child) {
if (!(child instanceof CellLayout)) {
throw new IllegalArgumentException("A Workspace can only have CellLayout children.");
}
CellLayout cl = ((CellLayout) child);
cl.setOnInterceptTouchListener(this);
- cl.setClickable(true);
cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- super.onChildViewAdded(parent, child);
+ super.onViewAdded(child);
}
boolean isTouchActive() {
@@ -548,10 +551,6 @@
// created CellLayout.
CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
- newScreen.setOnLongClickListener(mLongClickListener);
- newScreen.setOnClickListener(mLauncher);
- newScreen.setSoundEffectsEnabled(false);
-
int paddingLeftRight = mLauncher.getDeviceProfile().cellLayoutPaddingLeftRightPx;
int paddingBottom = mLauncher.getDeviceProfile().cellLayoutBottomPaddingPx;
newScreen.setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
@@ -929,10 +928,8 @@
Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
}
- if (!(child instanceof Folder)) {
- child.setHapticFeedbackEnabled(false);
- child.setOnLongClickListener(mLongClickListener);
- }
+ child.setHapticFeedbackEnabled(false);
+ child.setOnLongClickListener(ItemLongClickListener.INSTANCE_WORKSPACE);
if (child instanceof DropTarget) {
mDragController.addDropTarget((DropTarget) child);
}
@@ -1151,29 +1148,37 @@
* The overlay scroll is being controlled locally, just update our overlay effect
*/
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);
}
mOverlayShown = true;
+ // Not announcing the overlay page for accessibility since it announces itself.
} else if (Float.compare(scroll, 0f) == 0) {
if (mOverlayShown) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+ } else if (Float.compare(mOverlayTranslation, 0f) != 0) {
+ // When arriving to 0 overscroll from non-zero overscroll, announce page for
+ // accessibility since default announcements were disabled while in overscroll
+ // state.
+ // Not doing this if mOverlayShown because in that case the accessibility service
+ // will announce the launcher window description upon regaining focus after
+ // switching from the overlay screen.
+ announcePageForAccessibility();
}
mOverlayShown = false;
+ tryRunOverlayCallback();
}
+
float offset = 0f;
- float slip = 0f;
scroll = Math.max(scroll - offset, 0);
scroll = Math.min(1, scroll / (1 - offset));
float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll);
float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
- transX *= 1 - slip;
if (mIsRtl) {
transX = -transX;
@@ -1187,6 +1192,59 @@
mLauncher.getDragLayer().setAlpha(alpha);
}
+ /**
+ * @return false if the callback is still pending
+ */
+ private boolean tryRunOverlayCallback() {
+ if (mOnOverlayHiddenCallback == null) {
+ // Return true as no callback is pending. This is used by OnWindowFocusChangeListener
+ // to remove itself if multiple focus handles were added.
+ return true;
+ }
+ if (mOverlayShown || !hasWindowFocus()) {
+ return false;
+ }
+
+ mOnOverlayHiddenCallback.run();
+ mOnOverlayHiddenCallback = null;
+ return true;
+ }
+
+ /**
+ * Runs the given callback when the minus one overlay is hidden. Specifically, it is run
+ * when launcher's window has focus and the overlay is no longer being shown. If a callback
+ * is already present, the new callback will chain off it so both are run.
+ *
+ * @return Whether the callback was deferred.
+ */
+ public boolean runOnOverlayHidden(Runnable callback) {
+ if (mOnOverlayHiddenCallback == null) {
+ mOnOverlayHiddenCallback = callback;
+ } else {
+ // Chain the new callback onto the previous callback(s).
+ Runnable oldCallback = mOnOverlayHiddenCallback;
+ mOnOverlayHiddenCallback = () -> {
+ oldCallback.run();
+ callback.run();
+ };
+ }
+ if (!tryRunOverlayCallback()) {
+ ViewTreeObserver observer = getViewTreeObserver();
+ if (observer != null && observer.isAlive()) {
+ observer.addOnWindowFocusChangeListener(
+ new ViewTreeObserver.OnWindowFocusChangeListener() {
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ if (tryRunOverlayCallback() && observer.isAlive()) {
+ observer.removeOnWindowFocusChangeListener(this);
+ }
+ }});
+ }
+ return true;
+ }
+ return false;
+ }
+
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
@@ -1318,8 +1376,7 @@
}
private void updateChildrenLayersEnabled() {
- boolean enableChildrenLayers =
- isPageRearrangeEnabled() || mIsSwitchingState || isPageInTransition();
+ boolean enableChildrenLayers = mIsSwitchingState || isPageInTransition();
if (enableChildrenLayers != mChildrenLayersEnabled) {
mChildrenLayersEnabled = enableChildrenLayers;
@@ -1403,40 +1460,6 @@
mOutlineProvider = outlineProvider;
}
- public void onStartReordering() {
- super.onStartReordering();
- // Reordering handles its own animations, disable the automatic ones.
- disableLayoutTransitions();
- }
-
- public void onEndReordering() {
- super.onEndReordering();
-
- if (mLauncher.isWorkspaceLoading()) {
- // Invalid and dangerous operation if workspace is loading
- return;
- }
-
- ArrayList<Long> prevScreenOrder = (ArrayList<Long>) mScreenOrder.clone();
- mScreenOrder.clear();
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- CellLayout cl = ((CellLayout) getChildAt(i));
- mScreenOrder.add(getIdForScreen(cl));
- }
-
- for (int i = 0; i < prevScreenOrder.size(); i++) {
- if (mScreenOrder.get(i) != prevScreenOrder.get(i)) {
- mLauncher.getUserEventDispatcher().logOverviewReorder();
- break;
- }
- }
- LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
-
- // Re-enable auto layout transitions for page deletion.
- enableLayoutTransitions();
- }
-
public void snapToPageFromOverView(int whichPage) {
snapToPage(whichPage, OVERVIEW_TRANSITION_MS, Interpolators.ZOOM_IN);
}
@@ -1496,47 +1519,17 @@
if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
int total = getPageCount();
for (int i = 0; i < total; i++) {
- updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i), i);
+ updateAccessibilityFlags(accessibilityFlag, (CellLayout) getPageAt(i));
}
setImportantForAccessibility(accessibilityFlag);
}
}
- private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page, int pageNo) {
- if (isPageRearrangeEnabled()) {
- page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- page.getShortcutsAndWidgets().setImportantForAccessibility(
- IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- page.setContentDescription(getPageDescription(pageNo));
-
- // No custom action for the first page.
- if (!FeatureFlags.QSB_ON_FIRST_SCREEN || pageNo > 0) {
- if (mPagesAccessibilityDelegate == null) {
- mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
- }
- page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
- }
- } else {
- page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
- page.setContentDescription(null);
- page.setAccessibilityDelegate(null);
- }
- }
-
- public void setPageRearrangeEnabled(boolean isEnabled) {
- if (mPageRearrangeEnabled != isEnabled) {
- mPageRearrangeEnabled = isEnabled;
- if (isEnabled) {
- enableFreeScroll();
- } else {
- disableFreeScroll();
- }
- }
- }
-
- public boolean isPageRearrangeEnabled() {
- return mPageRearrangeEnabled;
+ private void updateAccessibilityFlags(int accessibilityFlag, CellLayout page) {
+ page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+ page.getShortcutsAndWidgets().setImportantForAccessibility(accessibilityFlag);
+ page.setContentDescription(null);
+ page.setAccessibilityDelegate(null);
}
public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
@@ -1552,10 +1545,6 @@
protected void enableAccessibleDrag(boolean enable) {
super.enableAccessibleDrag(enable);
setEnableForLayout(mLauncher.getHotseat().getLayout(),enable);
-
- // We need to allow our individual children to become click handlers in this
- // case, so temporarily unset the click handlers.
- setOnClickListener(enable ? null : mLauncher);
}
});
}
@@ -1575,7 +1564,6 @@
new DragPreviewProvider(child), options);
}
-
public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
DragPreviewProvider previewProvider, DragOptions dragOptions) {
child.clearFocus();
@@ -1624,7 +1612,7 @@
if (popupContainer != null) {
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
}
}
@@ -2174,7 +2162,7 @@
}
// Invalidating the scrim will also force this CellLayout
// to be invalidated so that it is highlighted if necessary.
- mLauncher.getDragLayer().invalidateScrim();
+ ViewScrim.get(this).invalidate();
}
public CellLayout getCurrentDragOverlappingLayout() {
@@ -2661,10 +2649,16 @@
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- if (info.container == NO_ID && info instanceof AppInfo) {
+ if (info.container == NO_ID) {
// Came from all apps -- make a copy
- info = ((AppInfo) info).makeShortcut();
- d.dragInfo = info;
+ if (info instanceof AppInfo) {
+ info = ((AppInfo) info).makeShortcut();
+ d.dragInfo = info;
+ } else if (info instanceof ShortcutInfo) {
+ info = new ShortcutInfo((ShortcutInfo) info);
+ d.dragInfo = info;
+ }
+
}
view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
break;
@@ -2904,8 +2898,9 @@
+ "Workspace#onDropCompleted. Please file a bug. ");
}
}
- if (d.cancelled && mDragInfo != null && mDragInfo.cell != null) {
- mDragInfo.cell.setVisibility(VISIBLE);
+ View cell = getHomescreenIconByItemId(d.originalDragInfo.id);
+ if (d.cancelled && cell != null) {
+ cell.setVisibility(VISIBLE);
}
mDragInfo = null;
}
@@ -3265,8 +3260,7 @@
&& v instanceof FolderIcon) {
FolderBadgeInfo folderBadgeInfo = new FolderBadgeInfo();
for (ShortcutInfo si : ((FolderInfo) info).contents) {
- folderBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider()
- .getBadgeInfoForItem(si));
+ folderBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(si));
}
((FolderIcon) v).setBadgeInfo(folderBadgeInfo);
}
@@ -3363,8 +3357,10 @@
}
@Override
- protected String getPageIndicatorDescription() {
- return getResources().getString(R.string.all_apps_button_label);
+ protected boolean canAnnouncePageDescription() {
+ // Disable announcements while overscrolling potentially to overlay screen because if we end
+ // up on the overlay screen, it will take care of announcing itself.
+ return Float.compare(mOverlayTranslation, 0f) == 0;
}
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 21f5d67..420a7c4 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,80 +18,25 @@
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
+import static com.android.launcher3.LauncherState.DRAG_HANDLE_INDICATOR;
+import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
+import static com.android.launcher3.LauncherState.HOTSEAT_SEARCH_BOX;
+import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.util.Property;
import android.view.View;
import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
-
-/**
- * A convenience class to update a view's visibility state after an alpha animation.
- */
-class AlphaUpdateListener extends AnimatorListenerAdapter implements ValueAnimator.AnimatorUpdateListener {
- private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
-
- private View mView;
- private boolean mAccessibilityEnabled;
- private boolean mCanceled = false;
-
- public AlphaUpdateListener(View v, boolean accessibilityEnabled) {
- mView = v;
- mAccessibilityEnabled = accessibilityEnabled;
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator arg0) {
- updateVisibility(mView, mAccessibilityEnabled);
- }
-
- public static void updateVisibility(View view, boolean accessibilityEnabled) {
- // We want to avoid the extra layout pass by setting the views to GONE unless
- // accessibility is on, in which case not setting them to GONE causes a glitch.
- int invisibleState = accessibilityEnabled ? View.GONE : View.INVISIBLE;
- if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
- view.setVisibility(invisibleState);
- } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
- && view.getVisibility() != View.VISIBLE) {
- view.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator arg0) {
- if (mCanceled) return;
- updateVisibility(mView, mAccessibilityEnabled);
- }
-
- @Override
- public void onAnimationStart(Animator arg0) {
- // We want the views to be visible for animation, so fade-in/out is visible
- mView.setVisibility(View.VISIBLE);
- }
-}
+import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.graphics.ViewScrim;
/**
* Manages the animations between each of the workspace states.
*/
public class WorkspaceStateTransitionAnimation {
- public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter();
-
- public final int mWorkspaceScrimAlpha;
-
private final Launcher mLauncher;
private final Workspace mWorkspace;
@@ -100,8 +45,6 @@
public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
mLauncher = launcher;
mWorkspace = workspace;
- mWorkspaceScrimAlpha = launcher.getResources()
- .getInteger(R.integer.config_workspaceScrimAlpha);
}
public void setState(LauncherState toState) {
@@ -110,9 +53,7 @@
public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
AnimationConfig config) {
- AnimatedPropertySetter propertySetter =
- new AnimatedPropertySetter(config.duration, builder);
- setWorkspaceProperty(toState, propertySetter);
+ setWorkspaceProperty(toState, config.getProperSetter(builder));
}
public float getFinalScale() {
@@ -132,18 +73,32 @@
propertySetter);
}
- propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN);
+ propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_OUT);
propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
- scaleAndTranslation[1], Interpolators.ZOOM_IN);
+ scaleAndTranslation[1], Interpolators.ZOOM_OUT);
propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
- scaleAndTranslation[2], Interpolators.ZOOM_IN);
+ scaleAndTranslation[2], Interpolators.ZOOM_OUT);
- propertySetter.setViewAlpha(mLauncher.getHotseat(), state.getHoseatAlpha(mLauncher),
+ int elements = state.getVisibleElements(mLauncher);
+ float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
+ propertySetter.setViewAlpha(mLauncher.getHotseat().getLayout(), hotseatIconsAlpha,
+ pageAlphaProvider.interpolator);
+ propertySetter.setViewAlpha(mLauncher.getWorkspace().getPageIndicator(),
+ hotseatIconsAlpha, pageAlphaProvider.interpolator);
+
+ propertySetter.setViewAlpha(mLauncher.getHotseatSearchBox(),
+ (elements & HOTSEAT_SEARCH_BOX) != 0 ? 1 : 0,
+ pageAlphaProvider.interpolator);
+
+ propertySetter.setViewAlpha(mLauncher.getDragHandleIndicator(),
+ (elements & DRAG_HANDLE_INDICATOR) != 0 ? 1 : 0,
pageAlphaProvider.interpolator);
// Set scrim
- propertySetter.setInt(mLauncher.getDragLayer().getScrim(), DRAWABLE_ALPHA,
- state.hasScrim ? mWorkspaceScrimAlpha : 0, Interpolators.DEACCEL_1_5);
+ propertySetter.setFloat(ViewScrim.get(mWorkspace), ViewScrim.PROGRESS,
+ state.hasScrim ? 1 : 0, Interpolators.LINEAR);
+ propertySetter.setFloat(ViewScrim.get(mLauncher.getAppsView()), ViewScrim.PROGRESS,
+ state.hasAllAppsScrim ? 1 : 0, Interpolators.LINEAR);
}
public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
@@ -154,78 +109,11 @@
private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter) {
float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
- int drawableAlpha = Math.round(pageAlpha * (state.hasScrim ? 255 : 0));
+ int drawableAlpha = Math.round(pageAlpha * (state.hasWorkspacePageBackground ? 255 : 0));
propertySetter.setInt(cl.getScrimBackground(),
- DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_IN);
+ DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_OUT);
propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
pageAlpha, pageAlphaProvider.interpolator);
}
-
- public static class PropertySetter {
-
- public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
- view.setAlpha(alpha);
- AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext()));
- }
-
- public <T> void setFloat(T target, Property<T, Float> property, float value,
- TimeInterpolator interpolator) {
- property.set(target, value);
- }
-
- public <T> void setInt(T target, Property<T, Integer> property, int value,
- TimeInterpolator interpolator) {
- property.set(target, value);
- }
- }
-
- public static class AnimatedPropertySetter extends PropertySetter {
-
- private final long mDuration;
- private final AnimatorSetBuilder mStateAnimator;
-
- public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) {
- mDuration = duration;
- mStateAnimator = builder;
- }
-
- @Override
- public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
- if (view.getAlpha() == alpha) {
- return;
- }
- ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
- anim.addListener(new AlphaUpdateListener(
- view, isAccessibilityEnabled(view.getContext())));
- anim.setDuration(mDuration).setInterpolator(interpolator);
- mStateAnimator.play(anim);
- }
-
- @Override
- public <T> void setFloat(T target, Property<T, Float> property, float value,
- TimeInterpolator interpolator) {
- if (property.get(target) == value) {
- return;
- }
- Animator anim = ObjectAnimator.ofFloat(target, property, value);
- anim.setDuration(mDuration).setInterpolator(interpolator);
- mStateAnimator.play(anim);
- }
-
- @Override
- public <T> void setInt(T target, Property<T, Integer> property, int value,
- TimeInterpolator interpolator) {
- if (property.get(target) == value) {
- return;
- }
- Animator anim = ObjectAnimator.ofInt(target, property, value);
- anim.setDuration(mDuration).setInterpolator(interpolator);
- mStateAnimator.play(anim);
- }
-
- private TimeInterpolator getFadeInterpolator(float finalAlpha) {
- return finalAlpha == 0 ? Interpolators.DEACCEL_2 : null;
- }
- }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3b6fea9..4398f6e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -25,6 +25,7 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
@@ -47,8 +48,8 @@
private static final String TAG = "LauncherAccessibilityDelegate";
public static final int REMOVE = R.id.action_remove;
- public static final int INFO = R.id.action_info;
public static final int UNINSTALL = R.id.action_uninstall;
+ public static final int RECONFIGURE = R.id.action_reconfigure;
protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
protected static final int MOVE = R.id.action_move;
protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
@@ -77,10 +78,10 @@
mActions.put(REMOVE, new AccessibilityAction(REMOVE,
launcher.getText(R.string.remove_drop_target_label)));
- mActions.put(INFO, new AccessibilityAction(INFO,
- launcher.getText(R.string.app_info_drop_target_label)));
mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
launcher.getText(R.string.uninstall_drop_target_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,
@@ -110,7 +111,7 @@
}
for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
- if (target.supportsAccessibilityDrop(item)) {
+ if (target.supportsAccessibilityDrop(item, host)) {
info.addAction(mActions.get(target.getAccessibilityAction()));
}
}
@@ -222,7 +223,8 @@
return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
} else {
for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
- if (action == dropTarget.getAccessibilityAction()) {
+ if (dropTarget.supportsAccessibilityDrop(item, host) &&
+ action == dropTarget.getAccessibilityAction()) {
dropTarget.onAccessibilityDrop(host, item);
return true;
}
@@ -357,29 +359,14 @@
mDragInfo.dragType = DragType.WIDGET;
}
- CellLayout.CellInfo cellInfo = new CellLayout.CellInfo(item, info);
-
Rect pos = new Rect();
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
mLauncher.getDragController().prepareAccessibleDrag(pos.centerX(), pos.centerY());
-
- Folder folder = Folder.getOpen(mLauncher);
- if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(item)) {
- folder.close(true);
- folder = null;
- }
- }
-
mLauncher.getDragController().addDragListener(this);
DragOptions options = new DragOptions();
options.isAccessibleDrag = true;
- if (folder != null) {
- folder.startDrag(cellInfo.cell, options);
- } else {
- mLauncher.getWorkspace().startDrag(cellInfo, options);
- }
+ ItemLongClickListener.beginDrag(item, mLauncher, info, options);
}
@Override
diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
deleted file mode 100644
index f9eb2ed..0000000
--- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
+++ /dev/null
@@ -1,96 +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.accessibility;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.util.SparseArray;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.FeatureFlags;
-
-public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate {
-
- private static final int MOVE_BACKWARD = R.id.action_move_screen_backwards;
- private static final int MOVE_FORWARD = R.id.action_move_screen_forwards;
-
- private final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
- private final Workspace mWorkspace;
-
- public OverviewScreenAccessibilityDelegate(Workspace workspace) {
- mWorkspace = workspace;
-
- Context context = mWorkspace.getContext();
- boolean isRtl = Utilities.isRtl(context.getResources());
- mActions.put(MOVE_BACKWARD, new AccessibilityAction(MOVE_BACKWARD,
- context.getText(isRtl ? R.string.action_move_screen_right :
- R.string.action_move_screen_left)));
- mActions.put(MOVE_FORWARD, new AccessibilityAction(MOVE_FORWARD,
- context.getText(isRtl ? R.string.action_move_screen_left :
- R.string.action_move_screen_right)));
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- if (host != null) {
- if (action == AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS ) {
- int index = mWorkspace.indexOfChild(host);
- mWorkspace.setCurrentPage(index);
- } else if (action == MOVE_FORWARD) {
- movePage(mWorkspace.indexOfChild(host) + 1, host);
- return true;
- } else if (action == MOVE_BACKWARD) {
- movePage(mWorkspace.indexOfChild(host) - 1, host);
- return true;
- }
- }
-
- return super.performAccessibilityAction(host, action, args);
- }
-
- private void movePage(int finalIndex, View view) {
- mWorkspace.onStartReordering();
- mWorkspace.removeView(view);
- mWorkspace.addView(view, finalIndex);
- mWorkspace.onEndReordering();
- mWorkspace.announceForAccessibility(mWorkspace.getContext().getText(R.string.screen_moved));
-
- mWorkspace.updateAccessibilityFlags();
- view.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
-
- int index = mWorkspace.indexOfChild(host);
- if (index < mWorkspace.getChildCount() - 1) {
- info.addAction(mActions.get(MOVE_FORWARD));
- }
-
- int startIndex = FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0;
- if (index > startIndex) {
- info.addAction(mActions.get(MOVE_BACKWARD));
- }
- }
-}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index b5c9af2..ae41794 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -15,14 +15,17 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+
import android.content.Context;
-import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.Rect;
import android.os.Process;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-import android.support.v4.view.PagerAdapter;
-import android.support.v4.view.ViewPager;
+import android.support.annotation.StringRes;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
@@ -32,68 +35,56 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
-import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
-import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
-import com.android.launcher3.ClickShadowView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragController;
-import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.ColorScrim;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BottomUserEducationView;
-
-import java.util.List;
-import java.util.Set;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.SpringRelativeLayout;
/**
* The all apps view container.
*/
-public class AllAppsContainerView extends RelativeLayout implements DragSource,
- OnLongClickListener, Insettable, BubbleTextShadowHandler, OnDeviceProfileChangeListener {
+public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
+ Insettable, OnDeviceProfileChangeListener {
private final Launcher mLauncher;
private final AdapterHolder[] mAH;
- private final ClickShadowView mTouchFeedbackView;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
private final AllAppsStore mAllAppsStore = new AllAppsStore();
+ private final Paint mNavBarScrimPaint;
+ private int mNavBarScrimHeight = 0;
+
private SearchUiManager mSearchUiManager;
private View mSearchContainer;
- private InterceptingViewPager mViewPager;
+ private AllAppsPagedView mViewPager;
private FloatingHeaderView mHeader;
- private TabsPagerAdapter mTabsPagerAdapter;
private SpannableStringBuilder mSearchQueryBuilder = null;
- private int mNumAppsPerRow;
- private int mNumPredictedAppsPerRow;
-
private boolean mUsingTabs;
- private boolean mHasPredictions = false;
private boolean mSearchModeWhileUsingTabs = false;
+ private RecyclerViewFastScroller mTouchHandler;
+ private final Point mFastScrollerOffset = new Point();
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -109,28 +100,24 @@
mLauncher.addOnDeviceProfileChangeListener(this);
mSearchQueryBuilder = new SpannableStringBuilder();
-
Selection.setSelection(mSearchQueryBuilder, 0);
- mTouchFeedbackView = new ClickShadowView(context);
- // Make the feedback view large enough to hold the blur bitmap.
- int size = mLauncher.getDeviceProfile().allAppsIconSizePx
- + mTouchFeedbackView.getExtraSize();
- addView(mTouchFeedbackView, size, size);
-
mAH = new AdapterHolder[2];
mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
- }
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- applyTouchDelegate();
- }
+ mNavBarScrimPaint = new Paint();
+ mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
- private void applyTouchDelegate() {
- // TODO: Reimplement once fast scroller is fixed.
+ mAllAppsStore.addUpdateListener(this::onAppsUpdated);
+
+ // Attach a scrim to be drawn behind all-apps and hotseat
+ new ColorScrim(this, Themes.getAttrColor(context, R.attr.allAppsScrimColor), DEACCEL_2)
+ .attach();
+
+ addSpringView(R.id.all_apps_header);
+ addSpringView(R.id.apps_list_view);
+ addSpringView(R.id.all_apps_tabs_view_pager);
}
public AllAppsStore getAppsStore() {
@@ -138,12 +125,6 @@
}
@Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- applyTouchDelegate();
- }
-
- @Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {
if (holder.recyclerView != null) {
@@ -155,40 +136,17 @@
}
}
- @Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- mTouchFeedbackView.setPressedIcon(icon, background);
- }
-
- /**
- * Sets the current set of apps.
- */
- public void setApps(List<AppInfo> apps) {
- boolean hasWorkProfileApp = hasWorkProfileApp(apps);
- rebindAdapters(hasWorkProfileApp);
- mAllAppsStore.setApps(apps);
- }
-
- /**
- * Adds or updates existing apps in the list
- */
- public void addOrUpdateApps(List<AppInfo> apps) {
- mAllAppsStore.addOrUpdateApps(apps);
- }
-
- /**
- * Removes some apps from the list.
- */
- public void removeApps(List<AppInfo> apps) {
- mAllAppsStore.removeApps(apps);
- }
-
- public void updatePromiseAppProgress(PromiseAppInfo app) {
- mAllAppsStore.updateAllIcons((child) -> {
- if (child.getTag() == app) {
- child.applyProgressLevel(app.level);
+ private void onAppsUpdated() {
+ if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
+ boolean hasWorkApps = false;
+ for (AppInfo app : mAllAppsStore.getApps()) {
+ if (mWorkMatcher.matches(app, null)) {
+ hasWorkApps = true;
+ break;
+ }
}
- });
+ rebindAdapters(hasWorkApps);
+ }
}
/**
@@ -201,11 +159,55 @@
return true;
}
AllAppsRecyclerView rv = getActiveRecyclerView();
- return rv == null || rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ if (rv == null) {
+ return true;
+ }
+ if (rv.getScrollbar().getThumbOffsetY() >= 0 &&
+ mLauncher.getDragLayer().isEventOverView(rv.getScrollbar(), ev)) {
+ return false;
+ }
+ return rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ if (rv != null &&
+ rv.getScrollbar().isHitInParent(ev.getX(), ev.getY(), mFastScrollerOffset)) {
+ mTouchHandler = rv.getScrollbar();
+ }
+ }
+ if (mTouchHandler != null) {
+ return mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mTouchHandler != null) {
+ mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
+ return true;
+ }
+ return false;
+ }
+
+ public String getDescription() {
+ @StringRes int descriptionRes;
+ if (mUsingTabs) {
+ descriptionRes =
+ mViewPager.getNextPage() == 0
+ ? R.string.all_apps_button_personal_label
+ : R.string.all_apps_button_work_label;
+ } else {
+ descriptionRes = R.string.all_apps_button_label;
+ }
+ return getContext().getString(descriptionRes);
}
public AllAppsRecyclerView getActiveRecyclerView() {
- if (!mUsingTabs || mViewPager.getCurrentItem() == 0) {
+ if (!mUsingTabs || mViewPager.getNextPage() == 0) {
return mAH[AdapterHolder.MAIN].recyclerView;
} else {
return mAH[AdapterHolder.WORK].recyclerView;
@@ -215,17 +217,17 @@
/**
* Resets the state of AllApps.
*/
- public void reset() {
+ public void reset(boolean animate) {
for (int i = 0; i < mAH.length; i++) {
if (mAH[i].recyclerView != null) {
mAH[i].recyclerView.scrollToTop();
}
}
if (isHeaderVisible()) {
- mHeader.reset();
+ mHeader.reset(animate);
}
// Reset the search bar and base recycler view after transitioning home
- mSearchUiManager.reset();
+ mSearchUiManager.resetSearch();
}
@Override
@@ -253,58 +255,12 @@
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
-
- if (mNumAppsPerRow != grid.inv.numColumns ||
- mNumPredictedAppsPerRow != grid.inv.numColumns) {
- mNumAppsPerRow = grid.inv.numColumns;
- mNumPredictedAppsPerRow = grid.inv.numColumns;
- for (int i = 0; i < mAH.length; i++) {
- mAH[i].applyNumsPerRow();
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
public boolean dispatchKeyEvent(KeyEvent event) {
mSearchUiManager.preDispatchKeyEvent(event);
return super.dispatchKeyEvent(event);
}
@Override
- public boolean onLongClick(final View v) {
- // When we have exited all apps or are in transition, disregard long clicks
- if (!mLauncher.isInState(LauncherState.ALL_APPS) ||
- mLauncher.getWorkspace().isSwitchingState()) return false;
- // Return if global dragging is not enabled or we are already dragging
- if (!mLauncher.isDraggingEnabled()) return false;
- if (mLauncher.getDragController().isDragging()) return false;
-
- // Start the drag
- final DragController dragController = mLauncher.getDragController();
- dragController.addDragListener(new DragController.DragListener() {
- @Override
- public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
- v.setVisibility(INVISIBLE);
- }
-
- @Override
- public void onDragEnd() {
- v.setVisibility(VISIBLE);
- dragController.removeDragListener(this);
- }
- });
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
- DragOptions options = new DragOptions();
- options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
- mLauncher.getWorkspace().beginDragShared(v, this, options);
- return false;
- }
-
- @Override
public void onDropCompleted(View target, DragObject d, boolean success) { }
@Override
@@ -327,37 +283,26 @@
ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
if (grid.isVerticalBarLayout()) {
mlp.leftMargin = insets.left;
- mlp.topMargin = insets.top;
mlp.rightMargin = insets.right;
setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
- mlp.leftMargin = mlp.rightMargin = mlp.topMargin = 0;
+ mlp.leftMargin = mlp.rightMargin = 0;
setPadding(0, 0, 0, 0);
}
setLayoutParams(mlp);
- View navBarBg = findViewById(R.id.nav_bar_bg);
- ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
- navBarBgLp.height = insets.bottom;
- navBarBg.setLayoutParams(navBarBgLp);
-
+ mNavBarScrimHeight = insets.bottom;
InsettableFrameLayout.dispatchInsets(this, insets);
}
- public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
- PackageUserKey tempKey = new PackageUserKey(null, null);
- mAllAppsStore.updateAllIcons((child) -> {
- if (child.getTag() instanceof ItemInfo) {
- ItemInfo info = (ItemInfo) child.getTag();
- if (tempKey.updateFromItemInfo(info) && updatedBadges.contains(tempKey)) {
- child.applyBadgeState(info, true /* animate */);
- }
- }
- });
- }
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
- public SpringAnimationHandler getSpringAnimationHandler() {
- return mUsingTabs ? null : mAH[AdapterHolder.MAIN].animationHandler;
+ if (mNavBarScrimHeight > 0) {
+ canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
+ mNavBarScrimPaint);
+ }
}
private void rebindAdapters(boolean showTabs) {
@@ -377,34 +322,15 @@
if (mUsingTabs) {
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
- setupWorkProfileTabs();
- setupHeader();
+ onTabChanged(mViewPager.getNextPage());
} else {
- mTabsPagerAdapter = null;
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
- if (FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
- setupHeader();
- } else {
- mHeader.setVisibility(View.GONE);
- }
}
+ setupHeader();
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
-
- applyTouchDelegate();
- }
-
- private boolean hasWorkProfileApp(List<AppInfo> apps) {
- if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
- for (AppInfo app : apps) {
- if (mWorkMatcher.matches(app, null)) {
- return true;
- }
- }
- }
- return false;
}
private void replaceRVContainer(boolean showTabs) {
@@ -419,92 +345,64 @@
int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
View newView = LayoutInflater.from(getContext()).inflate(layout, this, false);
addView(newView, index);
- mViewPager = showTabs ? (InterceptingViewPager) newView : null;
+ if (showTabs) {
+ mViewPager = (AllAppsPagedView) newView;
+ mViewPager.initParentViews(this);
+ mViewPager.getPageIndicator().setContainerView(this);
+ } else {
+ mViewPager = null;
+ }
}
public View getRecyclerViewContainer() {
return mViewPager != null ? mViewPager : findViewById(R.id.apps_list_view);
}
- private void setupWorkProfileTabs() {
- if (mTabsPagerAdapter != null) {
- return;
+ public void onTabChanged(int pos) {
+ mHeader.setMainActive(pos == 0);
+ reset(true /* animate */);
+ if (mAH[pos].recyclerView != null) {
+ mAH[pos].recyclerView.bindFastScrollbar();
+
+ findViewById(R.id.tab_personal)
+ .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
+ findViewById(R.id.tab_work)
+ .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
+
}
- final PersonalWorkSlidingTabStrip tabs = findViewById(R.id.tabs);
- mViewPager.setAdapter(mTabsPagerAdapter = new TabsPagerAdapter());
- mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
-
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- tabs.updateIndicatorPosition(position, positionOffset);
- }
-
- @Override
- public void onPageSelected(int pos) {
- tabs.updateTabTextColor(pos);
- mHeader.setMainActive(pos == 0);
- reset();
- applyTouchDelegate();
- if (mAH[pos].recyclerView != null) {
- mAH[pos].recyclerView.bindFastScrollbar();
- }
- if (pos == AdapterHolder.WORK) {
- BottomUserEducationView.showIfNeeded(mLauncher);
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
- });
- mAH[AdapterHolder.MAIN].recyclerView.bindFastScrollbar();
-
- findViewById(R.id.tab_personal)
- .setOnClickListener((View view) -> mViewPager.setCurrentItem(0));
- findViewById(R.id.tab_work)
- .setOnClickListener((View view) -> mViewPager.setCurrentItem(1));
- }
-
- public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
- mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps);
- boolean hasPredictions = !apps.isEmpty();
- if (mHasPredictions != hasPredictions) {
- mHasPredictions = hasPredictions;
- if (FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
- setupHeader();
- }
+ if (pos == AdapterHolder.WORK) {
+ BottomUserEducationView.showIfNeeded(mLauncher);
}
}
- public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
- return mAllAppsStore.getApp(mapper);
- }
-
public AlphabeticalAppsList getApps() {
return mAH[AdapterHolder.MAIN].appsList;
}
- public boolean isUsingTabs() {
- return mUsingTabs;
- }
-
public FloatingHeaderView getFloatingHeaderView() {
return mHeader;
}
+ public View getSearchView() {
+ return mSearchContainer;
+ }
+
+ public View getContentView() {
+ return mViewPager == null ? getActiveRecyclerView() : mViewPager;
+ }
+
+ public RecyclerViewFastScroller getScrollBar() {
+ AllAppsRecyclerView rv = getActiveRecyclerView();
+ return rv == null ? null : rv.getScrollbar();
+ }
+
public void setupHeader() {
- if (mHeader == null) {
- return;
- }
mHeader.setVisibility(View.VISIBLE);
mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null);
int padding = mHeader.getMaxTranslation();
- if (mHasPredictions && !mUsingTabs) {
- padding += mHeader.getPaddingTop() + mHeader.getPaddingBottom();
- }
for (int i = 0; i < mAH.length; i++) {
- mAH[i].paddingTopForTabs = padding;
+ mAH[i].padding.top = padding;
mAH[i].applyPadding();
}
}
@@ -534,13 +432,6 @@
}
}
- public void setRecyclerViewPaddingTop(int top) {
- for (int i = 0; i < mAH.length; i++) {
- mAH[i].padding.top = top;
- mAH[i].applyPadding();
- }
- }
-
public void setRecyclerViewVerticalFadingEdgeEnabled(boolean enabled) {
for (int i = 0; i < mAH.length; i++) {
mAH[i].applyVerticalFadingEdgeEnabled(enabled);
@@ -569,58 +460,38 @@
public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
- final SpringAnimationHandler animationHandler;
final AlphabeticalAppsList appsList;
final Rect padding = new Rect();
- int paddingTopForTabs;
AllAppsRecyclerView recyclerView;
boolean verticalFadingEdge;
AdapterHolder(boolean isWork) {
appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
- adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
- AllAppsContainerView.this, true);
+ adapter = new AllAppsGridAdapter(mLauncher, appsList);
appsList.setAdapter(adapter);
- animationHandler = adapter.getSpringAnimationHandler();
layoutManager = adapter.getLayoutManager();
}
void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
appsList.updateItemFilter(matcher);
recyclerView = (AllAppsRecyclerView) rv;
+ recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
recyclerView.setApps(appsList, mUsingTabs);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
recyclerView.setHasFixedSize(true);
// No animations will occur when changes occur to the items in this RecyclerView.
recyclerView.setItemAnimator(null);
- if (FeatureFlags.LAUNCHER3_PHYSICS && animationHandler != null) {
- recyclerView.setSpringAnimationHandler(animationHandler);
- }
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView);
recyclerView.addItemDecoration(focusedItemDecorator);
- recyclerView.preMeasureViews(adapter);
adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
applyVerticalFadingEdgeEnabled(verticalFadingEdge);
applyPadding();
- applyNumsPerRow();
}
void applyPadding() {
if (recyclerView != null) {
- int paddingTop = mUsingTabs || FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW
- ? paddingTopForTabs : padding.top;
- recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom);
- }
- }
-
- void applyNumsPerRow() {
- if (mNumAppsPerRow > 0) {
- if (recyclerView != null) {
- recyclerView.setNumAppsPerRow(mLauncher.getDeviceProfile(), mNumAppsPerRow);
- }
- adapter.setNumAppsPerRow(mNumAppsPerRow);
- appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
+ recyclerView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
}
}
@@ -630,37 +501,4 @@
&& verticalFadingEdge);
}
}
-
- private class TabsPagerAdapter extends PagerAdapter {
- @Override
- public int getCount() {
- return 2;
- }
-
- @Override
- public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
- return view == object;
- }
-
- @NonNull
- @Override
- public Object instantiateItem(@NonNull ViewGroup container, int position) {
- if (position == 0) {
- return mAH[AdapterHolder.MAIN].recyclerView;
- } else {
- return mAH[AdapterHolder.WORK].recyclerView;
- }
- }
-
- @Nullable
- @Override
- public CharSequence getPageTitle(int position) {
- if (position == 0) {
- return getResources().getString(R.string.all_apps_personal_tab);
- } else {
- return getResources().getString(R.string.all_apps_work_tab);
- }
- }
- }
-
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 2103106..27fc53a 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,8 +18,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.support.animation.DynamicAnimation;
-import android.support.animation.SpringAnimation;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -38,11 +36,10 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
-import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -56,29 +53,21 @@
// A normal icon
public static final int VIEW_TYPE_ICON = 1 << 1;
- // A prediction icon
- public static final int VIEW_TYPE_PREDICTION_ICON = 1 << 2;
// The message shown when there are no filtered results
- public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 3;
+ public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
// The message to continue to a market search when there are no filtered results
- public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 4;
+ public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
// but differ in enough attributes to require different view types
// A divider that separates the apps list and the search market button
- public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 5;
- // The divider that separates prediction icons from the app list
- public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
- public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 7;
+ public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
+ public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 5;
// Common view type masks
- public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER
- | VIEW_TYPE_PREDICTION_DIVIDER;
- public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
- | VIEW_TYPE_PREDICTION_ICON;
- public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON
- | VIEW_TYPE_PREDICTION_DIVIDER;
+ public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
+ public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
public interface BindViewCallback {
@@ -189,10 +178,8 @@
private final AlphabeticalAppsList mApps;
private final GridLayoutManager mGridLayoutMgr;
private final GridSpanSizer mGridSizer;
- private final View.OnClickListener mIconClickListener;
- private final View.OnLongClickListener mIconLongClickListener;
- private int mAppsPerRow;
+ private final int mAppsPerRow;
private BindViewCallback mBindViewCallback;
private OnFocusChangeListener mIconFocusListener;
@@ -202,10 +189,7 @@
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
- private final SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
-
- public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
- iconClickListener, View.OnLongClickListener iconLongClickListener, boolean springAnim) {
+ public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps) {
Resources res = launcher.getResources();
mLauncher = launcher;
mApps = apps;
@@ -214,18 +198,9 @@
mGridLayoutMgr = new AppsGridLayoutManager(launcher);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mLayoutInflater = LayoutInflater.from(launcher);
- mIconClickListener = iconClickListener;
- mIconLongClickListener = iconLongClickListener;
- if (FeatureFlags.LAUNCHER3_PHYSICS && springAnim) {
- mSpringAnimationHandler = new SpringAnimationHandler<>(
- SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
- } else {
- mSpringAnimationHandler = null;
- }
- }
- public SpringAnimationHandler getSpringAnimationHandler() {
- return mSpringAnimationHandler;
+ mAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
+ mGridLayoutMgr.setSpanCount(mAppsPerRow);
}
public static boolean isDividerViewType(int viewType) {
@@ -240,18 +215,6 @@
return (viewType & viewTypeMask) != 0;
}
- /**
- * Sets the number of apps per row.
- */
- public void setNumAppsPerRow(int appsPerRow) {
- mAppsPerRow = appsPerRow;
- mGridLayoutMgr.setSpanCount(appsPerRow);
- }
-
- public int getNumAppsPerRow() {
- return mAppsPerRow;
- }
-
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
@@ -284,11 +247,10 @@
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
- case VIEW_TYPE_PREDICTION_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
- icon.setOnClickListener(mIconClickListener);
- icon.setOnLongClickListener(mIconLongClickListener);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
+ icon.setOnLongClickListener(ItemLongClickListener.INSTANCE_ALL_APPS);
icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setOnFocusChangeListener(mIconFocusListener);
@@ -308,7 +270,6 @@
}
});
return new ViewHolder(searchMarketView);
- case VIEW_TYPE_PREDICTION_DIVIDER:
case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
@@ -324,7 +285,6 @@
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
- case VIEW_TYPE_PREDICTION_ICON:
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
@@ -363,22 +323,6 @@
}
@Override
- public void onViewAttachedToWindow(ViewHolder holder) {
- int type = holder.getItemViewType();
- if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- mSpringAnimationHandler.add(holder.itemView, holder);
- }
- }
-
- @Override
- public void onViewDetachedFromWindow(ViewHolder holder) {
- int type = holder.getItemViewType();
- if (mSpringAnimationHandler != null && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
- mSpringAnimationHandler.remove(holder.itemView);
- }
- }
-
- @Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
@@ -395,132 +339,4 @@
return item.viewType;
}
- /**
- * Helper class to set the SpringAnimation values for an item in the adapter.
- */
- private class AllAppsSpringAnimationFactory
- implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
- private static final float DEFAULT_MAX_VALUE_PX = 100;
- private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
-
- // Damping ratio range is [0, 1]
- private static final float SPRING_DAMPING_RATIO = 0.55f;
-
- // Stiffness is a non-negative number.
- private static final float MIN_SPRING_STIFFNESS = 580f;
- private static final float MAX_SPRING_STIFFNESS = 900f;
-
- // The amount by which each adjacent rows' stiffness will differ.
- private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
-
- // The percentage by which we multiply each row to create the row factor.
- private static final float ROW_PERCENTAGE = 0.3f;
-
- @Override
- public SpringAnimation initialize(ViewHolder vh) {
- return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
- }
-
- /**
- * @param spring A new or recycled SpringAnimation.
- * @param vh The ViewHolder that {@param spring} is related to.
- */
- @Override
- public void update(SpringAnimation spring, ViewHolder vh) {
- int numPredictedApps = Math.min(mAppsPerRow, mApps.getPredictedApps().size());
- int appPosition = getAppPosition(vh.getAdapterPosition(), numPredictedApps,
- mAppsPerRow);
-
- int col = appPosition % mAppsPerRow;
- int row = appPosition / mAppsPerRow;
-
- int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
- if (row > (numTotalRows / 2)) {
- // Mirror the rows so that the top row acts the same as the bottom row.
- row = Math.abs(numTotalRows - row);
- }
-
- calculateSpringValues(spring, row, col);
- }
-
- @Override
- public void setDefaultValues(SpringAnimation spring) {
- calculateSpringValues(spring, 0, mAppsPerRow / 2);
- }
-
- /**
- * We manipulate the stiffness, min, and max values based on the items distance to the
- * first row and the items distance to the center column to create the ^-shaped motion
- * effect.
- */
- private void calculateSpringValues(SpringAnimation spring, int row, int col) {
- float rowFactor = (1 + row) * ROW_PERCENTAGE;
- float colFactor = getColumnFactor(col, mAppsPerRow);
-
- float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
- float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
-
- float stiffness = Utilities.boundToRange(
- MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
- MIN_SPRING_STIFFNESS,
- MAX_SPRING_STIFFNESS);
-
- spring.setMinValue(minValue)
- .setMaxValue(maxValue)
- .getSpring()
- .setStiffness(stiffness)
- .setDampingRatio(SPRING_DAMPING_RATIO);
- }
-
- /**
- * @return The app position is the position of the app in the Adapter if we ignored all
- * other view types.
- *
- * The first app is at position 0, and the first app each following row is at a
- * position that is a multiple of {@param appsPerRow}.
- *
- * ie. If there are 5 apps per row, and there are two rows of apps:
- * 0 1 2 3 4
- * 5 6 7 8 9
- */
- private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
- if (position < numPredictedApps) {
- // Predicted apps are first in the adapter.
- return position;
- }
-
- // There is at most 1 divider view between the predicted apps and the alphabetical apps.
- int numDividerViews = numPredictedApps == 0 ? 0 : 1;
-
- // This offset takes into consideration an incomplete row of predicted apps.
- int numPredictedAppsOffset = appsPerRow - numPredictedApps;
- return position + numPredictedAppsOffset - numDividerViews;
- }
-
- /**
- * Increase the column factor as the distance increases between the column and the center
- * column(s).
- */
- private float getColumnFactor(int col, int numCols) {
- float centerColumn = numCols / 2;
- int distanceToCenter = (int) Math.abs(col - centerColumn);
-
- boolean evenNumberOfColumns = numCols % 2 == 0;
- if (evenNumberOfColumns && col < centerColumn) {
- distanceToCenter -= 1;
- }
-
- float factor = 0;
- while (distanceToCenter > 0) {
- if (distanceToCenter == 1) {
- factor += 0.2f;
- } else {
- factor += 0.1f;
- }
- --distanceToCenter;
- }
-
- return factor;
- }
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
new file mode 100644
index 0000000..b2e35a4
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -0,0 +1,79 @@
+/*
+ * 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.allapps;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.launcher3.PagedView;
+
+public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
+
+ 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) {
+ this(context, null);
+ }
+
+ public AllAppsPagedView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AllAppsPagedView(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, mMaxScrollX);
+ }
+
+ @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);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 4792cc2..a7447b7 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,29 +15,26 @@
*/
package com.android.launcher3.allapps;
-import android.animation.ObjectAnimator;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
-import android.util.Property;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
-import com.android.launcher3.touch.OverScroll;
-import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -51,7 +48,7 @@
private AlphabeticalAppsList mApps;
private AllAppsFastScrollHelper mFastScrollHelper;
- private int mNumAppsPerRow;
+ private final int mNumAppsPerRow;
// The specific view heights that we use to calculate scroll
private SparseIntArray mViewHeights = new SparseIntArray();
@@ -61,23 +58,6 @@
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
- private SpringAnimationHandler mSpringAnimationHandler;
- private OverScrollHelper mOverScrollHelper;
- private SwipeDetector mPullDetector;
- private float mContentTranslationY = 0;
- public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y =
- new Property<AllAppsRecyclerView, Float>(Float.class, "appsRecyclerViewContentTransY") {
- @Override
- public Float get(AllAppsRecyclerView allAppsRecyclerView) {
- return allAppsRecyclerView.getContentTranslationY();
- }
-
- @Override
- public void set(AllAppsRecyclerView allAppsRecyclerView, Float y) {
- allAppsRecyclerView.setContentTranslationY(y);
- }
- };
-
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -94,29 +74,9 @@
int defStyleRes) {
super(context, attrs, defStyleAttr);
Resources res = getResources();
- addOnItemTouchListener(this);
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
-
- mOverScrollHelper = new OverScrollHelper();
- mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL);
- mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
- }
-
- public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
- if (FeatureFlags.LAUNCHER3_PHYSICS) {
- mSpringAnimationHandler = springAnimationHandler;
- addOnScrollListener(new SpringMotionOnScrollListener());
- }
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent e) {
- mPullDetector.onTouchEvent(e);
- if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
- mSpringAnimationHandler.addMovement(e);
- }
- return super.onTouchEvent(e);
+ mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
}
/**
@@ -131,53 +91,17 @@
return mApps;
}
- /**
- * Sets the number of apps per row in this recycler view.
- */
- public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) {
- mNumAppsPerRow = numAppsPerRow;
+ private void updatePoolSize() {
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
- pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, mNumAppsPerRow);
- pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, 1);
- }
- /**
- * Ensures that we can present a stable scrollbar for views of varying types by pre-measuring
- * all the different view types.
- */
- public void preMeasureViews(AllAppsGridAdapter adapter) {
- View icon = adapter.onCreateViewHolder(this, AllAppsGridAdapter.VIEW_TYPE_ICON).itemView;
- final int iconHeight = icon.getLayoutParams().height;
- mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, iconHeight);
- mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, iconHeight);
-
- final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST);
- final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST);
-
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER,
- AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_WORK_TAB_FOOTER);
- }
-
- private void putSameHeightFor(AllAppsGridAdapter adapter, int w, int h, int... viewTypes) {
- View view = adapter.onCreateViewHolder(this, viewTypes[0]).itemView;
- view.measure(w, h);
- for (int viewType : viewTypes) {
- mViewHeights.put(viewType, view.getMeasuredHeight());
- }
+ mViewHeights.clear();
+ mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
/**
@@ -202,26 +126,6 @@
}
@Override
- protected void dispatchDraw(Canvas canvas) {
- canvas.translate(0, mContentTranslationY);
- super.dispatchDraw(canvas);
- canvas.translate(0, -mContentTranslationY);
- }
-
- public float getContentTranslationY() {
- return mContentTranslationY;
- }
-
- /**
- * Use this method instead of calling {@link #setTranslationY(float)}} directly to avoid drawing
- * on top of other Views.
- */
- public void setContentTranslationY(float y) {
- mContentTranslationY = y;
- invalidate();
- }
-
- @Override
protected boolean verifyDrawable(Drawable who) {
return who == mEmptySearchBackground || super.verifyDrawable(who);
}
@@ -229,6 +133,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateEmptySearchBackgroundBounds();
+ updatePoolSize();
}
@Override
@@ -236,19 +141,6 @@
if (mApps.hasFilter()) {
targetParent.containerType = ContainerType.SEARCHRESULT;
} else {
- if (v instanceof BubbleTextView) {
- BubbleTextView icon = (BubbleTextView) v;
- int position = getChildPosition(icon);
- if (position != NO_POSITION) {
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- AlphabeticalAppsList.AdapterItem item = items.get(position);
- if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
- targetParent.containerType = ContainerType.PREDICTION;
- target.predictedRank = item.rowAppIndex;
- return;
- }
- }
- }
targetParent.containerType = ContainerType.ALLAPPS;
}
}
@@ -275,8 +167,7 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
- mPullDetector.onTouchEvent(e);
- boolean result = super.onInterceptTouchEvent(e) || mOverScrollHelper.isInOverScroll();
+ boolean result = super.onInterceptTouchEvent(e);
if (!result && e.getAction() == MotionEvent.ACTION_DOWN
&& mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.setHotspot(e.getX(), e.getY());
@@ -468,7 +359,21 @@
}
} else {
// Rest of the views span the full width
- y += mViewHeights.get(item.viewType, 0);
+ int elHeight = mViewHeights.get(item.viewType);
+ if (elHeight == 0) {
+ ViewHolder holder = findViewHolderForAdapterPosition(i);
+ if (holder == null) {
+ holder = getAdapter().createViewHolder(this, item.viewType);
+ getAdapter().onBindViewHolder(holder, i);
+ holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
+ elHeight = holder.itemView.getMeasuredHeight();
+
+ getRecycledViewPool().putRecycledView(holder);
+ } else {
+ elHeight = holder.itemView.getMeasuredHeight();
+ }
+ }
+ y += elHeight;
}
}
mCachedScrollPositions.put(position, y);
@@ -510,114 +415,4 @@
y + mEmptySearchBackground.getIntrinsicHeight());
}
- private class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- if (mOverScrollHelper.isInOverScroll()) {
- // OverScroll will handle animating the springs.
- return;
- }
-
- // We only start the spring animation when we hit the top/bottom, to ensure
- // that all of the animations start at the same time.
- if (dy < 0 && !canScrollVertically(-1)) {
- mSpringAnimationHandler.animateToFinalPosition(0, 1);
- } else if (dy > 0 && !canScrollVertically(1)) {
- mSpringAnimationHandler.animateToFinalPosition(0, -1);
- }
- }
- }
-
- private class OverScrollHelper implements SwipeDetector.Listener {
-
- private static final float MAX_RELEASE_VELOCITY = 5000; // px / s
- private static final float MAX_OVERSCROLL_PERCENTAGE = 0.07f;
-
- private boolean mIsInOverScroll;
-
- // We use this value to calculate the actual amount the user has overscrolled.
- private float mFirstDisplacement = 0;
-
- private boolean mAlreadyScrollingUp;
- private int mFirstScrollYOnScrollUp;
-
- @Override
- public void onDragStart(boolean start) {
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- boolean isScrollingUp = displacement > 0;
- if (isScrollingUp) {
- if (!mAlreadyScrollingUp) {
- mFirstScrollYOnScrollUp = getCurrentScrollY();
- mAlreadyScrollingUp = true;
- }
- } else {
- mAlreadyScrollingUp = false;
- }
-
- // Only enter overscroll if the user is interacting with the RecyclerView directly
- // and if one of the following criteria are met:
- // - User scrolls down when they're already at the bottom.
- // - User starts scrolling up, hits the top, and continues scrolling up.
- boolean wasInOverScroll = mIsInOverScroll;
- mIsInOverScroll = !mScrollbar.isDraggingThumb() &&
- ((!canScrollVertically(1) && displacement < 0) ||
- (!canScrollVertically(-1) && isScrollingUp && mFirstScrollYOnScrollUp != 0));
-
- if (wasInOverScroll && !mIsInOverScroll) {
- // Exit overscroll. This can happen when the user is in overscroll and then
- // scrolls the opposite way.
- reset(false /* shouldSpring */);
- } else if (mIsInOverScroll) {
- if (Float.compare(mFirstDisplacement, 0) == 0) {
- // Because users can scroll before entering overscroll, we need to
- // subtract the amount where the user was not in overscroll.
- mFirstDisplacement = displacement;
- }
- float overscrollY = displacement - mFirstDisplacement;
- setContentTranslationY(getDampedOverScroll(overscrollY));
- }
-
- return mIsInOverScroll;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- reset(mIsInOverScroll /* shouldSpring */);
- }
-
- private void reset(boolean shouldSpring) {
- float y = getContentTranslationY();
- if (Float.compare(y, 0) != 0) {
- if (mSpringAnimationHandler != null && shouldSpring) {
- // We calculate our own velocity to give the springs the desired effect.
- float velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
- // We want to negate the velocity because we are moving to 0 from -1 due to the
- // downward motion. (y-axis -1 is above 0).
- mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity);
- }
-
- ObjectAnimator.ofFloat(AllAppsRecyclerView.this,
- AllAppsRecyclerView.CONTENT_TRANS_Y, 0)
- .setDuration(100)
- .start();
- }
- mIsInOverScroll = false;
- mFirstDisplacement = 0;
- mFirstScrollYOnScrollUp = 0;
- mAlreadyScrollingUp = false;
- }
-
- public boolean isInOverScroll() {
- return mIsInOverScroll;
- }
-
- private float getDampedOverScroll(float y) {
- return OverScroll.dampedScroll(y, getHeight());
- }
- }
-
}
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 17f1c89..dc34892 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -20,23 +20,30 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
+import com.android.launcher3.util.PackageUserKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
/**
* A utility class to maintain the collection of all apps.
*/
public class AllAppsStore {
+ private PackageUserKey mTempKey = new PackageUserKey(null, null);
private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
+ private boolean mDeferUpdates = false;
+ private boolean mUpdatePending = false;
+
public Collection<AppInfo> getApps() {
return mComponentToAppMap.values();
}
@@ -53,8 +60,15 @@
return mComponentToAppMap.get(key);
}
- public AppInfo getApp(ComponentKeyMapper<AppInfo> mapper) {
- return mapper.getItem(mComponentToAppMap);
+ public void setDeferUpdates(boolean deferUpdates) {
+ if (mDeferUpdates != deferUpdates) {
+ mDeferUpdates = deferUpdates;
+
+ if (!mDeferUpdates && mUpdatePending) {
+ notifyUpdate();
+ mUpdatePending = false;
+ }
+ }
}
/**
@@ -79,6 +93,10 @@
private void notifyUpdate() {
+ if (mDeferUpdates) {
+ mUpdatePending = true;
+ return;
+ }
int count = mUpdateListeners.size();
for (int i = 0; i < count; i++) {
mUpdateListeners.get(i).onAppsUpdated();
@@ -103,7 +121,26 @@
mIconContainers.remove(container);
}
- public void updateAllIcons(IconAction action) {
+ public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
+ updateAllIcons((child) -> {
+ if (child.getTag() instanceof ItemInfo) {
+ ItemInfo info = (ItemInfo) child.getTag();
+ if (mTempKey.updateFromItemInfo(info) && updatedBadges.contains(mTempKey)) {
+ child.applyBadgeState(info, true /* animate */);
+ }
+ }
+ });
+ }
+
+ public void updatePromiseAppProgress(PromiseAppInfo app) {
+ updateAllIcons((child) -> {
+ if (child.getTag() == app) {
+ child.applyProgressLevel(app.level);
+ }
+ });
+ }
+
+ private void updateAllIcons(IconAction action) {
for (int i = mIconContainers.size() - 1; i >= 0; i--) {
ViewGroup parent = mIconContainers.get(i);
int childCount = parent.getChildCount();
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c859347..ed9873e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,7 +1,12 @@
package com.android.launcher3.allapps;
+import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
+import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
+import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
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.PropertySetter.NO_ANIM_PROPERTY_SETTER;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
@@ -13,20 +18,15 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.SearchUiManager.OnScrollRangeChangeListener;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.views.AllAppsScrim;
/**
* Handles AllApps view transition.
@@ -38,8 +38,7 @@
* 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 OnScrollRangeChangeListener, StateHandler, OnDeviceProfileChangeListener {
+public class AllAppsTransitionController implements StateHandler, OnDeviceProfileChangeListener {
public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@@ -55,11 +54,7 @@
}
};
- public static final float PARALLAX_COEFFICIENT = .125f;
-
private AllAppsContainerView mAppsView;
- private Workspace mWorkspace;
- private Hotseat mHotseat;
private final Launcher mLauncher;
private final boolean mIsDarkTheme;
@@ -74,13 +69,11 @@
private float mShiftRange; // changes depending on the orientation
private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent
- private static final float DEFAULT_SHIFT_RANGE = 10;
-
- private AllAppsScrim mAllAppsScrim;
+ private float mScrollRangeDelta = 0;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
- mShiftRange = DEFAULT_SHIFT_RANGE;
+ mShiftRange = mLauncher.getDeviceProfile().heightPx;
mProgress = 1f;
mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
@@ -94,19 +87,19 @@
private void onProgressAnimationStart() {
// Initialize values that should not change until #onDragEnd
- mHotseat.setVisibility(View.VISIBLE);
mAppsView.setVisibility(View.VISIBLE);
}
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
mIsVerticalLayout = dp.isVerticalBarLayout();
+ setScrollRangeDelta(mScrollRangeDelta);
if (mIsVerticalLayout) {
mAppsView.setAlpha(1);
mLauncher.getHotseat().setTranslationY(0);
mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
+ mLauncher.getDragHandleIndicator().setTranslationY(0);
}
}
@@ -123,29 +116,22 @@
mProgress = progress;
float shiftCurrent = progress * mShiftRange;
- float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f);
- float alpha = 1 - workspaceHotseatAlpha;
-
mAppsView.setTranslationY(shiftCurrent);
- if (mAllAppsScrim == null) {
- mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
- }
float hotseatTranslation = -mShiftRange + shiftCurrent;
- mAllAppsScrim.setProgress(hotseatTranslation, alpha);
if (!mIsVerticalLayout) {
- mAppsView.setAlpha(alpha);
mLauncher.getHotseat().setTranslationY(hotseatTranslation);
mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation);
+ mLauncher.getDragHandleIndicator().setTranslationY(hotseatTranslation);
+ }
- // Use a light system UI (dark icons) if all apps is behind at least half of the
- // status bar.
- boolean forceChange = shiftCurrent <= mShiftRange / 4;
- if (forceChange) {
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
- } else {
- mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
- }
+ // Use a light system UI (dark icons) if all apps is behind at least half of the
+ // status bar.
+ boolean forceChange = shiftCurrent <= mShiftRange / 4;
+ if (forceChange) {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
+ } else {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
}
}
@@ -160,6 +146,7 @@
@Override
public void setState(LauncherState state) {
setProgress(state.getVerticalProgress(mLauncher));
+ setAlphas(state, NO_ANIM_PROPERTY_SETTER);
onProgressAnimationEnd();
}
@@ -172,6 +159,7 @@
AnimatorSetBuilder builder, AnimationConfig config) {
float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) {
+ setAlphas(toState, config.getProperSetter(builder));
// Fail fast
onProgressAnimationEnd();
return;
@@ -181,10 +169,24 @@
ObjectAnimator anim =
ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress);
anim.setDuration(config.duration);
- anim.setInterpolator(interpolator);
+ anim.setInterpolator(builder.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
anim.addListener(getProgressAnimatorListener());
builder.play(anim);
+
+ setAlphas(toState, config.getProperSetter(builder));
+ }
+
+ private void setAlphas(LauncherState toState, PropertySetter setter) {
+ int visibleElements = toState.getVisibleElements(mLauncher);
+ boolean hasHeader = (visibleElements & ALL_APPS_HEADER) != 0;
+ boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
+ boolean hasContent = (visibleElements & ALL_APPS_CONTENT) != 0;
+
+ setter.setViewAlpha(mAppsView.getSearchView(), hasHeader ? 1 : 0, LINEAR);
+ setter.setViewAlpha(mAppsView.getContentView(), hasContent ? 1 : 0, LINEAR);
+ setter.setViewAlpha(mAppsView.getScrollBar(), hasContent ? 1 : 0, LINEAR);
+ mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra, hasContent, setter);
}
public AnimatorListenerAdapter getProgressAnimatorListener() {
@@ -201,18 +203,16 @@
};
}
- public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) {
+ public void setupViews(AllAppsContainerView appsView) {
mAppsView = appsView;
- mHotseat = hotseat;
- mWorkspace = workspace;
- mHotseat.bringToFront();
- mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
}
- @Override
- public void onScrollRangeChanged(int scrollRange) {
- mShiftRange = scrollRange;
- setProgress(mProgress);
+ /**
+ * Updates the total scroll range but does not update the UI.
+ */
+ public void setScrollRangeDelta(float delta) {
+ mScrollRangeDelta = delta;
+ mShiftRange = mLauncher.getDeviceProfile().heightPx - mScrollRangeDelta;
}
/**
@@ -222,15 +222,12 @@
private void onProgressAnimationEnd() {
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.setVisibility(View.INVISIBLE);
- mHotseat.setVisibility(View.VISIBLE);
- mAppsView.reset();
+ mAppsView.reset(false /* animate */);
} else if (Float.compare(mProgress, 0f) == 0) {
- mHotseat.setVisibility(View.INVISIBLE);
mAppsView.setVisibility(View.VISIBLE);
mAppsView.onScrollUpEnd();
} else {
mAppsView.setVisibility(View.VISIBLE);
- mHotseat.setVisibility(View.VISIBLE);
}
}
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 29b32b0..434918d 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -17,19 +17,13 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.os.Process;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
@@ -47,8 +41,6 @@
public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
public static final String TAG = "AlphabeticalAppsList";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_PREDICTIONS = false;
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
@@ -95,13 +87,6 @@
// The index of this app not including sections
public int appIndex = -1;
- public static AdapterItem asPredictedApp(int pos, String sectionName, AppInfo appInfo,
- int appIndex) {
- AdapterItem item = asApp(pos, sectionName, appInfo, appIndex);
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON;
- return item;
- }
-
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
int appIndex) {
AdapterItem item = new AdapterItem();
@@ -120,13 +105,6 @@
return item;
}
- public static AdapterItem asPredictionDivider(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER;
- item.position = pos;
- return item;
- }
-
public static AdapterItem asAllAppsDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
@@ -151,7 +129,7 @@
private final Launcher mLauncher;
- // The set of apps from the system not including predictions
+ // The set of apps from the system
private final List<AppInfo> mApps = new ArrayList<>();
private final AllAppsStore mAllAppsStore;
@@ -161,10 +139,6 @@
private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
// The set of sections that we allow fast-scrolling to (includes non-merged sections)
private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
- // The set of predicted app component names
- private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
- // The set of predicted apps resolved from the component names and the current set of apps
- private final List<AppInfo> mPredictedApps = new ArrayList<>();
// Is it the work profile app list.
private final boolean mIsWork;
@@ -174,8 +148,7 @@
private AllAppsGridAdapter mAdapter;
private AlphabeticIndexCompat mIndexer;
private AppInfoComparator mAppNameComparator;
- private int mNumAppsPerRow;
- private int mNumPredictedAppsPerRow;
+ private final int mNumAppsPerRow;
private int mNumAppRowsInAdapter;
private ItemInfoMatcher mItemFilter;
@@ -185,6 +158,7 @@
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
mIsWork = isWork;
+ mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
mAllAppsStore.addUpdateListener(this);
}
@@ -194,16 +168,6 @@
}
/**
- * Sets the number of apps per row.
- */
- public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
- mNumAppsPerRow = numAppsPerRow;
- mNumPredictedAppsPerRow = numPredictedAppsPerRow;
-
- updateAdapterItems();
- }
-
- /**
* Sets the adapter to notify when this dataset changes.
*/
public void setAdapter(AllAppsGridAdapter adapter) {
@@ -218,13 +182,6 @@
}
/**
- * Returns the predicted apps.
- */
- public List<AppInfo> getPredictedApps() {
- return mPredictedApps;
- }
-
- /**
* Returns fast scroller sections of all the current filtered applications.
*/
public List<FastScrollSectionInfo> getFastScrollerSections() {
@@ -239,7 +196,7 @@
}
/**
- * Returns the number of rows of applications (not including predictions)
+ * Returns the number of rows of applications
*/
public int getNumAppRows() {
return mNumAppRowsInAdapter;
@@ -279,80 +236,6 @@
return false;
}
- private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
- if (mAllAppsStore.getApps().isEmpty()) {
- // Apps have not been bound yet.
- return Collections.emptyList();
- }
-
- List<AppInfo> predictedApps = new ArrayList<>();
- for (ComponentKeyMapper<AppInfo> mapper : components) {
- AppInfo info = mAllAppsStore.getApp(mapper);
- if (info != null) {
- predictedApps.add(info);
- } else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Predicted app not found: " + mapper);
- }
- }
- // Stop at the number of predicted apps
- if (predictedApps.size() == mNumPredictedAppsPerRow) {
- break;
- }
- }
- return predictedApps;
- }
-
- /**
- * Sets the current set of predicted apps.
- *
- * This can be called before we get the full set of applications, we should merge the results
- * only in onAppsUpdated() which is idempotent.
- *
- * 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<AppInfo>> apps) {
- mPredictedAppComponents.clear();
- mPredictedAppComponents.addAll(apps);
-
- List<AppInfo> newPredictedApps = processPredictedAppComponents(apps);
- // We only need to do work if any of the visible predicted apps have changed.
- if (!newPredictedApps.equals(mPredictedApps)) {
- if (newPredictedApps.size() == mPredictedApps.size()) {
- swapInNewPredictedApps(newPredictedApps);
- } else {
- // We need to update the appIndex of all the items.
- onAppsUpdated();
- }
- }
- }
-
- /**
- * Swaps out the old predicted apps with the new predicted apps, in place. This optimization
- * allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged.
- *
- * Note: This should only be called if the # of predicted apps is the same.
- * This method assumes that predicted apps are the first items in the adapter.
- */
- private void swapInNewPredictedApps(List<AppInfo> apps) {
- mPredictedApps.clear();
- mPredictedApps.addAll(apps);
-
- int size = apps.size();
- for (int i = 0; i < size; ++i) {
- AppInfo info = apps.get(i);
- AdapterItem orgItem = mAdapterItems.get(i);
- AdapterItem newItem = AdapterItem.asPredictedApp(orgItem.position, "", info,
- orgItem.appIndex);
- newItem.rowAppIndex = orgItem.rowAppIndex;
-
- mAdapterItems.set(i, newItem);
- mFilteredApps.set(i, info);
- mAdapter.notifyItemChanged(i);
- }
- }
-
/**
* Updates internals when the set of apps are updated.
*/
@@ -433,47 +316,6 @@
mFastScrollerSections.clear();
mAdapterItems.clear();
- if (!FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
- if (DEBUG_PREDICTIONS) {
- if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- }
- }
-
- // Process the predicted app components
- mPredictedApps.clear();
- if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
- mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
-
- if (!mPredictedApps.isEmpty()) {
- // Add a section for the predictions
- lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
- mFastScrollerSections.add(lastFastScrollerSectionInfo);
-
- // Add the predicted app items
- for (AppInfo info : mPredictedApps) {
- AdapterItem appItem = AdapterItem.asPredictedApp(position++, "", info,
- appIndex++);
- if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
- lastFastScrollerSectionInfo.fastScrollToItem = appItem;
- }
- mAdapterItems.add(appItem);
- mFilteredApps.add(info);
- }
-
- mAdapterItems.add(AdapterItem.asPredictionDivider(position++));
- }
- }
-
- }
-
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
for (AppInfo info : getFiltersAppInfos()) {
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index d8a9f63..461f5b5 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Point;
@@ -27,9 +29,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
import com.android.launcher3.R;
+import com.android.launcher3.anim.PropertySetter;
public class FloatingHeaderView extends LinearLayout implements
ValueAnimator.AnimatorUpdateListener {
@@ -58,7 +60,7 @@
}
};
- private ViewGroup mTabLayout;
+ protected ViewGroup mTabLayout;
private AllAppsRecyclerView mMainRV;
private AllAppsRecyclerView mWorkRV;
private AllAppsRecyclerView mCurrentRV;
@@ -66,8 +68,11 @@
private boolean mHeaderCollapsed;
private int mSnappedScrolledY;
private int mTranslationY;
+
+ private boolean mAllowTouchForwarding;
private boolean mForwardToRecyclerView;
+ protected boolean mTabsHidden;
protected int mMaxTranslation;
public FloatingHeaderView(@NonNull Context context) {
@@ -85,12 +90,13 @@
}
public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
+ mTabsHidden = tabsHidden;
mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
mParent = (ViewGroup) mMainRV.getParent();
setMainActive(true);
- reset();
+ reset(false);
}
private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) {
@@ -105,7 +111,13 @@
}
public int getMaxTranslation() {
- return mMaxTranslation;
+ if (mMaxTranslation == 0 && mTabsHidden) {
+ return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
+ } else if (mMaxTranslation > 0 && mTabsHidden) {
+ return mMaxTranslation + getPaddingTop();
+ } else {
+ return mMaxTranslation;
+ }
}
private boolean canSnapAt(int currentScrollY) {
@@ -131,7 +143,7 @@
mSnappedScrolledY = currentScrollY - mMaxTranslation;
} else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden
mHeaderCollapsed = true;
- mSnappedScrolledY = currentScrollY;
+ mSnappedScrolledY = -mMaxTranslation;
}
}
}
@@ -151,12 +163,19 @@
}
}
- public void reset() {
- int translateTo = 0;
- mAnimator.setIntValues(mTranslationY, translateTo);
- mAnimator.addUpdateListener(this);
- mAnimator.setDuration(150);
- mAnimator.start();
+ public void reset(boolean animate) {
+ if (mAnimator.isStarted()) {
+ mAnimator.cancel();
+ }
+ if (animate) {
+ mAnimator.setIntValues(mTranslationY, 0);
+ mAnimator.addUpdateListener(this);
+ mAnimator.setDuration(150);
+ mAnimator.start();
+ } else {
+ mTranslationY = 0;
+ apply();
+ }
mHeaderCollapsed = false;
mSnappedScrolledY = -mMaxTranslation;
mCurrentRV.scrollToTop();
@@ -174,6 +193,10 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
+ if (!mAllowTouchForwarding) {
+ mForwardToRecyclerView = false;
+ return super.onInterceptTouchEvent(ev);
+ }
calcOffset(mTempOffset);
ev.offsetLocation(mTempOffset.x, mTempOffset.y);
mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev);
@@ -201,6 +224,19 @@
p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft();
p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
}
+
+ public void setContentVisibility(boolean hasHeader, boolean hasContent, PropertySetter setter) {
+ setter.setViewAlpha(this, hasContent ? 1 : 0, LINEAR);
+ allowTouchForwarding(hasContent);
+ }
+
+ protected void allowTouchForwarding(boolean allow) {
+ mAllowTouchForwarding = allow;
+ }
+
+ public boolean hasVisibleContent() {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/allapps/InterceptingViewPager.java b/src/com/android/launcher3/allapps/InterceptingViewPager.java
deleted file mode 100644
index 3524ca9..0000000
--- a/src/com/android/launcher3/allapps/InterceptingViewPager.java
+++ /dev/null
@@ -1,91 +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.allapps;
-
-import android.content.Context;
-import android.graphics.PointF;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.view.ViewPager;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.touch.SwipeDetector;
-
-import static android.view.MotionEvent.INVALID_POINTER_ID;
-
-
-public class InterceptingViewPager extends ViewPager {
-
-
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private final int mSlop;
- private int mActivePointerId = INVALID_POINTER_ID;
-
- public InterceptingViewPager(@NonNull Context context) {
- super(context);
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
- public InterceptingViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- boolean result = super.onInterceptTouchEvent(ev);
- if (!result) {
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- mActivePointerId = ev.getPointerId(0);
- mDownPos.set(ev.getX(), ev.getY());
- mLastPos.set(mDownPos);
- break;
- case MotionEvent.ACTION_POINTER_UP:
- int ptrIdx = ev.getActionIndex();
- int ptrId = ev.getPointerId(ptrIdx);
- if (ptrId == mActivePointerId) {
- final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
- mDownPos.set(
- ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
- ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
- mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
- mActivePointerId = ev.getPointerId(newPointerIdx);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- float deltaX = ev.getX() - mDownPos.x;
- float deltaY = ev.getY() - mDownPos.y;
- if (Math.abs(deltaX) > mSlop && Math.abs(deltaX) > Math.abs(deltaY)) {
- return true;
- }
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
- break;
- default:
- break;
- }
- }
- return result;
- }
-
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index 05cd655..a069d5d 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -25,16 +25,16 @@
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
-
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.util.Themes;
/**
* Supports two indicator colors, dedicated for personal and work tabs.
*/
-public class PersonalWorkSlidingTabStrip extends LinearLayout {
+public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageIndicator {
private static final int POSITION_PERSONAL = 0;
private static final int POSITION_WORK = 1;
@@ -47,10 +47,13 @@
private int mSelectedIndicatorHeight;
private int mIndicatorLeft = -1;
private int mIndicatorRight = -1;
- private int mIndicatorPosition = 0;
- private float mIndicatorOffset;
+ private float mScrollOffset;
private int mSelectedPosition = 0;
+ private AllAppsContainerView mContainerView;
+ private int mLastActivePage = 0;
+ private boolean mIsRtl;
+
public PersonalWorkSlidingTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setOrientation(HORIZONTAL);
@@ -69,15 +72,15 @@
getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
mSharedPreferences = Launcher.getLauncher(getContext()).getSharedPrefs();
+ mIsRtl = Utilities.isRtl(getResources());
}
- public void updateIndicatorPosition(int position, float positionOffset) {
- mIndicatorPosition = position;
- mIndicatorOffset = positionOffset;
+ private void updateIndicatorPosition(float scrollOffset) {
+ mScrollOffset = scrollOffset;
updateIndicatorPosition();
}
- public void updateTabTextColor(int pos) {
+ private void updateTabTextColor(int pos) {
mSelectedPosition = pos;
for (int i = 0; i < getChildCount(); i++) {
Button tab = (Button) getChildAt(i);
@@ -89,32 +92,23 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
updateTabTextColor(mSelectedPosition);
- updateIndicatorPosition(mIndicatorPosition, mIndicatorOffset);
+ updateIndicatorPosition(mScrollOffset);
}
private void updateIndicatorPosition() {
- final View tab = getChildAt(mIndicatorPosition);
- int left, right;
-
- if (tab != null && tab.getWidth() > 0) {
- left = tab.getLeft();
- right = tab.getRight();
-
- if (mIndicatorOffset > 0f && mIndicatorPosition < getChildCount() - 1) {
- // Draw the selection partway between the tabs
- View nextTitle = getChildAt(mIndicatorPosition + 1);
- left = (int) (mIndicatorOffset * nextTitle.getLeft() +
- (1.0f - mIndicatorOffset) * left);
- right = (int) (mIndicatorOffset * nextTitle.getRight() +
- (1.0f - mIndicatorOffset) * right);
- }
- } else {
- left = right = -1;
+ int left = -1, right = -1;
+ final View leftTab = getLeftTab();
+ if (leftTab != null) {
+ left = (int) (leftTab.getLeft() + leftTab.getWidth() * mScrollOffset);
+ right = left + leftTab.getWidth();
}
-
setIndicatorPosition(left, right);
}
+ private View getLeftTab() {
+ return mIsRtl ? getChildAt(1) : getChildAt(0);
+ }
+
private void setIndicatorPosition(int left, int right) {
if (left != mIndicatorLeft || right != mIndicatorRight) {
mIndicatorLeft = left;
@@ -129,8 +123,6 @@
float y = getHeight() - mDividerPaint.getStrokeWidth();
canvas.drawLine(getPaddingLeft(), y, getWidth() - getPaddingRight(), y, mDividerPaint);
-
- final float middleX = getWidth() / 2.0f;
canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
}
@@ -139,7 +131,7 @@
if (mSharedPreferences.getBoolean(KEY_SHOWED_PEEK_WORK_TAB, false)) {
return;
}
- if (mIndicatorPosition != POSITION_PERSONAL) {
+ if (mLastActivePage != POSITION_PERSONAL) {
return;
}
highlightWorkTab();
@@ -153,4 +145,32 @@
v.setPressed(false);
});
}
+
+ @Override
+ public void setScroll(int currentScroll, int totalScroll) {
+ float scrollOffset = ((float) currentScroll) / totalScroll;
+ updateIndicatorPosition(scrollOffset);
+ }
+
+ @Override
+ public void setActiveMarker(int activePage) {
+ updateTabTextColor(activePage);
+ if (mContainerView != null && mLastActivePage != activePage) {
+ mContainerView.onTabChanged(activePage);
+ }
+ mLastActivePage = activePage;
+ }
+
+ public void setContainerView(AllAppsContainerView containerView) {
+ mContainerView = containerView;
+ }
+
+ @Override
+ public void setMarkersCount(int numMarkers) { }
+
+ @Override
+ public void setPageDescription(CharSequence description) {
+ // We don't want custom page description as the tab-bar already has two tabs with their
+ // own descriptions.
+ }
}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index bb17ed5..68193f5 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,8 +15,6 @@
*/
package com.android.launcher3.allapps;
-import android.support.animation.SpringAnimation;
-import android.support.annotation.NonNull;
import android.view.KeyEvent;
/**
@@ -30,28 +28,13 @@
void initialize(AllAppsContainerView containerView);
/**
- * A {@link SpringAnimation} that will be used when the user flings.
- */
- @NonNull SpringAnimation getSpringForFling();
-
- /**
* Notifies the search manager to close any active search session.
*/
- void reset();
+ void resetSearch();
/**
* Called before dispatching a key event, in case the search manager wants to initialize
* some UI beforehand.
*/
void preDispatchKeyEvent(KeyEvent keyEvent);
-
- void addOnScrollRangeChangeListener(OnScrollRangeChangeListener listener);
-
- /**
- * Callback for listening to changes in the vertical scroll range when opening all-apps.
- */
- interface OnScrollRangeChangeListener {
-
- void onScrollRangeChanged(int scrollRange);
- }
}
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index a56c8b8..ad61c55 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -15,12 +15,14 @@
*/
package com.android.launcher3.allapps.search;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.getSize;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.graphics.IconNormalizer.ICON_VISIBLE_AREA_FACTOR;
+
import android.content.Context;
import android.graphics.Rect;
-import android.support.animation.FloatValueHolder;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.NonNull;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
@@ -29,10 +31,11 @@
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.View;
-import android.widget.FrameLayout;
+import android.view.ViewGroup.MarginLayoutParams;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -47,22 +50,17 @@
/**
* Layout to contain the All-apps search UI.
*/
-public class AppsSearchContainerLayout extends FrameLayout
+public class AppsSearchContainerLayout extends ExtendedEditText
implements SearchUiManager, AllAppsSearchBarController.Callbacks,
- AllAppsStore.OnUpdateListener {
+ AllAppsStore.OnUpdateListener, Insettable {
+
private final Launcher mLauncher;
- private final int mMinHeight;
- private final int mSearchBoxHeight;
private final AllAppsSearchBarController mSearchBarController;
private final SpannableStringBuilder mSearchQueryBuilder;
- private ExtendedEditText mSearchInput;
private AlphabeticalAppsList mApps;
- private View mDivider;
- private HeaderElevationController mElevationController;
private AllAppsContainerView mAppsView;
- private SpringAnimation mSpring;
public AppsSearchContainerLayout(Context context) {
this(context, null);
@@ -76,39 +74,19 @@
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
- mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
- mSearchBoxHeight = getResources()
- .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
mSearchBarController = new AllAppsSearchBarController();
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
- // Note: This spring does nothing.
- mSpring = new SpringAnimation(new FloatValueHolder()).setSpring(new SpringForce(0));
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mSearchInput = findViewById(R.id.search_box_input);
- mDivider = findViewById(R.id.search_divider);
- mElevationController = new HeaderElevationController(mDivider);
-
// Update the hint to contain the icon.
// Prefix the original hint with two spaces. The first space gets replaced by the icon
// using span. The second space is used for a singe space character between the hint
// and the icon.
- SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
+ SpannableString spanned = new SpannableString(" " + getHint());
spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
- mSearchInput.setHint(spanned);
-
- DeviceProfile dp = mLauncher.getDeviceProfile();
- if (!dp.isVerticalBarLayout()) {
- LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
- lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
- }
+ setHint(spanned);
}
@Override
@@ -125,26 +103,39 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Update the width to match the grid padding
DeviceProfile dp = mLauncher.getDeviceProfile();
- if (!dp.isVerticalBarLayout()) {
- getLayoutParams().height = dp.getInsets().top + mMinHeight;
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int myRequestedWidth = getSize(widthMeasureSpec);
+ int rowWidth = myRequestedWidth - mAppsView.getActiveRecyclerView().getPaddingLeft()
+ - mAppsView.getActiveRecyclerView().getPaddingRight();
+
+ int cellWidth = DeviceProfile.calculateCellWidth(rowWidth, dp.inv.numHotseatIcons);
+ int iconVisibleSize = Math.round(ICON_VISIBLE_AREA_FACTOR * dp.iconSizePx);
+ int iconPadding = cellWidth - iconVisibleSize;
+
+ int myWidth = rowWidth - iconPadding + getPaddingLeft() + getPaddingRight();
+ super.onMeasure(makeMeasureSpec(myWidth, EXACTLY), heightMeasureSpec);
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // Shift the widget horizontally so that its centered in the parent (b/63428078)
+ View parent = (View) getParent();
+ int availableWidth = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight();
+ int myWidth = right - left;
+ int expectedLeft = parent.getPaddingLeft() + (availableWidth - myWidth) / 2;
+ int shift = expectedLeft - left;
+ setTranslationX(shift);
+ }
@Override
public void initialize(AllAppsContainerView appsView) {
mApps = appsView.getApps();
mAppsView = appsView;
- appsView.addElevationController(mElevationController);
mSearchBarController.initialize(
- new DefaultAppSearchAlgorithm(mApps.getApps()), mSearchInput, mLauncher, this);
- }
-
- @Override
- public @NonNull SpringAnimation getSpringForFling() {
- return mSpring;
+ new DefaultAppSearchAlgorithm(mApps.getApps()), this, mLauncher, this);
}
@Override
@@ -153,8 +144,7 @@
}
@Override
- public void reset() {
- mElevationController.reset();
+ public void resetSearch() {
mSearchBarController.reset();
}
@@ -200,27 +190,19 @@
}
private void notifyResultChanged() {
- mElevationController.reset();
mAppsView.onSearchResultsChanged();
}
@Override
- public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) {
- mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- DeviceProfile dp = mLauncher.getDeviceProfile();
- if (!dp.isVerticalBarLayout()) {
- Rect insets = dp.getInsets();
- int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
- int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight)
- + ((MarginLayoutParams) getLayoutParams()).bottomMargin;
- listener.onScrollRangeChanged(hotseatBottom - searchTopMargin);
- } else {
- listener.onScrollRangeChanged(bottom);
- }
- }
- });
+ public void setInsets(Rect insets) {
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ if (dp.isVerticalBarLayout()) {
+ mLauncher.getAllAppsController().setScrollRangeDelta(0);
+ } else {
+ MarginLayoutParams mlp = ((MarginLayoutParams) getLayoutParams());
+ int myBot = mlp.topMargin + (int) getTranslationY() + mlp.height;
+ mLauncher.getAllAppsController().setScrollRangeDelta(
+ dp.hotseatBarBottomPaddingPx + myBot);
+ }
}
}
diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
deleted file mode 100644
index 7cd32b2..0000000
--- a/src/com/android/launcher3/allapps/search/HeaderElevationController.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.android.launcher3.allapps.search;
-
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
-
-/**
- * Helper class for controlling the header elevation in response to RecyclerView scroll.
- */
-public class HeaderElevationController extends RecyclerView.OnScrollListener {
-
- private final View mHeader;
- private final View mHeaderChild;
- private final float mMaxElevation;
- private final float mScrollToElevation;
-
- private int mCurrentY = 0;
-
- public HeaderElevationController(View header) {
- mHeader = header;
- final Resources res = mHeader.getContext().getResources();
- mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
- mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
-
- // We need to provide a custom outline so the shadow only appears on the bottom edge.
- // The top, left and right edges are all extended out to match parent's edge, so that
- // the shadow is clipped by the parent.
- final ViewOutlineProvider vop = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- // Set the left and top to be at the parents edge. Since the coordinates are
- // relative to this view,
- // (x = -view.getLeft()) for this view => (x = 0) for parent
- final int left = -view.getLeft();
- final int top = -view.getTop();
-
- // Since the view is centered align, the spacing on left and right are same.
- // Add same spacing on the right to reach parent's edge.
- final int right = view.getWidth() - left;
- final int bottom = view.getHeight();
- final int offset = (int) mMaxElevation;
- outline.setRect(left - offset, top - offset, right + offset, bottom);
- }
- };
- mHeader.setOutlineProvider(vop);
- mHeaderChild = ((ViewGroup) mHeader).getChildAt(0);
- }
-
- public void reset() {
- mCurrentY = 0;
- onScroll(mCurrentY);
- }
-
- @Override
- public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
- onScroll(mCurrentY);
- }
-
- private void onScroll(int scrollY) {
- float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
- float newElevation = mMaxElevation * elevationPct;
- if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
- mHeader.setElevation(newElevation);
-
- // To simulate a scrolling effect for the header, we translate the header down, and
- // its content up by the same amount, so that it gets clipped by the parent, making it
- // look like the content was scrolled out of the view.
- int shift = Math.min(mHeader.getHeight(), scrollY);
- mHeader.setTranslationY(-shift);
- mHeaderChild.setTranslationY(shift);
- }
- }
-
-}
diff --git a/src/com/android/launcher3/anim/AlphaUpdateListener.java b/src/com/android/launcher3/anim/AlphaUpdateListener.java
new file mode 100644
index 0000000..a3d02d9
--- /dev/null
+++ b/src/com/android/launcher3/anim/AlphaUpdateListener.java
@@ -0,0 +1,61 @@
+/*
+ * 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.anim;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.view.View;
+
+/**
+ * A convenience class to update a view's visibility state after an alpha animation.
+ */
+public class AlphaUpdateListener extends AnimationSuccessListener
+ implements AnimatorUpdateListener {
+ private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
+
+ private View mView;
+
+ public AlphaUpdateListener(View v) {
+ mView = v;
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator arg0) {
+ updateVisibility(mView);
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ updateVisibility(mView);
+ }
+
+ @Override
+ public void onAnimationStart(Animator arg0) {
+ // We want the views to be visible for animation, so fade-in/out is visible
+ mView.setVisibility(View.VISIBLE);
+ }
+
+ public static void updateVisibility(View view) {
+ if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != View.INVISIBLE) {
+ view.setVisibility(View.INVISIBLE);
+ } else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
+ && view.getVisibility() != View.VISIBLE) {
+ view.setVisibility(View.VISIBLE);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 68e9847..087752d 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -18,6 +18,7 @@
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
+import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import java.util.ArrayList;
@@ -184,10 +185,14 @@
private void getAnimationsRecur(AnimatorSet anim, ArrayList<ValueAnimator> out) {
long forceDuration = anim.getDuration();
+ TimeInterpolator forceInterpolator = anim.getInterpolator();
for (Animator child : anim.getChildAnimations()) {
if (forceDuration > 0) {
child.setDuration(forceDuration);
}
+ if (forceInterpolator != null) {
+ child.setInterpolator(forceInterpolator);
+ }
if (child instanceof ValueAnimator) {
out.add((ValueAnimator) child);
} else if (child instanceof AnimatorSet) {
@@ -206,7 +211,6 @@
anim.setCurrentPlayTime(Math.min(playPos, anim.getDuration()));
}
}
-
}
private class OnAnimationEndDispatcher extends AnimationSuccessListener {
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index 7cd9651..b209a2d 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -16,19 +16,28 @@
package com.android.launcher3.anim;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.util.SparseArray;
+import android.view.animation.Interpolator;
import com.android.launcher3.LauncherAnimUtils;
import java.util.ArrayList;
+import java.util.List;
/**
* Utility class for building animator set
*/
public class AnimatorSetBuilder {
+ public static final int ANIM_VERTICAL_PROGRESS = 0;
+ public static final int ANIM_OVERVIEW_TRANSLATION = 1;
+
protected final ArrayList<Animator> mAnims = new ArrayList<>();
- private long mStartDelay = 0;
+
+ private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
+ private List<Runnable> mOnFinishRunnables = new ArrayList<>();
/**
* Associates a tag with all the animations added after this call.
@@ -39,14 +48,32 @@
mAnims.add(anim);
}
- public void setStartDelay(long startDelay) {
- mStartDelay = startDelay;
+ public void addOnFinishRunnable(Runnable onFinishRunnable) {
+ mOnFinishRunnables.add(onFinishRunnable);
}
public AnimatorSet build() {
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
anim.playTogether(mAnims);
- anim.setStartDelay(mStartDelay);
+ if (!mOnFinishRunnables.isEmpty()) {
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ for (Runnable onFinishRunnable : mOnFinishRunnables) {
+ onFinishRunnable.run();
+ }
+ mOnFinishRunnables.clear();
+ }
+ });
+ }
return anim;
}
+
+ public Interpolator getInterpolator(int animId, Interpolator fallback) {
+ return mInterpolators.get(animId, fallback);
+ }
+
+ public void setInterpolator(int animId, Interpolator interpolator) {
+ mInterpolators.put(animId, interpolator);
+ }
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 2343654..06ddf22 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -16,6 +16,7 @@
package com.android.launcher3.anim;
+import android.graphics.Path;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -44,22 +45,42 @@
public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f);
- public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.8f,0, 0.4f, 1);
+ public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1);
- public static final Interpolator APP_CLOSE_ALPHA = new PathInterpolator(0.9f, 0, 1f, 1f);
+ public static final Interpolator EXAGGERATED_EASE;
+
+ static {
+ Path exaggeratedEase = new Path();
+ exaggeratedEase.moveTo(0, 0);
+ exaggeratedEase.cubicTo(0.05f, 0f, 0.133333f, 0.08f, 0.166666f, 0.4f);
+ exaggeratedEase.cubicTo(0.225f, 0.94f, 0.5f, 1f, 1f, 1f);
+ EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
+ }
+
+ public static final Interpolator APP_CLOSE_ALPHA = new PathInterpolator(0.4f, 0, 1f, 1f);
public static final Interpolator OVERSHOOT_0 = new OvershootInterpolator(0);
+ public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
/**
- * Inversion of zInterpolate, compounded with an ease-out.
+ * Inversion of ZOOM_OUT, compounded with an ease-out.
*/
public static final Interpolator ZOOM_IN = new Interpolator() {
+ @Override
+ public float getInterpolation(float v) {
+ return DEACCEL_3.getInterpolation(1 - ZOOM_OUT.getInterpolation(1 - v));
+ }
+ };
+
+ public static final Interpolator ZOOM_OUT = new Interpolator() {
private static final float FOCAL_LENGTH = 0.35f;
@Override
public float getInterpolation(float v) {
- return DEACCEL_3.getInterpolation(1 - zInterpolate(1 - v));
+ return zInterpolate(v);
}
/**
diff --git a/src/com/android/launcher3/anim/PropertySetter.java b/src/com/android/launcher3/anim/PropertySetter.java
new file mode 100644
index 0000000..757edff
--- /dev/null
+++ b/src/com/android/launcher3/anim/PropertySetter.java
@@ -0,0 +1,92 @@
+/*
+ * 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.anim;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.util.Property;
+import android.view.View;
+
+/**
+ * Utility class for setting a property with or without animation
+ */
+public class PropertySetter {
+
+ public static final PropertySetter NO_ANIM_PROPERTY_SETTER = new PropertySetter();
+
+ public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ if (view != null) {
+ view.setAlpha(alpha);
+ AlphaUpdateListener.updateVisibility(view);
+ }
+ }
+
+ public <T> void setFloat(T target, Property<T, Float> property, float value,
+ TimeInterpolator interpolator) {
+ property.set(target, value);
+ }
+
+ public <T> void setInt(T target, Property<T, Integer> property, int value,
+ TimeInterpolator interpolator) {
+ property.set(target, value);
+ }
+
+ public static class AnimatedPropertySetter extends PropertySetter {
+
+ private final long mDuration;
+ private final AnimatorSetBuilder mStateAnimator;
+
+ public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) {
+ mDuration = duration;
+ mStateAnimator = builder;
+ }
+
+ @Override
+ public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ if (view == null || view.getAlpha() == alpha) {
+ return;
+ }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
+ anim.addListener(new AlphaUpdateListener(view));
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ mStateAnimator.play(anim);
+ }
+
+ @Override
+ public <T> void setFloat(T target, Property<T, Float> property, float value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return;
+ }
+ Animator anim = ObjectAnimator.ofFloat(target, property, value);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ mStateAnimator.play(anim);
+ }
+
+ @Override
+ public <T> void setInt(T target, Property<T, Integer> property, int value,
+ TimeInterpolator interpolator) {
+ if (property.get(target) == value) {
+ return;
+ }
+ Animator anim = ObjectAnimator.ofInt(target, property, value);
+ anim.setDuration(mDuration).setInterpolator(interpolator);
+ mStateAnimator.play(anim);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
deleted file mode 100644
index 29a2430..0000000
--- a/src/com/android/launcher3/anim/SpringAnimationHandler.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.anim;
-
-import android.support.animation.FloatPropertyCompat;
-import android.support.animation.SpringAnimation;
-import android.support.animation.SpringForce;
-import android.support.annotation.IntDef;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-
-import com.android.launcher3.R;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-
-/**
- * Handler class that manages springs for a set of views that should all move based on the same
- * {@link MotionEvent}s.
- *
- * Supports setting either X or Y velocity on the list of springs added to this handler.
- */
-public class SpringAnimationHandler<T> {
-
- private static final String TAG = "SpringAnimationHandler";
- private static final boolean DEBUG = false;
-
- private static final float VELOCITY_DAMPING_FACTOR = 0.175f;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({Y_DIRECTION, X_DIRECTION})
- public @interface Direction {}
- public static final int Y_DIRECTION = 0;
- public static final int X_DIRECTION = 1;
- private int mVelocityDirection;
-
- private VelocityTracker mVelocityTracker;
- private float mCurrentVelocity = 0;
- private boolean mShouldComputeVelocity = false;
-
- private AnimationFactory<T> mAnimationFactory;
-
- private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
-
- /**
- * @param direction Either {@link #X_DIRECTION} or {@link #Y_DIRECTION}.
- * Determines which direction we use to calculate and set the velocity.
- * @param factory The AnimationFactory is responsible for initializing and updating the
- * SpringAnimations added to this class.
- */
- public SpringAnimationHandler(@Direction int direction, AnimationFactory<T> factory) {
- mVelocityDirection = direction;
- mAnimationFactory = factory;
- }
-
- /**
- * Adds a spring to the list of springs handled by this class.
- * @param spring The new spring to be added.
- * @param setDefaultValues If True, sets the spring to the default
- * {@link AnimationFactory} values.
- */
- public void add(SpringAnimation spring, boolean setDefaultValues) {
- if (setDefaultValues) {
- mAnimationFactory.setDefaultValues(spring);
- }
- spring.setStartVelocity(mCurrentVelocity);
- mAnimations.add(spring);
- }
-
- public AnimationFactory<T> getFactory() {
- return mAnimationFactory;
- }
-
- /**
- * Adds a new or recycled animation to the list of springs handled by this class.
- *
- * @param view The view the spring is attached to.
- * @param object Used to initialize and update the spring.
- */
- public void add(View view, T object) {
- SpringAnimation spring = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
- if (spring == null) {
- spring = mAnimationFactory.initialize(object);
- view.setTag(R.id.spring_animation_tag, spring);
- }
- mAnimationFactory.update(spring, object);
- add(spring, false /* setDefaultValues */);
- }
-
- /**
- * Stops and removes the spring attached to {@param view}.
- */
- public void remove(View view) {
- remove((SpringAnimation) view.getTag(R.id.spring_animation_tag));
- }
-
- public void remove(SpringAnimation animation) {
- if (animation.canSkipToEnd()) {
- animation.skipToEnd();
- }
- mAnimations.remove(animation);
- }
-
- public void addMovement(MotionEvent event) {
- int action = event.getActionMasked();
- if (DEBUG) Log.d(TAG, "addMovement#action=" + action);
- switch (action) {
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_DOWN:
- reset();
- break;
- }
-
- getVelocityTracker().addMovement(event);
- mShouldComputeVelocity = true;
- }
-
- public void animateToFinalPosition(float position, int startValue) {
- animateToFinalPosition(position, startValue, mShouldComputeVelocity);
- }
-
- /**
- * @param position The final animation position.
- * @param startValue < 0 if scrolling from start to end; > 0 if scrolling from end to start
- * The magnitude of the number changes how the spring will move.
- * @param setVelocity If true, we set the velocity to {@link #mCurrentVelocity} before
- * starting the animation.
- */
- private void animateToFinalPosition(float position, int startValue, boolean setVelocity) {
- if (DEBUG) {
- Log.d(TAG, "animateToFinalPosition#position=" + position + ", startValue=" + startValue);
- }
-
- if (mShouldComputeVelocity) {
- mCurrentVelocity = computeVelocity();
- }
-
- int size = mAnimations.size();
- for (int i = 0; i < size; ++i) {
- mAnimations.get(i).setStartValue(startValue);
- if (setVelocity) {
- mAnimations.get(i).setStartVelocity(mCurrentVelocity);
- }
- mAnimations.get(i).animateToFinalPosition(position);
- }
-
- reset();
- }
-
- /**
- * Similar to {@link #animateToFinalPosition(float, int)}, but used in cases where we want to
- * manually set the velocity.
- */
- public void animateToPositionWithVelocity(float position, int startValue, float velocity) {
- if (DEBUG) {
- Log.d(TAG, "animateToPosition#pos=" + position + ", start=" + startValue
- + ", velocity=" + velocity);
- }
-
- mCurrentVelocity = velocity;
- mShouldComputeVelocity = false;
- animateToFinalPosition(position, startValue, true);
- }
-
-
- public boolean isRunning() {
- // All the animations run at the same time so we can just check the first one.
- return !mAnimations.isEmpty() && mAnimations.get(0).isRunning();
- }
-
- public void skipToEnd() {
- if (DEBUG) Log.d(TAG, "setStartVelocity#skipToEnd");
- if (DEBUG) Log.v(TAG, "setStartVelocity#skipToEnd", new Exception());
-
- int size = mAnimations.size();
- for (int i = 0; i < size; ++i) {
- if (mAnimations.get(i).canSkipToEnd()) {
- mAnimations.get(i).skipToEnd();
- }
- }
- }
-
- public void reset() {
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mCurrentVelocity = 0;
- mShouldComputeVelocity = false;
- }
-
-
- private float computeVelocity() {
- getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
-
- float velocity = isVerticalDirection()
- ? getVelocityTracker().getYVelocity()
- : getVelocityTracker().getXVelocity();
- velocity *= VELOCITY_DAMPING_FACTOR;
-
- if (DEBUG) Log.d(TAG, "computeVelocity=" + velocity);
- return velocity;
- }
-
- private boolean isVerticalDirection() {
- return mVelocityDirection == Y_DIRECTION;
- }
-
- private VelocityTracker getVelocityTracker() {
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- return mVelocityTracker;
- }
-
- /**
- * This interface is used to initialize and update the SpringAnimations added to the
- * {@link SpringAnimationHandler}.
- *
- * @param <T> The object that each SpringAnimation is attached to.
- */
- public interface AnimationFactory<T> {
-
- /**
- * Initializes a new Spring for {@param object}.
- */
- SpringAnimation initialize(T object);
-
- /**
- * Updates the value of {@param spring} based on {@param object}.
- */
- void update(SpringAnimation spring, T object);
-
- /**
- * Sets the factory default values for the given {@param spring}.
- */
- void setDefaultValues(SpringAnimation spring);
- }
-
- /**
- * Helper method to create a new SpringAnimation for {@param view}.
- */
- public static SpringAnimation forView(View view, FloatPropertyCompat property, float finalPos) {
- SpringAnimation spring = new SpringAnimation(view, property, finalPos);
- spring.setSpring(new SpringForce(finalPos));
- return spring;
- }
-
-}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 72d49f0..9487427 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -80,7 +80,7 @@
Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
return;
}
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.save();
// We draw the badge relative to its center.
float badgeCenterX = iconBounds.right - mDotCenterOffset / 2;
float badgeCenterY = iconBounds.top + mDotCenterOffset / 2;
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index 112cca5..3270ba2 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -45,7 +45,7 @@
/**
* @return a map of active installs to their progress
*/
- public abstract HashMap<String, Integer> updateAndGetActiveSessionCache();
+ public abstract HashMap<String, PackageInstaller.SessionInfo> updateAndGetActiveSessionCache();
public abstract void onStop();
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 1ffd3da..dd17916 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -59,13 +59,13 @@
}
@Override
- public HashMap<String, Integer> updateAndGetActiveSessionCache() {
- HashMap<String, Integer> activePackages = new HashMap<>();
+ public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
+ HashMap<String, SessionInfo> activePackages = new HashMap<>();
UserHandle user = Process.myUserHandle();
for (SessionInfo info : getAllVerifiedSessions()) {
addSessionInfoToCache(info, user);
if (info.getAppPackageName() != null) {
- activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
+ activePackages.put(info.getAppPackageName(), info);
mActiveSessions.put(info.getSessionId(), info.getAppPackageName());
}
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 62055dc..03e3861 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -58,7 +58,6 @@
public abstract long getSerialNumberForUser(UserHandle user);
public abstract UserHandle getUserForSerialNumber(long serialNumber);
public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandle user);
- public abstract long getUserCreationTime(UserHandle user);
public abstract boolean isQuietModeEnabled(UserHandle user);
public abstract boolean isUserUnlocked(UserHandle user);
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index e57786d..1ff6981 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -23,7 +23,6 @@
import android.os.UserManager;
import android.util.ArrayMap;
import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.ManagedProfileHeuristic;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -127,15 +126,5 @@
}
return mPm.getUserBadgedLabel(label, user);
}
-
- @Override
- public long getUserCreationTime(UserHandle user) {
- SharedPreferences prefs = ManagedProfileHeuristic.prefs(mContext);
- String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
- if (!prefs.contains(key)) {
- prefs.edit().putLong(key, System.currentTimeMillis()).apply();
- }
- return prefs.getLong(key, 0);
- }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVM.java b/src/com/android/launcher3/compat/UserManagerCompatVM.java
index 75c1877..cf74b37 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVM.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVM.java
@@ -27,9 +27,4 @@
UserManagerCompatVM(Context context) {
super(context);
}
-
- @Override
- public long getUserCreationTime(UserHandle user) {
- return mUserManager.getUserCreationTime(user);
- }
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 059b04e..f4c6380 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -35,8 +35,6 @@
public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled the promise icon is visible in all apps while installation an app.
public static final boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
- // When enabled allows use of physics based motions in the Launcher.
- public static final boolean LAUNCHER3_PHYSICS = true;
// When enabled allows use of spring motions on the icons.
public static final boolean LAUNCHER3_SPRING_ICONS = true;
@@ -53,8 +51,8 @@
// When enabled shows a work profile tab in all apps
public static final boolean ALL_APPS_TABS_ENABLED = true;
- // When enabled prediction row is rendered as it's own custom view
- public static final boolean ALL_APPS_PREDICTION_ROW_VIEW = true;
- public static final boolean ENABLE_TWO_SWIPE_TARGETS = true;
+ // When true, overview shows screenshots in the orientation they were taken rather than
+ // trying to make them fit the orientation the device is in.
+ public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
}
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index db199c1..278eefd 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -16,6 +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 android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.appwidget.AppWidgetManager;
@@ -23,7 +28,6 @@
import android.content.ClipDescription;
import android.content.Intent;
import android.content.pm.LauncherApps.PinItemRequest;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.PointF;
@@ -43,23 +47,18 @@
import com.android.launcher3.LauncherAppWidgetHost;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
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.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
-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;
-
@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
@@ -84,6 +83,7 @@
private Bundle mWidgetOptions;
private boolean mFinishOnPause = false;
+ private InstantAppResolver mInstantAppResolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -97,6 +97,7 @@
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.
@@ -154,15 +155,7 @@
.setPackage(getPackageName())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- if (!getResources().getBoolean(R.bool.allow_rotation) &&
- !Utilities.isAllowRotationPrefEnabled(this) &&
- (getResources().getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE && !isInMultiWindowMode())) {
- // If we are starting the drag in landscape even though home is locked in portrait,
- // restart the home activity to temporarily allow rotation.
- homeIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
- }
-
+ listener.initWhenReady();
startActivity(homeIntent,
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
mFinishOnPause = true;
@@ -308,7 +301,7 @@
private void logCommand(int command) {
getUserEventDispatcher().dispatchUserEvent(newLauncherEvent(
newCommandAction(command),
- newItemTarget(mWidgetCell.getWidgetView()),
+ newItemTarget(mWidgetCell.getWidgetView(), mInstantAppResolver),
newContainerTarget(ContainerType.PINITEM)), null);
}
}
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 9638a75..df4a7c1 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -17,6 +17,8 @@
package com.android.launcher3.dragndrop;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
+import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.content.ClipDescription;
import android.content.Intent;
@@ -79,6 +81,7 @@
AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */);
launcher.getDragLayer().setOnDragListener(this);
+ launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
mLauncher = launcher;
mDragController = launcher.getDragController();
@@ -157,6 +160,7 @@
}
private void postCleanup() {
+ clearReference();
if (mLauncher != null) {
// Remove any drag params from the launcher intent since the drag operation is complete.
Intent newIntent = new Intent(mLauncher.getIntent());
@@ -164,16 +168,12 @@
mLauncher.setIntent(newIntent);
}
- new Handler(Looper.getMainLooper()).post(new Runnable() {
- @Override
- public void run() {
- removeListener();
- }
- });
+ new Handler(Looper.getMainLooper()).post(this::removeListener);
}
public void removeListener() {
if (mLauncher != null) {
+ mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
mLauncher.getDragLayer().setOnDragListener(null);
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 818cea7..5c6946c 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -583,6 +583,12 @@
}
mDragObject.dragComplete = true;
+ if (mIsInPreDrag) {
+ if (dropTarget != null) {
+ dropTarget.onDragExit(mDragObject);
+ }
+ return;
+ }
// Drop onto the target.
boolean accepted = false;
@@ -591,17 +597,15 @@
if (dropTarget.acceptDrop(mDragObject)) {
if (flingAnimation != null) {
flingAnimation.run();
- } else if (!mIsInPreDrag) {
+ } else {
dropTarget.onDrop(mDragObject, mOptions);
}
accepted = true;
}
}
final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
- if (!mIsInPreDrag) {
- mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
- dispatchDropComplete(dropTargetAsView, accepted);
- }
+ mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
+ dispatchDropComplete(dropTargetAsView, accepted);
}
private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index a32f6b1..8519365 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -27,50 +27,42 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DropTargetBar;
-import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
-import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.ViewScrim;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
/**
* A ViewGroup that coordinates dragging across its descendants
*/
-public class DragLayer extends InsettableFrameLayout {
+public class DragLayer extends BaseDragLayer<Launcher> {
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
- private final int[] mTmpXY = new int[2];
-
@Thunk DragController mDragController;
- private Launcher mLauncher;
-
// Variables relating to animation of views after drop
private ValueAnimator mDropAnim = null;
private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5;
@@ -79,19 +71,13 @@
@Thunk View mAnchorView = null;
private boolean mHoverPointClosesFolder = false;
- private final Rect mHitRect = new Rect();
-
- private TouchCompleteListener mTouchCompleteListener;
private int mTopViewIndex;
private int mChildCountOnLastUpdate = -1;
// Related to adjacent page hints
private final ViewGroupFocusHelper mFocusIndicatorHelper;
- private final PageCutOutScrimDrawable mPageCutOutScrim;
- protected TouchController[] mControllers;
- private TouchController mActiveController;
/**
* Used to create a new DragLayer from XML.
*
@@ -106,14 +92,11 @@
setChildrenDrawingOrderEnabled(true);
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
- mPageCutOutScrim = new PageCutOutScrimDrawable(this);
- mPageCutOutScrim.setCallback(this);
}
- public void setup(Launcher launcher, DragController dragController) {
- mLauncher = launcher;
+ public void setup(DragController dragController) {
mDragController = dragController;
- mControllers = UiFactory.createTouchControllers(mLauncher);
+ mControllers = UiFactory.createTouchControllers(mActivity);
}
public ViewGroupFocusHelper getFocusIndicatorHelper() {
@@ -125,13 +108,8 @@
return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
}
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return super.verifyDrawable(who) || who == mPageCutOutScrim;
- }
-
public boolean isEventOverHotseat(MotionEvent ev) {
- return isEventOverView(mLauncher.getHotseat(), ev);
+ return isEventOverView(mActivity.getHotseat(), ev);
}
private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
@@ -139,66 +117,46 @@
}
private boolean isEventOverDropTargetBar(MotionEvent ev) {
- return isEventOverView(mLauncher.getDropTargetBar(), ev);
- }
-
- public boolean isEventOverView(View view, MotionEvent ev) {
- getDescendantRectRelativeToSelf(view, mHitRect);
- return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+ return isEventOverView(mActivity.getDropTargetBar(), ev);
}
@Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- if (mTouchCompleteListener != null) {
- mTouchCompleteListener.onTouchComplete();
- }
- mTouchCompleteListener = null;
- } else if (action == MotionEvent.ACTION_DOWN) {
- mLauncher.finishAutoCancelActionMode();
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ ViewScrim scrim = ViewScrim.get(child);
+ if (scrim != null) {
+ scrim.draw(canvas, getWidth(), getHeight());
}
- mActiveController = null;
+ return super.drawChild(canvas, child, drawingTime);
+ }
- AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
- mActiveController = topView;
+ @Override
+ protected boolean findActiveController(MotionEvent ev) {
+ if (mActivity.getStateManager().getState().disableInteraction) {
+ // You Shall Not Pass!!!
+ mActiveController = null;
return true;
}
-
- if (mDragController.onControllerInterceptTouchEvent(ev)) {
- mActiveController = mDragController;
- return true;
- }
-
- for (TouchController controller : mControllers) {
- if (controller.onControllerInterceptTouchEvent(ev)) {
- mActiveController = controller;
- return true;
- }
- }
- return false;
+ return super.findActiveController(ev);
}
@Override
public boolean onInterceptHoverEvent(MotionEvent ev) {
- if (mLauncher == null || mLauncher.getWorkspace() == null) {
+ if (mActivity == null || mActivity.getWorkspace() == null) {
return false;
}
- Folder currentFolder = Folder.getOpen(mLauncher);
+ Folder currentFolder = Folder.getOpen(mActivity);
if (currentFolder == null) {
return false;
} else {
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ AccessibilityManager accessibilityManager = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager.isTouchExplorationEnabled()) {
final int action = ev.getAction();
boolean isOverFolderOrSearchBar;
switch (action) {
case MotionEvent.ACTION_HOVER_ENTER:
isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
- (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
if (!isOverFolderOrSearchBar) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
@@ -208,7 +166,7 @@
break;
case MotionEvent.ACTION_HOVER_MOVE:
isOverFolderOrSearchBar = isEventOverFolder(currentFolder, ev) ||
- (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
+ (isInAccessibleDrag() && isEventOverDropTargetBar(ev));
if (!isOverFolderOrSearchBar && !mHoverPointClosesFolder) {
sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
mHoverPointClosesFolder = true;
@@ -229,14 +187,22 @@
this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
}
+ @Override
+ public boolean onHoverEvent(MotionEvent ev) {
+ // If we've received this, we've already done the necessary handling
+ // in onInterceptHoverEvent. Return true to consume the event.
+ return false;
+ }
+
+
private boolean isInAccessibleDrag() {
- return mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
+ return mActivity.getAccessibilityDelegate().isInAccessibleDrag();
}
@Override
public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
// Shortcuts can appear above folder
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) {
if (child == topView) {
return super.onRequestSendAccessibilityEvent(child, event);
@@ -253,13 +219,13 @@
@Override
public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
if (topView != null) {
// Only add the top view as a child for accessibility when it is open
childrenForAccessibility.add(topView);
if (isInAccessibleDrag()) {
- childrenForAccessibility.add(mLauncher.getDropTargetBar());
+ childrenForAccessibility.add(mActivity.getDropTargetBar());
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
@@ -267,101 +233,9 @@
}
@Override
- public boolean onHoverEvent(MotionEvent ev) {
- // If we've received this, we've already done the necessary handling
- // in onInterceptHoverEvent. Return true to consume the event.
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
- if (mTouchCompleteListener != null) {
- mTouchCompleteListener.onTouchComplete();
- }
- mTouchCompleteListener = null;
- }
-
- if (mActiveController != null) {
- return mActiveController.onControllerTouchEvent(ev);
- }
- return false;
- }
-
- /**
- * Determine the rect of the descendant in this DragLayer's coordinates
- *
- * @param descendant The descendant whose coordinates we want to find.
- * @param r The rect into which to place the results.
- * @return The factor by which this descendant is scaled relative to this DragLayer.
- */
- public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
- mTmpXY[0] = 0;
- mTmpXY[1] = 0;
- float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
-
- r.set(mTmpXY[0], mTmpXY[1],
- (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
- (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
- return scale;
- }
-
- public float getLocationInDragLayer(View child, int[] loc) {
- loc[0] = 0;
- loc[1] = 0;
- return getDescendantCoordRelativeToSelf(child, loc);
- }
-
- public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
- return getDescendantCoordRelativeToSelf(descendant, coord, false);
- }
-
- /**
- * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
- * coordinates.
- *
- * @param descendant The descendant to which the passed coordinate is relative.
- * @param coord The coordinate that we want mapped.
- * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
- * sometimes this is relevant as in a child's coordinates within the root descendant.
- * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
- * this scale factor is assumed to be equal in X and Y, and so if at any point this
- * assumption fails, we will need to return a pair of scale factors.
- */
- public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
- boolean includeRootScroll) {
- return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
- coord, includeRootScroll);
- }
-
- /**
- * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
- */
- public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
- Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
- }
-
- public void getViewRectRelativeToSelf(View v, Rect r) {
- int[] loc = new int[2];
- getLocationInWindow(loc);
- int x = loc[0];
- int y = loc[1];
-
- v.getLocationInWindow(loc);
- int vX = loc[0];
- int vY = loc[1];
-
- int left = vX - x;
- int top = vY - y;
- r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
- }
-
- @Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- // Consume the unhandled move if a container is open, to avoid switching pages underneath.
- boolean isContainerOpen = AbstractFloatingView.getTopOpenView(mLauncher) != null;
- return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction);
+ return super.dispatchUnhandledMove(focused, direction)
+ || mDragController.dispatchUnhandledMove(focused, direction);
}
@Override
@@ -374,91 +248,6 @@
}
}
- @Override
- public LayoutParams generateLayoutParams(AttributeSet attrs) {
- return new LayoutParams(getContext(), attrs);
- }
-
- @Override
- protected LayoutParams generateDefaultLayoutParams() {
- return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- }
-
- // Override to allow type-checking of LayoutParams.
- @Override
- protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
- return p instanceof LayoutParams;
- }
-
- @Override
- protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
- return new LayoutParams(p);
- }
-
- public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
- public int x, y;
- public boolean customPosition = false;
-
- public LayoutParams(Context c, AttributeSet attrs) {
- super(c, attrs);
- }
-
- public LayoutParams(int width, int height) {
- super(width, height);
- }
-
- public LayoutParams(ViewGroup.LayoutParams lp) {
- super(lp);
- }
-
- public void setWidth(int width) {
- this.width = width;
- }
-
- public int getWidth() {
- return width;
- }
-
- public void setHeight(int height) {
- this.height = height;
- }
-
- public int getHeight() {
- return height;
- }
-
- public void setX(int x) {
- this.x = x;
- }
-
- public int getX() {
- return x;
- }
-
- public void setY(int y) {
- this.y = y;
- }
-
- public int getY() {
- return y;
- }
- }
-
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- int count = getChildCount();
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
- if (flp instanceof LayoutParams) {
- final LayoutParams lp = (LayoutParams) flp;
- if (lp.customPosition) {
- child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
- }
- }
- }
- }
-
public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
int duration) {
@@ -530,11 +319,7 @@
final int fromX = r.left;
final int fromY = r.top;
child.setVisibility(INVISIBLE);
- Runnable onCompleteRunnable = new Runnable() {
- public void run() {
- child.setVisibility(VISIBLE);
- }
- };
+ Runnable onCompleteRunnable = () -> child.setVisibility(VISIBLE);
animateViewIntoPosition(dragView, fromX, fromY, toX, toY, 1, 1, 1, toScale, toScale,
onCompleteRunnable, ANIMATION_END_DISAPPEAR, duration, anchorView);
}
@@ -701,12 +486,14 @@
public void onViewAdded(View child) {
super.onViewAdded(child);
updateChildIndices();
+ UiFactory.onLauncherStateOrFocusChanged(mActivity);
}
@Override
public void onViewRemoved(View child) {
super.onViewRemoved(child);
updateChildIndices();
+ UiFactory.onLauncherStateOrFocusChanged(mActivity);
}
@Override
@@ -752,49 +539,10 @@
}
}
- public void invalidateScrim() {
- if (mPageCutOutScrim.getAlpha() > 0) {
- invalidate();
- }
- }
-
- public Drawable getScrim() {
- return mPageCutOutScrim;
- }
-
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the background below children.
- mPageCutOutScrim.draw(canvas);
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
}
-
- @Override
- protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null) {
- return topView.requestFocus(direction, previouslyFocusedRect);
- } else {
- return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
- }
- }
-
- @Override
- public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
- View topView = AbstractFloatingView.getTopOpenView(mLauncher);
- if (topView != null) {
- topView.addFocusables(views, direction);
- } else {
- super.addFocusables(views, direction, focusableMode);
- }
- }
-
- public void setTouchCompleteListener(TouchCompleteListener listener) {
- mTouchCompleteListener = listener;
- }
-
- public interface TouchCompleteListener {
- void onTouchComplete();
- }
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index a59b899..8d4f2ef 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -448,7 +448,7 @@
canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
if (crossFade) {
mPaint.setAlpha((int) (255 * mCrossFadeProgress));
- final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ final int saveCount = canvas.save();
float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
canvas.scale(sX, sY);
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index 1c6f77c..5576d91 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -36,7 +36,7 @@
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
-import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.Preconditions;
/**
@@ -113,7 +113,7 @@
final float previewShiftX = shiftFactor * previewWidth;
final float previewShiftY = shiftFactor * previewHeight;
- Bitmap previewBitmap = UiFactory.createFromRenderer(previewWidth, previewHeight, false,
+ Bitmap previewBitmap = BitmapRenderer.createHardwareBitmap(previewWidth, previewHeight,
(canvas) -> {
int count = canvas.save();
canvas.translate(previewShiftX, previewShiftY);
diff --git a/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java b/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java
deleted file mode 100644
index 367124b..0000000
--- a/src/com/android/launcher3/dragndrop/PageCutOutScrimDrawable.java
+++ /dev/null
@@ -1,90 +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.dragndrop;
-
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.Region;
-import android.graphics.drawable.Drawable;
-import android.support.v4.graphics.ColorUtils;
-
-import com.android.launcher3.CellLayout;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.dynamicui.WallpaperColorInfo;
-
-/**
- * Scrim drawable which draws a hole for the current drop target page.
- */
-public class PageCutOutScrimDrawable extends Drawable {
-
- private final Rect mHighlightRect = new Rect();
- private final DragLayer mDragLayer;
- private final Launcher mLauncher;
- private final WallpaperColorInfo mWallpaperColorInfo;
-
- private int mAlpha = 0;
-
- public PageCutOutScrimDrawable(DragLayer dragLayer) {
- mDragLayer = dragLayer;
- mLauncher = Launcher.getLauncher(mDragLayer.getContext());
- mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
- }
-
- @Override
- public void draw(Canvas canvas) {
- // Draw the background below children.
- if (mAlpha > 0) {
- // Update the scroll position first to ensure scrim cutout is in the right place.
- mLauncher.getWorkspace().computeScrollWithoutInvalidation();
- CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout();
- canvas.save();
- if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
- // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
- mDragLayer.getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
- canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
- }
- // for super light wallpaper it needs to be darken for contrast to workspace
- // for dark wallpapers the text is white so darkening works as well
- int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor());
- canvas.drawColor(ColorUtils.setAlphaComponent(color, mAlpha));
- canvas.restore();
- }
- }
-
- @Override
- public void setAlpha(int alpha) {
- if (mAlpha != alpha) {
- mAlpha = alpha;
- invalidateSelf();
- }
- }
-
- @Override
- public int getAlpha() {
- return mAlpha;
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) { }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-}
diff --git a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
index 80a89e3..32605a2 100644
--- a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
+++ b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
@@ -2,7 +2,6 @@
import android.content.Context;
import android.graphics.Color;
-import android.support.v4.graphics.ColorUtils;
import android.util.Pair;
import com.android.launcher3.compat.WallpaperColorsCompat;
@@ -34,7 +33,8 @@
private int mSecondaryColor;
private boolean mIsDark;
private boolean mSupportsDarkText;
- private OnThemeChangeListener mOnThemeChangeListener;
+
+ private OnChangeListener[] mTempListeners;
private WallpaperColorInfo(Context context) {
mWallpaperManager = WallpaperManagerCompat.getInstance(context);
@@ -62,10 +62,8 @@
@Override
public void onColorsChanged(WallpaperColorsCompat colors, int which) {
if ((which & FLAG_SYSTEM) != 0) {
- boolean wasDarkTheme = mIsDark;
- boolean didSupportDarkText = mSupportsDarkText;
update(colors);
- notifyChange(wasDarkTheme != mIsDark || didSupportDarkText != mSupportsDarkText);
+ notifyChange();
}
}
@@ -86,10 +84,6 @@
& WallpaperColorsCompat.HINT_SUPPORTS_DARK_THEME) > 0 : false;
}
- public void setOnThemeChangeListener(OnThemeChangeListener onThemeChangeListener) {
- this.mOnThemeChangeListener = onThemeChangeListener;
- }
-
public void addOnChangeListener(OnChangeListener listener) {
mListeners.add(listener);
}
@@ -98,23 +92,19 @@
mListeners.remove(listener);
}
- public void notifyChange(boolean themeChanged) {
- if (themeChanged) {
- if (mOnThemeChangeListener != null) {
- mOnThemeChangeListener.onThemeChanged();
- }
- } else {
- for (OnChangeListener listener : mListeners) {
- listener.onExtractedColorsChanged(this);
- }
+ private void notifyChange() {
+ OnChangeListener[] copy =
+ mTempListeners != null && mTempListeners.length == mListeners.size() ?
+ mTempListeners : new OnChangeListener[mListeners.size()];
+
+ // Create a new array to avoid concurrent modification when the activity destroys itself.
+ mTempListeners = mListeners.toArray(copy);
+ for (OnChangeListener listener : mTempListeners) {
+ listener.onExtractedColorsChanged(this);
}
}
public interface OnChangeListener {
void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
}
-
- public interface OnThemeChangeListener {
- void onThemeChanged();
- }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 8abafb0..12d7dc7 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -18,7 +18,6 @@
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
@@ -86,7 +85,7 @@
/**
* Represents a set of icons chosen by the user or generated by the system.
*/
-public class Folder extends AbstractFloatingView implements DragSource, View.OnClickListener,
+public class Folder extends AbstractFloatingView implements DragSource,
View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
private static final String TAG = "Launcher.Folder";
@@ -208,11 +207,11 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mContent = (FolderPagedView) findViewById(R.id.folder_content);
+ mContent = findViewById(R.id.folder_content);
mContent.setFolder(this);
- mPageIndicator = (PageIndicatorDots) findViewById(R.id.folder_page_indicator);
- mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
+ mPageIndicator = findViewById(R.id.folder_page_indicator);
+ mFolderName = findViewById(R.id.folder_name);
mFolderName.setOnBackKeyListener(this);
mFolderName.setOnFocusChangeListener(this);
@@ -253,13 +252,6 @@
mFooterHeight = mFooter.getMeasuredHeight();
}
- public void onClick(View v) {
- Object tag = v.getTag();
- if (tag instanceof ShortcutInfo) {
- mLauncher.onClick(v);
- }
- }
-
public boolean onLongClick(View v) {
// Return if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return true;
@@ -489,6 +481,8 @@
openFolder.close(true);
}
+ mIsOpen = true;
+
DragLayer dragLayer = mLauncher.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.
@@ -502,8 +496,6 @@
}
}
- mIsOpen = true;
-
mContent.completePendingPageChanges();
if (!mDragInProgress) {
// Open on the first page.
@@ -522,7 +514,7 @@
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
}
};
anim.addListener(new AnimatorListenerAdapter() {
@@ -930,7 +922,7 @@
int centeredTop = centerY - height / 2;
// We need to bound the folder to the currently visible workspace area
- if (mLauncher.isInState(OVERVIEW)) {
+ if (mLauncher.getStateManager().getState().overviewUi) {
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
sTempRect);
} else {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 2de09b8..cb5d872 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,6 +63,7 @@
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -159,14 +160,14 @@
.inflate(resId, group, false);
icon.setClipToPadding(false);
- icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
+ icon.mFolderName = icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
icon.mFolderName.setCompoundDrawablePadding(0);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
icon.setTag(folderInfo);
- icon.setOnClickListener(launcher);
+ icon.setOnClickListener(ItemClickHandler.INSTANCE);
icon.mInfo = folderInfo;
icon.mLauncher = launcher;
icon.mBadgeRenderer = launcher.getDeviceProfile().mBadgeRenderer;
@@ -467,11 +468,10 @@
final int saveCount;
if (canvas.isHardwareAccelerated()) {
- saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
} else {
- saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipPath(mBackground.getClipPath(), Region.Op.INTERSECT);
+ saveCount = canvas.save();
+ canvas.clipPath(mBackground.getClipPath());
}
mPreviewItemManager.draw(canvas);
@@ -568,7 +568,7 @@
@Override
public void onAdd(ShortcutInfo item, int rank) {
boolean wasBadged = mBadgeInfo.hasBadge();
- mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
+ mBadgeInfo.addBadgeInfo(mLauncher.getBadgeInfoForItem(item));
boolean isBadged = mBadgeInfo.hasBadge();
updateBadgeScale(wasBadged, isBadged);
invalidate();
@@ -578,7 +578,7 @@
@Override
public void onRemove(ShortcutInfo item) {
boolean wasBadged = mBadgeInfo.hasBadge();
- mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
+ mBadgeInfo.subtractBadgeInfo(mLauncher.getBadgeInfoForItem(item));
boolean isBadged = mBadgeInfo.hasBadge();
updateBadgeScale(wasBadged, isBadged);
invalidate();
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index f4462aa..fa7565a 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -44,14 +44,15 @@
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
-import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
-public class FolderPagedView extends PagedView {
+public class FolderPagedView extends PagedView<PageIndicatorDots> {
private static final String TAG = "FolderPagedView";
@@ -89,8 +90,6 @@
private Folder mFolder;
private PagedFolderKeyEventListener mKeyListener;
- private PageIndicator mPageIndicator;
-
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
@@ -239,7 +238,7 @@
R.layout.folder_application, null, false);
textView.applyFromShortcutInfo(item);
textView.setHapticFeedbackEnabled(false);
- textView.setOnClickListener(mFolder);
+ textView.setOnClickListener(ItemClickHandler.INSTANCE);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
textView.setOnKeyListener(mKeyListener);
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 285aef8..069ec4b 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -129,18 +129,15 @@
};
public void setup(Launcher launcher, View invalidateDelegate,
- int availableSpace, int topPadding) {
+ int availableSpaceX, int topPadding) {
mInvalidateDelegate = invalidateDelegate;
mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
DeviceProfile grid = launcher.getDeviceProfile();
- final int previewSize = grid.folderIconSizePx;
- final int previewPadding = grid.folderIconPreviewPadding;
+ previewSize = grid.folderIconSizePx;
- this.previewSize = (previewSize - 2 * previewPadding);
-
- basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
- basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+ basePreviewOffsetX = (availableSpaceX - previewSize) / 2;
+ basePreviewOffsetY = topPadding + grid.folderIconOffsetYPx;
// Stroke width is 1dp
mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
@@ -227,11 +224,10 @@
final int saveCount;
if (canvas.isHardwareAccelerated()) {
saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
- offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
- null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+ offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius, null);
} else {
- saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ saveCount = canvas.save();
canvas.clipPath(getClipPath(), Region.Op.DIFFERENCE);
}
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 06d3eb1..1f69f6e 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -168,7 +168,7 @@
}
private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.save();
canvas.translate(params.transX, params.transY);
canvas.scale(params.scale, params.scale);
Drawable d = params.drawable;
diff --git a/src/com/android/launcher3/graphics/BitmapRenderer.java b/src/com/android/launcher3/graphics/BitmapRenderer.java
index 4652ded..3d11c44 100644
--- a/src/com/android/launcher3/graphics/BitmapRenderer.java
+++ b/src/com/android/launcher3/graphics/BitmapRenderer.java
@@ -15,9 +15,40 @@
*/
package com.android.launcher3.graphics;
+import android.annotation.TargetApi;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Picture;
+import android.os.Build;
-public interface BitmapRenderer {
+import com.android.launcher3.Utilities;
- void render(Canvas out);
+public class BitmapRenderer {
+
+ public static final boolean USE_HARDWARE_BITMAP = Utilities.ATLEAST_P;
+
+ public static Bitmap createSoftwareBitmap(int width, int height, Renderer renderer) {
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ renderer.draw(new Canvas(result));
+ return result;
+ }
+
+ @TargetApi(Build.VERSION_CODES.P)
+ public static Bitmap createHardwareBitmap(int width, int height, Renderer renderer) {
+ if (!USE_HARDWARE_BITMAP) {
+ return createSoftwareBitmap(width, height, renderer);
+ }
+
+ Picture picture = new Picture();
+ renderer.draw(picture.beginRecording(width, height));
+ picture.endRecording();
+ return Bitmap.createBitmap(picture);
+ }
+
+ /**
+ * Interface representing a bitmap draw operation.
+ */
+ public interface Renderer {
+ void draw(Canvas out);
+ }
}
diff --git a/src/com/android/launcher3/graphics/ColorScrim.java b/src/com/android/launcher3/graphics/ColorScrim.java
new file mode 100644
index 0000000..1ffce18
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ColorScrim.java
@@ -0,0 +1,64 @@
+/*
+ * 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.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.R;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+
+/**
+ * Simple scrim which draws a color
+ */
+public class ColorScrim extends ViewScrim {
+
+ private final int mColor;
+ private final Interpolator mInterpolator;
+ private int mCurrentColor;
+
+ public ColorScrim(View view, int color, Interpolator interpolator) {
+ super(view);
+ mColor = color;
+ mInterpolator = interpolator;
+ }
+
+ @Override
+ protected void onProgressChanged() {
+ mCurrentColor = ColorUtils.setAlphaComponent(mColor,
+ Math.round(mInterpolator.getInterpolation(mProgress) * Color.alpha(mColor)));
+ }
+
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ if (mProgress > 0) {
+ canvas.drawColor(mCurrentColor);
+ }
+ }
+
+ public static ColorScrim createExtractedColorScrim(View view) {
+ WallpaperColorInfo colors = WallpaperColorInfo.getInstance(view.getContext());
+ int alpha = view.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
+ ColorScrim scrim = new ColorScrim(view, ColorUtils.setAlphaComponent(
+ colors.getSecondaryColor(), alpha), Interpolators.LINEAR);
+ scrim.attach();
+ return scrim;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 6a328e9..5094280 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -24,19 +24,17 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
-import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.view.View;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.nio.ByteBuffer;
@@ -78,7 +76,7 @@
/**
* Draws the {@link #mView} into the given {@param destCanvas}.
*/
- private void drawDragView(Canvas destCanvas, float scale) {
+ protected void drawDragView(Canvas destCanvas, float scale) {
destCanvas.save();
destCanvas.scale(scale, scale);
@@ -103,7 +101,7 @@
}
destCanvas.translate(-mView.getScrollX() + blurSizeOutline / 2,
-mView.getScrollY() + blurSizeOutline / 2);
- destCanvas.clipRect(clipRect, Op.REPLACE);
+ destCanvas.clipRect(clipRect);
mView.draw(destCanvas);
// Restore text visibility of FolderIcon if necessary
@@ -119,28 +117,26 @@
* Responsibility for the bitmap is transferred to the caller.
*/
public Bitmap createDragBitmap() {
- float scale = 1f;
int width = mView.getWidth();
int height = mView.getHeight();
- boolean forceSoftwareRenderer = false;
if (mView instanceof BubbleTextView) {
Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
width = bounds.width();
height = bounds.height();
} else if (mView instanceof LauncherAppWidgetHostView) {
- scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
+ float scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
width = (int) (mView.getWidth() * scale);
height = (int) (mView.getHeight() * scale);
// Use software renderer for widgets as we know that they already work
- forceSoftwareRenderer = true;
+ return BitmapRenderer.createSoftwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, scale));
}
- final float scaleFinal = scale;
- return UiFactory.createFromRenderer(width + blurSizeOutline, height + blurSizeOutline,
- forceSoftwareRenderer, (c) -> drawDragView(c, scaleFinal));
+ return BitmapRenderer.createHardwareBitmap(width + blurSizeOutline,
+ height + blurSizeOutline, (c) -> drawDragView(c, 1));
}
public final void generateDragOutline(Bitmap preview) {
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 32d9e41..34a4e2d 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -17,6 +17,7 @@
package com.android.launcher3.graphics;
import android.content.Context;
+import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -65,6 +66,12 @@
* Returns a FastBitmapDrawable with the icon.
*/
public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
+ FastBitmapDrawable drawable = new FastBitmapDrawable(info);
+ drawable.setIsDisabled(info.isDisabled());
+ return drawable;
+ }
+
+ public FastBitmapDrawable newIcon(BitmapInfo info, ActivityInfo target) {
return new FastBitmapDrawable(info);
}
@@ -78,7 +85,6 @@
return new PreloadIconDrawable(info, mPreloadProgressPath, context);
}
-
protected Path getPreloadProgressPath(Context context) {
if (Utilities.ATLEAST_OREO) {
try {
diff --git a/src/com/android/launcher3/graphics/FixedScaleDrawable.java b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
index 262a95e..0f0e424 100644
--- a/src/com/android/launcher3/graphics/FixedScaleDrawable.java
+++ b/src/com/android/launcher3/graphics/FixedScaleDrawable.java
@@ -29,7 +29,7 @@
@Override
public void draw(Canvas canvas) {
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ int saveCount = canvas.save();
canvas.scale(mScaleX, mScaleY,
getBounds().exactCenterX(), getBounds().exactCenterY());
super.draw(canvas);
diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java
deleted file mode 100644
index 6253e18..0000000
--- a/src/com/android/launcher3/graphics/GradientView.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.graphics;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.LinearGradient;
-import android.graphics.Paint;
-import android.graphics.RadialGradient;
-import android.graphics.RectF;
-import android.graphics.Shader;
-import android.support.v4.graphics.ColorUtils;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.View;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.util.Themes;
-
-/**
- * Draws a translucent radial gradient background from an initial state with progress 0.0 to a
- * final state with progress 1.0;
- */
-public class GradientView extends View implements WallpaperColorInfo.OnChangeListener {
-
- private static final int DEFAULT_COLOR = Color.WHITE;
- private static final int ALPHA_MASK_HEIGHT_DP = 500;
- private static final int ALPHA_MASK_WIDTH_DP = 2;
- private static final boolean DEBUG = false;
-
- private final Bitmap mAlphaGradientMask;
-
- private boolean mShowScrim = true;
- private int mColor1 = DEFAULT_COLOR;
- private int mColor2 = DEFAULT_COLOR;
- private int mWidth;
- private int mHeight;
- private final RectF mAlphaMaskRect = new RectF();
- private final RectF mFinalMaskRect = new RectF();
- private final Paint mPaintWithScrim = new Paint();
- private final Paint mPaintNoScrim = new Paint();
- private float mProgress;
- private final int mMaskHeight, mMaskWidth;
- private final int mAlphaColors;
- private final Paint mDebugPaint = DEBUG ? new Paint() : null;
- private final Interpolator mAccelerator = Interpolators.ACCEL;
- private final float mAlphaStart;
- private final WallpaperColorInfo mWallpaperColorInfo;
- private final int mScrimColor;
-
- public GradientView(Context context, AttributeSet attrs) {
- super(context, attrs);
- DisplayMetrics dm = getResources().getDisplayMetrics();
- this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
- this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
- Launcher launcher = Launcher.getLauncher(context);
- this.mAlphaStart = 0;
- this.mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
- this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher);
- mAlphaColors = getResources().getInteger(R.integer.extracted_color_gradient_alpha);
- updateColors();
- mAlphaGradientMask = createDitheredAlphaMask();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mWallpaperColorInfo.addOnChangeListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mWallpaperColorInfo.removeOnChangeListener(this);
- }
-
- @Override
- public void onExtractedColorsChanged(WallpaperColorInfo info) {
- updateColors();
- invalidate();
- }
-
- private void updateColors() {
- this.mColor1 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getMainColor(),
- mAlphaColors);
- this.mColor2 = ColorUtils.setAlphaComponent(mWallpaperColorInfo.getSecondaryColor(),
- mAlphaColors);
- if (mWidth + mHeight > 0) {
- createRadialShader();
- }
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- this.mWidth = getMeasuredWidth();
- this.mHeight = getMeasuredHeight();
- if (mWidth + mHeight > 0) {
- createRadialShader();
- }
- }
-
- // only being called when colors change
- private void createRadialShader() {
- final float gradientCenterY = 1.05f;
- float radius = Math.max(mHeight, mWidth) * gradientCenterY;
- float posScreenBottom = (radius - mHeight) / radius; // center lives below screen
-
- RadialGradient shaderNoScrim = new RadialGradient(
- mWidth * 0.5f,
- mHeight * gradientCenterY,
- radius,
- new int[] {mColor1, mColor1, mColor2},
- new float[] {0f, posScreenBottom, 1f},
- Shader.TileMode.CLAMP);
- mPaintNoScrim.setShader(shaderNoScrim);
-
- int color1 = ColorUtils.compositeColors(mScrimColor,mColor1);
- int color2 = ColorUtils.compositeColors(mScrimColor,mColor2);
- RadialGradient shaderWithScrim = new RadialGradient(
- mWidth * 0.5f,
- mHeight * gradientCenterY,
- radius,
- new int[] { color1, color1, color2 },
- new float[] {0f, posScreenBottom, 1f},
- Shader.TileMode.CLAMP);
- mPaintWithScrim.setShader(shaderWithScrim);
- }
-
- public void setProgress(float progress) {
- setProgress(progress, true);
- }
-
- public void setProgress(float progress, boolean showScrim) {
- this.mProgress = progress;
- this.mShowScrim = showScrim;
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- Paint paint = mShowScrim ? mPaintWithScrim : mPaintNoScrim;
-
- float head = 0.29f;
- float linearProgress = head + (mProgress * (1f - head));
- float startMaskY = (1f - linearProgress) * mHeight - mMaskHeight * linearProgress;
- float interpolatedAlpha = (255 - mAlphaStart) * mAccelerator.getInterpolation(mProgress);
- paint.setAlpha((int) (mAlphaStart + interpolatedAlpha));
- float div = (float) Math.floor(startMaskY + mMaskHeight);
- mAlphaMaskRect.set(0, startMaskY, mWidth, div);
- mFinalMaskRect.set(0, div, mWidth, mHeight);
- canvas.drawBitmap(mAlphaGradientMask, null, mAlphaMaskRect, paint);
- canvas.drawRect(mFinalMaskRect, paint);
-
- if (DEBUG) {
- mDebugPaint.setColor(0xFF00FF00);
- canvas.drawLine(0, startMaskY, mWidth, startMaskY, mDebugPaint);
- canvas.drawLine(0, startMaskY + mMaskHeight, mWidth, startMaskY + mMaskHeight, mDebugPaint);
- }
- }
-
- public Bitmap createDitheredAlphaMask() {
- Bitmap dst = Bitmap.createBitmap(mMaskWidth, mMaskHeight, Bitmap.Config.ALPHA_8);
- Canvas c = new Canvas(dst);
- Paint paint = new Paint(Paint.DITHER_FLAG);
- LinearGradient lg = new LinearGradient(0, 0, 0, mMaskHeight,
- new int[]{
- 0x00FFFFFF,
- ColorUtils.setAlphaComponent(Color.WHITE, (int) (0xFF * 0.95)),
- 0xFFFFFFFF},
- new float[]{0f, 0.8f, 1f},
- Shader.TileMode.CLAMP);
- paint.setShader(lg);
- c.drawRect(0, 0, mMaskWidth, mMaskHeight, paint);
- return dst;
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
deleted file mode 100644
index ebfe1e7..0000000
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ /dev/null
@@ -1,145 +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.graphics;
-
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_ADAPTIVE_ICON;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.SparseArray;
-
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.ItemInfoWithIcon;
-import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.UiFactory;
-
-/**
- * Utility class to generate shadow and outline effect, which are used for click feedback
- * and drag-n-drop respectively.
- */
-public class HolographicOutlineHelper {
-
- /**
- * Bitmap used as shadow for Adaptive icons
- */
- public static final Bitmap ADAPTIVE_ICON_SHADOW_BITMAP =
- Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
-
- private static HolographicOutlineHelper sInstance;
-
- private final Canvas mCanvas = new Canvas();
- private final Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- private final Paint mErasePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
-
- private final float mShadowBitmapShift;
- private final BlurMaskFilter mShadowBlurMaskFilter;
-
- // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps
- private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4);
-
- private HolographicOutlineHelper(Context context) {
- mShadowBitmapShift = context.getResources().getDimension(R.dimen.blur_size_click_shadow);
- mShadowBlurMaskFilter = new BlurMaskFilter(mShadowBitmapShift, BlurMaskFilter.Blur.NORMAL);
- mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
- }
-
- public static HolographicOutlineHelper getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new HolographicOutlineHelper(context.getApplicationContext());
- }
- return sInstance;
- }
-
- public Bitmap createMediumDropShadow(BubbleTextView view) {
- if (view.getTag() instanceof ItemInfoWithIcon &&
- ((((ItemInfoWithIcon) view.getTag()).runtimeStatusFlags & FLAG_ADAPTIVE_ICON)
- != 0)) {
- return ADAPTIVE_ICON_SHADOW_BITMAP;
- }
- Drawable drawable = view.getIcon();
- if (drawable == null) {
- return null;
- }
-
- float scaleX = view.getScaleX();
- float scaleY = view.getScaleY();
- Rect rect = drawable.getBounds();
-
- int bitmapWidth = (int) (rect.width() * scaleX);
- int bitmapHeight = (int) (rect.height() * scaleY);
- if (bitmapHeight <= 0 || bitmapWidth <= 0) {
- return null;
- }
-
- int key = (bitmapWidth << 16) | bitmapHeight;
- Bitmap cache = mBitmapCache.get(key);
- if (cache == null) {
- cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8);
- mCanvas.setBitmap(cache);
- mBitmapCache.put(key, cache);
- } else {
- mCanvas.setBitmap(cache);
- mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- }
-
- int saveCount = mCanvas.save();
- mCanvas.scale(scaleX, scaleY);
- mCanvas.translate(-rect.left, -rect.top);
- if (!UiFactory.USE_HARDWARE_BITMAP) {
- // TODO: Outline generation requires alpha extraction, which is costly for
- // hardware bitmaps. Instead use canvas layer operations once its available.
- drawable.draw(mCanvas);
- }
- mCanvas.restoreToCount(saveCount);
- mCanvas.setBitmap(null);
-
- mBlurPaint.setMaskFilter(mShadowBlurMaskFilter);
-
- int extraSize = (int) (2 * mShadowBitmapShift);
-
- int resultWidth = bitmapWidth + extraSize;
- int resultHeight = bitmapHeight + extraSize;
- key = (resultWidth << 16) | resultHeight;
- Bitmap result = mBitmapCache.get(key);
- if (result == null) {
- result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8);
- mCanvas.setBitmap(result);
- } else {
- // Use put instead of delete, to avoid unnecessary shrinking of cache array
- mBitmapCache.put(key, null);
- mCanvas.setBitmap(result);
- mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- }
- mCanvas.drawBitmap(cache, mShadowBitmapShift, mShadowBitmapShift, mBlurPaint);
- mCanvas.setBitmap(null);
- return result;
- }
-
- public void recycleShadowBitmap(Bitmap bitmap) {
- if (bitmap != null && bitmap != ADAPTIVE_ICON_SHADOW_BITMAP) {
- mBitmapCache.put((bitmap.getWidth() << 16) | bitmap.getHeight(), bitmap);
- }
- }
-}
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index bd20c87..680c020 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -61,6 +61,9 @@
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
private static final float SCALE_NOT_INITIALIZED = 0;
+ // Ratio of the diameter of an normalized circular icon to the actual icon size.
+ public static final float ICON_VISIBLE_AREA_FACTOR = 0.92f;
+
private final int mMaxSize;
private final Bitmap mBitmap;
private final Bitmap mBitmapARGB;
@@ -373,4 +376,12 @@
last = i;
}
}
+
+ /**
+ * @return The diameter of the normalized circle that fits inside of the square (size x size).
+ */
+ public static int getNormalizedCircleSize(int size) {
+ float area = size * size * MAX_CIRCLE_AREA_FACTOR;
+ return (int) Math.round(Math.sqrt((4 * area) / Math.PI));
+ }
}
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 34fc921..3b5585b 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -29,11 +29,13 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.os.Build;
@@ -52,7 +54,6 @@
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.Themes;
@@ -61,6 +62,8 @@
*/
public class LauncherIcons implements AutoCloseable {
+ private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
+
public static final Object sPoolSync = new Object();
private static LauncherIcons sPool;
@@ -85,6 +88,9 @@
*/
public void recycle() {
synchronized (sPoolSync) {
+ // Clear any temporary state variables
+ mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+
next = sPool;
sPool = this;
}
@@ -106,6 +112,9 @@
private IconNormalizer mNormalizer;
private ShadowGenerator mShadowGenerator;
+ private Drawable mWrapperIcon;
+ private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+
// sometimes we store linked lists of these things
private LauncherIcons next;
@@ -173,6 +182,16 @@
* The bitmap is also visually normalized with other icons.
*/
public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) {
+ return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
+ }
+
+ /**
+ * Returns a bitmap suitable for displaying as an icon at various launcher UIs like all apps
+ * view or workspace. The icon is badged for {@param user}.
+ * The bitmap is also visually normalized with other icons.
+ */
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
+ boolean isInstantApp) {
float[] scale = new float[1];
icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale);
Bitmap bitmap = createIconBitmap(icon, scale[0]);
@@ -191,6 +210,9 @@
} else {
result = createIconBitmap(badged, 1f);
}
+ } else if (isInstantApp) {
+ badgeWithDrawable(bitmap, mContext.getDrawable(R.drawable.ic_instant_app_badge));
+ result = bitmap;
} else {
result = bitmap;
}
@@ -209,13 +231,23 @@
Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
}
+ /**
+ * Sets the background color used for wrapped adaptive icon
+ */
+ public void setWrapperBackgroundColor(int color) {
+ mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
+ }
+
private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
RectF outIconBounds, float[] outScale) {
float scale = 1f;
if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
boolean[] outShape = new boolean[1];
- AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
- mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
+ if (mWrapperIcon == null) {
+ mWrapperIcon = mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper)
+ .mutate();
+ }
+ AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
dr.setBounds(0, 0, 1, 1);
scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
if (Utilities.ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
@@ -224,6 +256,8 @@
fsd.setScale(scale);
icon = dr;
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+
+ ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
}
} else {
scale = getNormalizer().getScale(icon, outIconBounds, null, null);
@@ -302,7 +336,7 @@
} else {
icon.setBounds(left, top, left+width, top+height);
}
- mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
+ mCanvas.save();
mCanvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
icon.draw(mCanvas);
mCanvas.restore();
@@ -349,7 +383,7 @@
final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
result.color = badge.iconColor;
- result.icon = UiFactory.createFromRenderer(mIconBitmapSize, mIconBitmapSize, false, (c) -> {
+ result.icon = BitmapRenderer.createHardwareBitmap(mIconBitmapSize, mIconBitmapSize, (c) -> {
getShadowGenerator().recreateIcon(unbadgedfinal, c);
badgeWithDrawable(c, new FastBitmapDrawable(badge));
});
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index a40b6df..42ba191 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -162,9 +162,9 @@
}
@Override
- public void draw(Canvas canvas) {
+ public void drawInternal(Canvas canvas, Rect bounds) {
if (mRanFinishAnimation) {
- super.draw(canvas);
+ super.drawInternal(canvas, bounds);
return;
}
@@ -172,15 +172,13 @@
mProgressPaint.setColor(mIndicatorColor);
mProgressPaint.setAlpha(mTrackAlpha);
if (mShadowBitmap != null) {
- canvas.drawBitmap(mShadowBitmap, getBounds().left, getBounds().top, mProgressPaint);
+ canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
}
canvas.drawPath(mScaledProgressPath, mProgressPaint);
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- Rect bounds = getBounds();
-
+ int saveCount = canvas.save();
canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
- super.draw(canvas);
+ super.drawInternal(canvas, bounds);
canvas.restoreToCount(saveCount);
}
diff --git a/src/com/android/launcher3/graphics/ViewScrim.java b/src/com/android/launcher3/graphics/ViewScrim.java
new file mode 100644
index 0000000..e1727e0
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ViewScrim.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util.Property;
+import android.view.View;
+import android.view.ViewParent;
+
+import com.android.launcher3.R;
+
+/**
+ * A utility class that can be used to draw a scrim behind a view
+ */
+public abstract class ViewScrim<T extends View> {
+
+ public static Property<ViewScrim, Float> PROGRESS =
+ new Property<ViewScrim, Float>(Float.TYPE, "progress") {
+ @Override
+ public Float get(ViewScrim viewScrim) {
+ return viewScrim.mProgress;
+ }
+
+ @Override
+ public void set(ViewScrim object, Float value) {
+ object.setProgress(value);
+ }
+ };
+
+ protected final T mView;
+ protected float mProgress = 0;
+
+ public ViewScrim(T view) {
+ mView = view;
+ }
+
+ public void attach() {
+ mView.setTag(R.id.view_scrim, this);
+ }
+
+ public void setProgress(float progress) {
+ if (mProgress != progress) {
+ mProgress = progress;
+ onProgressChanged();
+ invalidate();
+ }
+ }
+
+ public abstract void draw(Canvas canvas, int width, int height);
+
+ protected void onProgressChanged() { }
+
+ public void invalidate() {
+ ViewParent parent = mView.getParent();
+ if (parent != null) {
+ ((View) parent).invalidate();
+ }
+ }
+
+ public static ViewScrim get(View view) {
+ return (ViewScrim) view.getTag(R.id.view_scrim);
+ }
+}
diff --git a/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
new file mode 100644
index 0000000..2318a77
--- /dev/null
+++ b/src/com/android/launcher3/graphics/WorkspaceAndHotseatScrim.java
@@ -0,0 +1,152 @@
+/*
+ * 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.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.support.v4.graphics.ColorUtils;
+import android.util.DisplayMetrics;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+
+/**
+ * View scrim which draws behind hotseat and workspace
+ */
+public class WorkspaceAndHotseatScrim extends ViewScrim<Workspace> implements
+ View.OnAttachStateChangeListener, WallpaperColorInfo.OnChangeListener {
+
+ private static final int DARK_SCRIM_COLOR = 0x55000000;
+ private static final int MAX_HOTSEAT_SCRIM_ALPHA = 100;
+ private static final int ALPHA_MASK_HEIGHT_DP = 500;
+ private static final int ALPHA_MASK_BITMAP_DP = 200;
+ private static final int ALPHA_MASK_WIDTH_DP = 2;
+
+ private final Rect mHighlightRect = new Rect();
+ private final Launcher mLauncher;
+ private final WallpaperColorInfo mWallpaperColorInfo;
+
+ private final boolean mHasHotseatScrim;
+ private final RectF mFinalMaskRect = new RectF();
+ private final Paint mBottomMaskPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+
+ private final Bitmap mBottomMask;
+ private final int mMaskHeight;
+
+ private int mFullScrimColor;
+
+ private final int mMaxAlpha;
+ private int mAlpha = 0;
+
+ public WorkspaceAndHotseatScrim(Workspace view) {
+ super(view);
+ mLauncher = Launcher.getLauncher(view.getContext());
+ mWallpaperColorInfo = WallpaperColorInfo.getInstance(mLauncher);
+
+ mMaxAlpha = mLauncher.getResources().getInteger(R.integer.config_workspaceScrimAlpha);
+ mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_BITMAP_DP,
+ view.getResources().getDisplayMetrics());
+
+ mHasHotseatScrim = !mWallpaperColorInfo.supportsDarkText();
+ mBottomMask = mHasHotseatScrim ? createDitheredAlphaMask() : null;
+
+ view.addOnAttachStateChangeListener(this);
+ onExtractedColorsChanged(mWallpaperColorInfo);
+ }
+
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ // Draw the background below children.
+ if (mAlpha > 0) {
+ // Update the scroll position first to ensure scrim cutout is in the right place.
+ mView.computeScrollWithoutInvalidation();
+ CellLayout currCellLayout = mView.getCurrentDragOverlappingLayout();
+ canvas.save();
+ if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
+ // 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);
+ }
+
+ canvas.drawColor(ColorUtils.setAlphaComponent(mFullScrimColor, mAlpha));
+ canvas.restore();
+ }
+
+ if (mHasHotseatScrim && !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ mFinalMaskRect.set(0, height - mMaskHeight, width, height);
+ mBottomMaskPaint.setAlpha(Math.round(MAX_HOTSEAT_SCRIM_ALPHA * (1 - mProgress)));
+ canvas.drawBitmap(mBottomMask, null, mFinalMaskRect, mBottomMaskPaint);
+ }
+ }
+
+ @Override
+ protected void onProgressChanged() {
+ mAlpha = Math.round(mMaxAlpha * mProgress);
+ }
+
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ mWallpaperColorInfo.addOnChangeListener(this);
+ onExtractedColorsChanged(mWallpaperColorInfo);
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ mWallpaperColorInfo.removeOnChangeListener(this);
+ }
+
+ @Override
+ public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+ // for super light wallpaper it needs to be darken for contrast to workspace
+ // for dark wallpapers the text is white so darkening works as well
+ mFullScrimColor = ColorUtils.compositeColors(DARK_SCRIM_COLOR,
+ wallpaperColorInfo.getMainColor());
+ mBottomMaskPaint.setColor(mFullScrimColor);
+ }
+
+ public Bitmap createDitheredAlphaMask() {
+ DisplayMetrics dm = mLauncher.getResources().getDisplayMetrics();
+ int width = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
+ int gradientHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
+ Bitmap dst = Bitmap.createBitmap(width, mMaskHeight, Bitmap.Config.ALPHA_8);
+ Canvas c = new Canvas(dst);
+ Paint paint = new Paint(Paint.DITHER_FLAG);
+ LinearGradient lg = new LinearGradient(0, 0, 0, gradientHeight,
+ new int[]{
+ 0x00FFFFFF,
+ ColorUtils.setAlphaComponent(Color.WHITE, (int) (0xFF * 0.95)),
+ 0xFFFFFFFF},
+ new float[]{0f, 0.8f, 1f},
+ Shader.TileMode.CLAMP);
+ paint.setShader(lg);
+ c.drawRect(0, 0, width, gradientHeight, paint);
+ return dst;
+ }
+}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index c07ab08..c50189c 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -236,4 +236,19 @@
}
}
}
+
+ /**
+ * Simple subclass which assumes that the target view is a child of the container.
+ */
+ public static class SimpleFocusIndicatorHelper extends FocusIndicatorHelper {
+
+ public SimpleFocusIndicatorHelper(View container) {
+ super(container);
+ }
+
+ @Override
+ public void viewToRect(View v, Rect outRect) {
+ outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+ }
+ }
}
diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
index 9c80b0f..05ae406 100644
--- a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
+++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
@@ -17,13 +17,14 @@
package com.android.launcher3.keyboard;
import android.graphics.Canvas;
-import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.support.v7.widget.RecyclerView.State;
import android.view.View;
import android.view.View.OnFocusChangeListener;
+import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
+
/**
* {@link ItemDecoration} for drawing and animating focused view background.
*/
@@ -32,13 +33,7 @@
private FocusIndicatorHelper mHelper;
public FocusedItemDecorator(View container) {
- mHelper = new FocusIndicatorHelper(container) {
-
- @Override
- public void viewToRect(View v, Rect outRect) {
- outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
- }
- };
+ mHelper = new SimpleFocusIndicatorHelper(container);
}
public OnFocusChangeListener getFocusListener() {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 00ee009..01b1424 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -15,16 +15,15 @@
*/
package com.android.launcher3.logging;
+import android.content.Context;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.view.View;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.ButtonDropTarget;
-import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.InfoDropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.UninstallDropTarget;
import com.android.launcher3.userevent.nano.LauncherLogExtensions.TargetExtension;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -32,6 +31,7 @@
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;
@@ -71,7 +71,7 @@
switch (action.type) {
case Action.Type.TOUCH:
str += getFieldName(action.touch, Action.Touch.class);
- if (action.touch == Action.Touch.SWIPE) {
+ if (action.touch == Action.Touch.SWIPE || action.touch == Action.Touch.FLING) {
str += " direction=" + getFieldName(action.dir, Action.Direction.class);
}
return str;
@@ -114,25 +114,37 @@
if (t.intentHash != 0) {
typeStr += ", intentHash=" + t.intentHash;
}
- if (t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) {
- typeStr += ", predictiveRank=" + t.predictedRank;
+ if ((t.packageNameHash != 0 || t.componentHash != 0 || t.intentHash != 0) &&
+ t.itemType != ItemType.TASK) {
+ typeStr += ", predictiveRank=" + t.predictedRank + ", grid(" + t.gridX + "," + t.gridY
+ + "), span(" + t.spanX + "," + t.spanY
+ + "), pageIdx=" + t.pageIndex;
+
}
- return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
- + "), pageIdx=" + t.pageIndex;
+ return typeStr;
}
- public static Target newItemTarget(View v) {
+ 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.getTag() instanceof ItemInfo)
- ? newItemTarget((ItemInfo) v.getTag())
+ ? newItemTarget((ItemInfo) v.getTag(), instantAppResolver)
: newTarget(Target.Type.ITEM);
}
- public static Target newItemTarget(ItemInfo info) {
+ 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 = ItemType.APP_ICON;
+ t.itemType = (instantAppResolver != null && info instanceof AppInfo
+ && instantAppResolver.isInstantApp(((AppInfo) info)) )
+ ? ItemType.WEB_APP
+ : ItemType.APP_ICON;
t.predictedRank = -100; // Never assigned
break;
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
@@ -156,12 +168,8 @@
return newTarget(Target.Type.CONTAINER);
}
Target t = newTarget(Target.Type.CONTROL);
- if (v instanceof InfoDropTarget) {
- t.controlType = ControlType.APPINFO_TARGET;
- } else if (v instanceof UninstallDropTarget) {
- t.controlType = ControlType.UNINSTALL_TARGET;
- } else if (v instanceof DeleteDropTarget) {
- t.controlType = ControlType.REMOVE_TARGET;
+ if (v instanceof ButtonDropTarget) {
+ t.controlType = ((ButtonDropTarget) v).getControlTypeForLogging();
}
return t;
}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 243dbea..90355bd 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -27,15 +27,18 @@
import android.view.View;
import android.view.ViewParent;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.LogConfig;
import java.util.Locale;
@@ -64,8 +67,7 @@
FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
private static final String UUID_STORAGE = "uuid";
- public static UserEventDispatcher newInstance(Context context, boolean isInLandscapeMode,
- boolean isInMultiWindowMode) {
+ public static UserEventDispatcher newInstance(Context context, DeviceProfile dp) {
SharedPreferences sharedPrefs = Utilities.getDevicePrefs(context);
String uuidStr = sharedPrefs.getString(UUID_STORAGE, null);
if (uuidStr == null) {
@@ -74,9 +76,10 @@
}
UserEventDispatcher ued = Utilities.getOverrideObject(UserEventDispatcher.class,
context.getApplicationContext(), R.string.user_event_dispatcher_class);
- ued.mIsInLandscapeMode = isInLandscapeMode;
- ued.mIsInMultiWindowMode = isInMultiWindowMode;
+ ued.mIsInLandscapeMode = dp.isVerticalBarLayout();
+ ued.mIsInMultiWindowMode = dp.isMultiWindowMode;
ued.mUuidStr = uuidStr;
+ ued.mInstantAppResolver = InstantAppResolver.newInstance(context);
return ued;
}
@@ -125,6 +128,7 @@
private boolean mIsInMultiWindowMode;
private boolean mIsInLandscapeMode;
private String mUuidStr;
+ protected InstantAppResolver mInstantAppResolver;
// APP_ICON SHORTCUT WIDGET
// --------------------------------------------------------------
@@ -150,7 +154,7 @@
public void logAppLaunch(View v, Intent intent) {
LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
- newItemTarget(v), newTarget(Target.Type.CONTAINER));
+ newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
if (fillInLogContainerData(event, v)) {
fillIntentInfo(event.srcTarget[0], intent);
@@ -158,9 +162,23 @@
dispatchUserEvent(event, intent);
}
+ public void logTaskLaunch(int action, int direction, ComponentName componentName){
+ LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE
+ newTarget(Target.Type.ITEM));
+ if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) {
+ event.action.dir = direction;
+ }
+ event.srcTarget[0].itemType = LauncherLogProto.ItemType.TASK;
+ fillComponentInfo(event.srcTarget[0], componentName);
+ dispatchUserEvent(event, null);
+ }
+
protected void fillIntentInfo(Target target, Intent intent) {
target.intentHash = intent.hashCode();
- ComponentName cn = intent.getComponent();
+ 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();
@@ -169,49 +187,76 @@
public void logNotificationLaunch(View v, PendingIntent intent) {
LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.TAP),
- newItemTarget(v), newTarget(Target.Type.CONTAINER));
+ newItemTarget(v, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
if (fillInLogContainerData(event, v)) {
event.srcTarget[0].packageNameHash = (mUuidStr + intent.getCreatorPackage()).hashCode();
}
dispatchUserEvent(event, null);
}
- public void logActionCommand(int command, int containerType) {
- logActionCommand(command, newContainerTarget(containerType));
+ public void logActionCommand(int command, Target srcTarget) {
+ logActionCommand(command, srcTarget, null);
}
- public void logActionCommand(int command, Target target) {
- LauncherEvent event = newLauncherEvent(newCommandAction(command), target);
+ public void logActionCommand(int command, int srcContainerType, int dstContainerType) {
+ logActionCommand(command, newContainerTarget(srcContainerType),
+ dstContainerType >=0 ? newContainerTarget(dstContainerType) : null);
+ }
+
+ public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
+ LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
+ 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 containerType) {
+ public void logActionCommand(int command, View itemView, int srcContainerType) {
LauncherEvent event = newLauncherEvent(newCommandAction(command),
- newItemTarget(itemView), newTarget(Target.Type.CONTAINER));
+ newItemTarget(itemView, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
if (fillInLogContainerData(event, itemView)) {
// TODO: Remove the following two lines once fillInLogContainerData can take in a
// container view.
event.srcTarget[0].type = Target.Type.CONTAINER;
- event.srcTarget[0].containerType = containerType;
+ event.srcTarget[0].containerType = srcContainerType;
}
dispatchUserEvent(event, null);
}
public void logActionOnControl(int action, int controlType) {
- logActionOnControl(action, controlType, null);
+ logActionOnControl(action, controlType, null, -1);
+ }
+
+ public void logActionOnControl(int action, int controlType, int parentContainerType) {
+ logActionOnControl(action, controlType, null, parentContainerType);
}
public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer) {
- final LauncherEvent event = controlInContainer == null
+ logActionOnControl(action, controlType, controlInContainer, -1);
+ }
+
+ public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
+ int parentContainerType) {
+ final LauncherEvent event = (controlInContainer == null && parentContainerType < 0)
? newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL))
: newLauncherEvent(newTouchAction(action), newTarget(Target.Type.CONTROL),
newTarget(Target.Type.CONTAINER));
event.srcTarget[0].controlType = controlType;
- fillInLogContainerData(event, controlInContainer);
+ if (controlInContainer != null) {
+ fillInLogContainerData(event, controlInContainer);
+ }
+ if (parentContainerType >= 0) {
+ event.srcTarget[1].containerType = parentContainerType;
+ }
+ if (action == Action.Touch.DRAGDROP) {
+ event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
+ }
dispatchUserEvent(event, null);
}
@@ -232,10 +277,35 @@
event.action.dir = dir;
event.srcTarget[0].pageIndex = pageIndex;
dispatchUserEvent(event, null);
+ }
- if (action == Action.Touch.SWIPE) {
- resetElapsedContainerMillis();
+ /**
+ * 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 the foreground activity
+ * (2) APP: if another app was the foreground activity
+ */
+ public void logStateChangeAction(int action, int dir, int srcChildTargetType,
+ int srcParentContainerType, int dstContainerType,
+ int pageIndex) {
+ LauncherEvent event;
+ if (srcChildTargetType == LauncherLogProto.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;
+ dispatchUserEvent(event, null);
+ resetElapsedContainerMillis("state changed");
}
public void logActionOnItem(int action, int dir, int itemType) {
@@ -253,11 +323,11 @@
}
ItemInfo info = (ItemInfo) icon.getTag();
LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.LONGPRESS),
- newItemTarget(info), newTarget(Target.Type.CONTAINER));
+ newItemTarget(info, mInstantAppResolver), newTarget(Target.Type.CONTAINER));
provider.fillInLogContainerData(icon, info, event.srcTarget[0], event.srcTarget[1]);
dispatchUserEvent(event, null);
- resetElapsedContainerMillis();
+ resetElapsedContainerMillis("deep shortcut open");
}
/* Currently we are only interested in whether this event happens or not and don't
@@ -271,9 +341,11 @@
public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
LauncherEvent event = newLauncherEvent(newTouchAction(Action.Touch.DRAGDROP),
- newItemTarget(dragObj.originalDragInfo), newTarget(Target.Type.CONTAINER));
+ newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
+ newTarget(Target.Type.CONTAINER));
event.destTarget = new Target[] {
- newItemTarget(dragObj.originalDragInfo), newDropTarget(dropTargetAsView)
+ newItemTarget(dragObj.originalDragInfo, mInstantAppResolver),
+ newDropTarget(dropTargetAsView)
};
dragObj.dragSource.fillInLogContainerData(null, dragObj.originalDragInfo,
@@ -290,9 +362,15 @@
/**
* Currently logs following containers: workspace, allapps, widget tray.
+ * @param reason
*/
- public final void resetElapsedContainerMillis() {
+ public final void resetElapsedContainerMillis(String reason) {
mElapsedContainerMillis = SystemClock.uptimeMillis();
+ if (!IS_VERBOSE) {
+ return;
+ }
+ Log.d(TAG, "resetElapsedContainerMillis reason=" + reason);
+
}
public final void resetElapsedSessionMillis() {
@@ -321,13 +399,13 @@
log += "\n Destination " + getTargetsStr(ev.destTarget);
}
log += String.format(Locale.US,
- "\n Elapsed container %d ms session %d ms action %d ms",
+ "\n Elapsed container %d ms, session %d ms, action %d ms",
ev.elapsedContainerMillis,
ev.elapsedSessionMillis,
ev.actionDurationMillis);
log += "\n isInLandscapeMode " + ev.isInLandscapeMode;
log += "\n isInMultiWindowMode " + ev.isInMultiWindowMode;
- log += "\n";
+ log += "\n\n";
Log.d(TAG, log);
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index a33a039..fefe07d 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -17,10 +17,7 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.LauncherActivityInfo;
-import android.os.Process;
import android.os.UserHandle;
-import android.util.ArrayMap;
import android.util.LongSparseArray;
import android.util.Pair;
import com.android.launcher3.AllAppsList;
@@ -37,7 +34,6 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.GridOccupancy;
-import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
import java.util.ArrayList;
import java.util.List;
@@ -64,7 +60,6 @@
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<>();
- ArrayMap<UserHandle, UserFolderInfo> userFolderMap = new ArrayMap<>();
// Get the list of workspace screens. We need to append to this list and
// can not use sBgWorkspaceScreens because loadWorkspace() may not have been
@@ -87,21 +82,6 @@
if (item instanceof AppInfo) {
item = ((AppInfo) item).makeShortcut();
}
-
- if (!Process.myUserHandle().equals(item.user)) {
- // Check if this belongs to a work folder.
- if (!(entry.second instanceof LauncherActivityInfo)) {
- continue;
- }
-
- UserFolderInfo userFolderInfo = userFolderMap.get(item.user);
- if (userFolderInfo == null) {
- userFolderInfo = new UserFolderInfo(context, item.user, dataModel);
- userFolderMap.put(item.user, userFolderInfo);
- }
- item = userFolderInfo.convertToWorkspaceItem(
- (ShortcutInfo) item, (LauncherActivityInfo) entry.second);
- }
}
if (item != null) {
filteredItems.add(item);
@@ -160,10 +140,6 @@
}
});
}
-
- for (UserFolderInfo userFolderInfo : userFolderMap.values()) {
- userFolderInfo.applyPendingState(getModelWriter());
- }
}
protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) {
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9aa30e7..fcdc088 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -29,7 +29,6 @@
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.android.launcher3.widget.WidgetsListAdapter;
import java.util.ArrayList;
import java.util.concurrent.Executor;
@@ -80,19 +79,18 @@
*/
public final void scheduleCallbackTask(final CallbackTask task) {
final Callbacks callbacks = mModel.getCallback();
- mUiExecutor.execute(new Runnable() {
- public void run() {
- Callbacks cb = mModel.getCallback();
- if (callbacks == cb && cb != null) {
- task.execute(callbacks);
- }
+ mUiExecutor.execute(() -> {
+ Callbacks cb = mModel.getCallback();
+ if (callbacks == cb && cb != null) {
+ task.execute(callbacks);
}
});
}
public ModelWriter getModelWriter() {
- // Updates from model task, do not deal with icon position in hotseat.
- return mModel.getWriter(false /* hasVerticalHotseat */);
+ // Updates from model task, do not deal with icon position in hotseat. Also no need to
+ // verify changes as the ModelTasks always push the changes to callbacks
+ return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 8640401..fff1e69 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -106,6 +106,11 @@
public final WidgetsModel widgetsModel = new WidgetsModel();
/**
+ * Id when the model was last bound
+ */
+ public int lastBindId = 0;
+
+ /**
* Clears all the data
*/
public synchronized void clear() {
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
new file mode 100644
index 0000000..1149b55
--- /dev/null
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -0,0 +1,164 @@
+/*
+ * 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.model;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.util.MultiHashMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Helper class to send broadcasts to package installers that have:
+ * - Items on the first screen
+ * - Items with an active install session
+ *
+ * The packages are broken down by: folder items, workspace items, hotseat items, and widgets.
+ *
+ * Package installers only receive data for items that they are installing.
+ */
+public class FirstScreenBroadcast {
+
+ private static final String TAG = "FirstScreenBroadcast";
+ private static final boolean DEBUG = false;
+
+ private static final String ACTION_FIRST_SCREEN_ACTIVE_INSTALLS
+ = "com.android.launcher3.action.FIRST_SCREEN_ACTIVE_INSTALLS";
+
+ private static final String FOLDER_ITEM_EXTRA = "folderItem";
+ private static final String WORKSPACE_ITEM_EXTRA = "workspaceItem";
+ private static final String HOTSEAT_ITEM_EXTRA = "hotseatItem";
+ private static final String WIDGET_ITEM_EXTRA = "widgetItem";
+
+ private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
+
+ private final MultiHashMap<String, String> mPackagesForInstaller;
+
+ public FirstScreenBroadcast(HashMap<String, 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<String, SessionInfo> sessionInfoForPackage) {
+ MultiHashMap<String, String> packagesForInstaller = new MultiHashMap<>();
+ for (Map.Entry<String, SessionInfo> entry : sessionInfoForPackage.entrySet()) {
+ packagesForInstaller.addToList(entry.getValue().getInstallerPackageName(),
+ entry.getKey());
+ }
+ return packagesForInstaller;
+ }
+
+ /**
+ * Sends a broadcast to all package installers that have items with active sessions on the users
+ * 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);
+ }
+ }
+
+ /**
+ * @param installerPackageName Package name of the package installer.
+ * @param packages List of packages with active sessions for this package installer.
+ * @param firstScreenItems List of items on the first screen.
+ */
+ private void sendBroadcastToInstaller(Context context, String installerPackageName,
+ List<String> packages, List<ItemInfo> firstScreenItems) {
+ Set<String> folderItems = new HashSet<>();
+ Set<String> workspaceItems = new HashSet<>();
+ Set<String> hotseatItems = new HashSet<>();
+ Set<String> widgetItems = new HashSet<>();
+
+ for (ItemInfo info : firstScreenItems) {
+ if (info instanceof FolderInfo) {
+ FolderInfo folderInfo = (FolderInfo) info;
+ String folderItemInfoPackage;
+ for (ItemInfo folderItemInfo : folderInfo.contents) {
+ folderItemInfoPackage = getPackageName(folderItemInfo);
+ if (folderItemInfoPackage != null
+ && packages.contains(folderItemInfoPackage)) {
+ folderItems.add(folderItemInfoPackage);
+ }
+ }
+ }
+
+ String packageName = getPackageName(info);
+ if (packageName == null || !packages.contains(packageName)) {
+ continue;
+ }
+ if (info instanceof LauncherAppWidgetInfo) {
+ widgetItems.add(packageName);
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ hotseatItems.add(packageName);
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ workspaceItems.add(packageName);
+ }
+ }
+
+ if (DEBUG) {
+ printList(installerPackageName, "Folder item", folderItems);
+ printList(installerPackageName, "Workspace item", workspaceItems);
+ printList(installerPackageName, "Hotseat item", hotseatItems);
+ printList(installerPackageName, "Widget item", widgetItems);
+ }
+
+ context.sendBroadcast(new Intent(ACTION_FIRST_SCREEN_ACTIVE_INSTALLS)
+ .setPackage(installerPackageName)
+ .putStringArrayListExtra(FOLDER_ITEM_EXTRA, new ArrayList<>(folderItems))
+ .putStringArrayListExtra(WORKSPACE_ITEM_EXTRA, new ArrayList<>(workspaceItems))
+ .putStringArrayListExtra(HOTSEAT_ITEM_EXTRA, new ArrayList<>(hotseatItems))
+ .putStringArrayListExtra(WIDGET_ITEM_EXTRA, new ArrayList<>(widgetItems))
+ .putExtra(VERIFICATION_TOKEN_EXTRA, PendingIntent.getActivity(context, 0,
+ new Intent(), PendingIntent.FLAG_ONE_SHOT)));
+ }
+
+ private static String getPackageName(ItemInfo info) {
+ String packageName = null;
+ if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+ if (widgetInfo.providerName != null) {
+ packageName = widgetInfo.providerName.getPackageName();
+ }
+ } else if (info.getTargetComponent() != null){
+ packageName = info.getTargetComponent().getPackageName();
+ }
+ return packageName;
+ }
+
+ private static void printList(String packageInstaller, String label, Set<String> packages) {
+ for (String pkg : packages) {
+ Log.d(TAG, packageInstaller + ":" + label + ":" + pkg);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 24e5b9c..0fd9b73 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -25,7 +25,6 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
@@ -37,7 +36,6 @@
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetListRowEntry;
-import com.android.launcher3.widget.WidgetsListAdapter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -100,6 +98,7 @@
workspaceItems.addAll(mBgDataModel.workspaceItems);
appWidgets.addAll(mBgDataModel.appWidgets);
orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
+ mBgDataModel.lastBindId++;
}
final int currentScreen;
@@ -162,7 +161,7 @@
// 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(mUiExecutor) : mainExecutor;
+ validFirstPage ? new ViewOnDrawExecutor() : mainExecutor;
mainExecutor.execute(new Runnable() {
@Override
@@ -210,7 +209,7 @@
/** Filters the set of items who are directly or indirectly (via another container) on the
* specified screen. */
- private <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
+ public static <T extends ItemInfo> void filterCurrentWorkspaceItems(long currentScreenId,
ArrayList<T> allWorkspaceItems,
ArrayList<T> currentScreenItems,
ArrayList<T> otherScreenItems) {
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 00dd3aa..06da843 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -16,6 +16,12 @@
package com.android.launcher3.model;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+import static com.android.launcher3.model.LoaderResults.filterCurrentWorkspaceItems;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -24,8 +30,8 @@
import android.content.IntentFilter;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionInfo;
import android.graphics.Bitmap;
-import android.graphics.drawable.AdaptiveIconDrawable;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
@@ -36,7 +42,6 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.ClickShadowView;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.IconCache;
import com.android.launcher3.InstallShortcutReceiver;
@@ -62,7 +67,6 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LooperIdleLock;
-import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Provider;
@@ -76,11 +80,6 @@
import java.util.Map;
import java.util.concurrent.CancellationException;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
-import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
-import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
-
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -95,6 +94,8 @@
private final AllAppsList mBgAllAppsList;
private final BgDataModel mBgDataModel;
+ private FirstScreenBroadcast mFirstScreenBroadcast;
+
private final LoaderResults mResults;
private final LauncherAppsCompat mLauncherApps;
@@ -137,6 +138,22 @@
}
}
+ private void sendFirstScreenActiveInstallsBroadcast() {
+ ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
+
+ ArrayList<ItemInfo> allItems = new ArrayList<>();
+ synchronized (mBgDataModel) {
+ allItems.addAll(mBgDataModel.workspaceItems);
+ allItems.addAll(mBgDataModel.appWidgets);
+ }
+ long firstScreen = mBgDataModel.workspaceScreens.isEmpty()
+ ? -1 // In this case, we can still look at the items in the hotseat.
+ : mBgDataModel.workspaceScreens.get(0);
+ filterCurrentWorkspaceItems(firstScreen, allItems, firstScreenItems,
+ new ArrayList<>() /* otherScreenItems are ignored */);
+ mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+ }
+
public void run() {
synchronized (this) {
// Skip fast if we are already stopped.
@@ -147,15 +164,17 @@
TraceHelper.beginSection(TAG);
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
- TraceHelper.partitionSection(TAG, "step 1.1: loading UI resources");
- loadUiResources();
- TraceHelper.partitionSection(TAG, "step 1.2: loading workspace");
+ TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
loadWorkspace();
verifyNotStopped();
TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
mResults.bindWorkspace();
+ // Notify the installer packages of packages with active installs on the first screen.
+ TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
+ sendFirstScreenActiveInstallsBroadcast();
+
// Take a break
TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
waitForIdle();
@@ -212,15 +231,6 @@
this.notify();
}
- public void loadUiResources() {
- if (Utilities.ATLEAST_OREO) {
- LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
- ClickShadowView.setAdaptiveIconScaleFactor(li.getNormalizer()
- .getScale(new AdaptiveIconDrawable(null, null), null, null, null));
- li.recycle();
- }
- }
-
private void loadWorkspace() {
final Context context = mApp.getContext();
final ContentResolver contentResolver = context.getContentResolver();
@@ -256,8 +266,9 @@
synchronized (mBgDataModel) {
mBgDataModel.clear();
- final HashMap<String, Integer> installingPkgs =
+ final HashMap<String, SessionInfo> installingPkgs =
mPackageInstaller.updateAndGetActiveSessionCache();
+ mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
@@ -525,11 +536,11 @@
}
if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
- Integer progress = installingPkgs.get(targetPkg);
- if (progress != null) {
- info.setInstallProgress(progress);
- } else {
+ SessionInfo si = installingPkgs.get(targetPkg);
+ if (si == null) {
info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+ } else {
+ info.setInstallProgress((int) (si.getProgress() * 100));
}
}
@@ -619,7 +630,11 @@
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
component);
appWidgetInfo.restoreStatus = c.restoreFlag;
- Integer installProgress = installingPkgs.get(component.getPackageName());
+ SessionInfo si =
+ installingPkgs.get(component.getPackageName());
+ Integer installProgress = si == null
+ ? null
+ : (int) (si.getProgress() * 100);
if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
// Restore has started once.
@@ -812,8 +827,6 @@
// This builds the icon bitmaps.
mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
}
-
- ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user);
}
if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
index 6f33bed..f186e95 100644
--- a/src/com/android/launcher3/model/ModelPreload.java
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -49,11 +49,8 @@
@Override
public final void run() {
- if (!mModel.isModelLoaded()) {
- Log.d(TAG, "Workspace not loaded, loading now");
- mModel.startLoaderForResults(
- new LoaderResults(mApp, mBgDataModel, mAllAppsList, 0, null));
- }
+ mModel.startLoaderForResultsIfNotLoaded(
+ new LoaderResults(mApp, mBgDataModel, mAllAppsList, 0, null));
Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
onComplete(mModel.isModelLoaded());
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 032ed78..eba7515 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -21,17 +21,21 @@
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LooperExecutor;
@@ -48,15 +52,23 @@
private static final String TAG = "ModelWriter";
private final Context mContext;
+ private final LauncherModel mModel;
private final BgDataModel mBgDataModel;
+ private final Handler mUiHandler;
+
private final Executor mWorkerExecutor;
private final boolean mHasVerticalHotseat;
+ private final boolean mVerifyChanges;
- public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
+ public ModelWriter(Context context, LauncherModel model, BgDataModel dataModel,
+ boolean hasVerticalHotseat, boolean verifyChanges) {
mContext = context;
+ mModel = model;
mBgDataModel = dataModel;
mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
mHasVerticalHotseat = hasVerticalHotseat;
+ mVerifyChanges = verifyChanges;
+ mUiHandler = new Handler(Looper.getMainLooper());
}
private void updateItemInfoProps(
@@ -212,15 +224,16 @@
item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getLong(Settings.EXTRA_VALUE);
writer.put(Favorites._ID, item.id);
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- mWorkerExecutor.execute(new Runnable() {
- public void run() {
- cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+ ModelVerifier verifier = new ModelVerifier();
- synchronized (mBgDataModel) {
- checkItemInfoLocked(item.id, item, stackTrace);
- mBgDataModel.addItem(mContext, item, true);
- }
+ final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+ mWorkerExecutor.execute(() -> {
+ cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+
+ synchronized (mBgDataModel) {
+ checkItemInfoLocked(item.id, item, stackTrace);
+ mBgDataModel.addItem(mContext, item, true);
+ verifier.verifyModel();
}
});
}
@@ -243,14 +256,15 @@
* Removes the specified items from the database
*/
public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
- mWorkerExecutor.execute(new Runnable() {
- public void run() {
- for (ItemInfo item : items) {
- final Uri uri = Favorites.getContentUri(item.id);
- mContext.getContentResolver().delete(uri, null, null);
+ ModelVerifier verifier = new ModelVerifier();
- mBgDataModel.removeItem(mContext, item);
- }
+ mWorkerExecutor.execute(() -> {
+ for (ItemInfo item : items) {
+ final Uri uri = Favorites.getContentUri(item.id);
+ mContext.getContentResolver().delete(uri, null, null);
+
+ mBgDataModel.removeItem(mContext, item);
+ verifier.verifyModel();
}
});
}
@@ -259,17 +273,18 @@
* Remove the specified folder and all its contents from the database.
*/
public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
- mWorkerExecutor.execute(new Runnable() {
- public void run() {
- ContentResolver cr = mContext.getContentResolver();
- cr.delete(LauncherSettings.Favorites.CONTENT_URI,
- LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
- mBgDataModel.removeItem(mContext, info.contents);
- info.contents.clear();
+ ModelVerifier verifier = new ModelVerifier();
- cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
- mBgDataModel.removeItem(mContext, info);
- }
+ mWorkerExecutor.execute(() -> {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+ LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
+ mBgDataModel.removeItem(mContext, info.contents);
+ info.contents.clear();
+
+ cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
+ mBgDataModel.removeItem(mContext, info);
+ verifier.verifyModel();
});
}
@@ -324,6 +339,7 @@
private abstract class UpdateItemBaseRunnable implements Runnable {
private final StackTraceElement[] mStackTrace;
+ private final ModelVerifier mVerifier = new ModelVerifier();
UpdateItemBaseRunnable() {
mStackTrace = new Throwable().getStackTrace();
@@ -368,7 +384,45 @@
} else {
mBgDataModel.workspaceItems.remove(modelItem);
}
+ mVerifier.verifyModel();
}
}
}
+
+ /**
+ * Utility class to verify model updates are propagated properly to the callback.
+ */
+ public class ModelVerifier {
+
+ final int startId;
+
+ ModelVerifier() {
+ startId = mBgDataModel.lastBindId;
+ }
+
+ void verifyModel() {
+ if (!mVerifyChanges || mModel.getCallback() == null) {
+ return;
+ }
+
+ int executeId = mBgDataModel.lastBindId;
+
+ mUiHandler.post(() -> {
+ int currentId = mBgDataModel.lastBindId;
+ if (currentId > executeId) {
+ // Model was already bound after job was executed.
+ return;
+ }
+ if (executeId == startId) {
+ // Bound model has not changed during the job
+ return;
+ }
+ // Bound model was changed between submitting the job and executing the job
+ Callbacks callbacks = mModel.getCallback();
+ if (callbacks != null) {
+ callbacks.rebindModel();
+ }
+ });
+ }
+ }
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 1ff0dac..9f8f263 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,6 +1,8 @@
package com.android.launcher3.model;
+import static android.appwidget.AppWidgetProviderInfo.WIDGET_FEATURE_HIDE_FROM_PICKER;
+
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -153,6 +155,11 @@
// 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);
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 2fefa85..32410a6 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
-import android.support.annotation.Nullable;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.MarginLayoutParams;
@@ -83,7 +82,7 @@
public void addGutter() {
if (mGutter == null) {
- mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter);
+ mGutter = mContainer.inflateAndAdd(R.layout.notification_gutter, mContainer);
}
}
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 114b2b8..1fd7078 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,6 +16,8 @@
package com.android.launcher3.notification;
+import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
+
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -43,8 +45,6 @@
import java.util.Map;
import java.util.Set;
-import static com.android.launcher3.SettingsActivity.NOTIFICATION_BADGING;
-
/**
* A {@link NotificationListenerService} that sends updates to its
* {@link NotificationsChangedListener} when notifications are posted or canceled,
@@ -71,6 +71,11 @@
private final Ranking mTempRanking = new Ranking();
/** Maps groupKey's to the corresponding group of notifications. */
private final Map<String, NotificationGroup> mNotificationGroupMap = new HashMap<>();
+ /** Maps keys to their corresponding current group key */
+ private final Map<String, String> mNotificationGroupKeyMap = new HashMap<>();
+
+ /** The last notification key that was dismissed from launcher UI */
+ private String mLastKeyDismissedByLauncher;
private SettingsObserver mNotificationBadgingObserver;
@@ -249,13 +254,67 @@
}
NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+ String key = sbn.getKey();
if (notificationGroup != null) {
- notificationGroup.removeChildKey(sbn.getKey());
+ notificationGroup.removeChildKey(key);
if (notificationGroup.isEmpty()) {
- cancelNotification(notificationGroup.getGroupSummaryKey());
+ if (key.equals(mLastKeyDismissedByLauncher)) {
+ // Only cancel the group notification if launcher dismissed the last child.
+ cancelNotification(notificationGroup.getGroupSummaryKey());
+ }
mNotificationGroupMap.remove(sbn.getGroupKey());
}
}
+ if (key.equals(mLastKeyDismissedByLauncher)) {
+ mLastKeyDismissedByLauncher = null;
+ }
+ }
+
+ public void cancelNotificationFromLauncher(String key) {
+ mLastKeyDismissedByLauncher = key;
+ cancelNotification(key);
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(RankingMap rankingMap) {
+ super.onNotificationRankingUpdate(rankingMap);
+ String[] keys = rankingMap.getOrderedKeys();
+ for (StatusBarNotification sbn : getActiveNotifications(keys)) {
+ updateGroupKeyIfNecessary(sbn);
+ }
+ }
+
+ private void updateGroupKeyIfNecessary(StatusBarNotification sbn) {
+ String childKey = sbn.getKey();
+ String oldGroupKey = mNotificationGroupKeyMap.get(childKey);
+ String newGroupKey = sbn.getGroupKey();
+ if (oldGroupKey == null || !oldGroupKey.equals(newGroupKey)) {
+ // The group key has changed.
+ mNotificationGroupKeyMap.put(childKey, newGroupKey);
+ if (oldGroupKey != null && mNotificationGroupMap.containsKey(oldGroupKey)) {
+ // Remove the child key from the old group.
+ NotificationGroup oldGroup = mNotificationGroupMap.get(oldGroupKey);
+ oldGroup.removeChildKey(childKey);
+ if (oldGroup.isEmpty()) {
+ mNotificationGroupMap.remove(oldGroupKey);
+ }
+ }
+ }
+ if (sbn.isGroup() && newGroupKey != null) {
+ // Maintain group info so we can cancel the summary when the last child is canceled.
+ NotificationGroup notificationGroup = mNotificationGroupMap.get(newGroupKey);
+ if (notificationGroup == null) {
+ notificationGroup = new NotificationGroup();
+ mNotificationGroupMap.put(newGroupKey, notificationGroup);
+ }
+ boolean isGroupSummary = (sbn.getNotification().flags
+ & Notification.FLAG_GROUP_SUMMARY) != 0;
+ if (isGroupSummary) {
+ notificationGroup.setGroupSummaryKey(childKey);
+ } else {
+ notificationGroup.addChildKey(childKey);
+ }
+ }
}
/** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -295,20 +354,7 @@
private boolean shouldBeFilteredOut(StatusBarNotification sbn) {
Notification notification = sbn.getNotification();
- boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
- if (sbn.isGroup()) {
- // Maintain group info so we can cancel the summary when the last child is canceled.
- NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
- if (notificationGroup == null) {
- notificationGroup = new NotificationGroup();
- mNotificationGroupMap.put(sbn.getGroupKey(), notificationGroup);
- }
- if (isGroupHeader) {
- notificationGroup.setGroupSummaryKey(sbn.getKey());
- } else {
- notificationGroup.addChildKey(sbn.getKey());
- }
- }
+ updateGroupKeyIfNecessary(sbn);
getCurrentRanking().getRanking(sbn.getKey(), mTempRanking);
if (!mTempRanking.canShowBadge()) {
@@ -324,6 +370,7 @@
CharSequence title = notification.extras.getCharSequence(Notification.EXTRA_TITLE);
CharSequence text = notification.extras.getCharSequence(Notification.EXTRA_TEXT);
boolean missingTitleAndText = TextUtils.isEmpty(title) && TextUtils.isEmpty(text);
+ boolean isGroupHeader = (notification.flags & Notification.FLAG_GROUP_SUMMARY) != 0;
return (isGroupHeader || missingTitleAndText);
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index 5e3d216..3ce7291 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,41 +15,16 @@
*/
package com.android.launcher3.pageindicators;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.View;
-
/**
* Base class for a page indicator.
*/
-public abstract class PageIndicator extends View {
+public interface PageIndicator {
- protected int mNumPages = 1;
+ void setScroll(int currentScroll, int totalScroll);
- public PageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
+ void setActiveMarker(int activePage);
- public void setScroll(int currentScroll, int totalScroll) {}
+ void setMarkersCount(int numMarkers);
- public void setActiveMarker(int activePage) {}
-
- public void addMarker() {
- mNumPages++;
- onPageCountChanged();
- }
-
- public void removeMarker() {
- mNumPages--;
- onPageCountChanged();
- }
-
- public void setMarkersCount(int numMarkers) {
- mNumPages = numMarkers;
- onPageCountChanged();
- }
-
- protected void onPageCountChanged() {}
-
- public void setShouldAutoHide(boolean shouldAutoHide) {}
+ void setPageDescription(CharSequence description);
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index 6276c80..524ec3c 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -43,7 +43,7 @@
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
*/
-public class PageIndicatorDots extends PageIndicator {
+public class PageIndicatorDots extends View implements PageIndicator {
private static final float SHIFT_PER_ANIMATION = 0.5f;
private static final float SHIFT_THRESHOLD = 0.1f;
@@ -79,6 +79,7 @@
private final int mInActiveColor;
private final boolean mIsRtl;
+ private int mNumPages;
private int mActivePage;
/**
@@ -221,11 +222,17 @@
}
@Override
- protected void onPageCountChanged() {
+ public void setMarkersCount(int numMarkers) {
+ mNumPages = numMarkers;
requestLayout();
}
@Override
+ public void setPageDescription(CharSequence description) {
+ setContentDescription(description);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Add extra spacing of mDotRadius on all sides so than entry animation could be run.
int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index ee4e4ee..4ad7feb 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -1,9 +1,5 @@
package com.android.launcher3.pageindicators;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -20,9 +16,7 @@
import android.util.Property;
import android.view.Gravity;
import android.view.View;
-import android.view.View.OnClickListener;
import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import com.android.launcher3.DeviceProfile;
@@ -31,17 +25,13 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
/**
- * A PageIndicator that briefly shows a fraction of a line when moving between pages in
- * portrait mode. In Landscape simply draws the caret drawable bottom-corner aligned in
- * the drag-layer.
+ * A PageIndicator that briefly shows a fraction of a line when moving between pages
*
* The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
*/
-public class WorkspacePageIndicator extends PageIndicator implements Insettable, OnClickListener {
+public class WorkspacePageIndicator extends View implements Insettable, PageIndicator {
private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
@@ -56,7 +46,6 @@
private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
private final Launcher mLauncher;
- private final AccessibilityManager mAccessibilityManager;
private boolean mShouldAutoHide = true;
@@ -71,8 +60,6 @@
private Paint mLinePaint;
private final int mLineHeight;
- private boolean mIsLandscapeUi;
-
private static final Property<WorkspacePageIndicator, Integer> PAINT_ALPHA
= new Property<WorkspacePageIndicator, Integer>(Integer.class, "paint_alpha") {
@Override
@@ -83,7 +70,7 @@
@Override
public void set(WorkspacePageIndicator obj, Integer alpha) {
obj.mLinePaint.setAlpha(alpha);
- obj.invalidateIfPortrait();
+ obj.invalidate();
}
};
@@ -97,7 +84,7 @@
@Override
public void set(WorkspacePageIndicator obj, Float numPages) {
obj.mNumPagesFloat = numPages;
- obj.invalidateIfPortrait();
+ obj.invalidate();
}
};
@@ -111,7 +98,7 @@
@Override
public void set(WorkspacePageIndicator obj, Integer totalScroll) {
obj.mTotalScroll = totalScroll;
- obj.invalidateIfPortrait();
+ obj.invalidate();
}
};
@@ -138,24 +125,23 @@
boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
- mAccessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
}
@Override
protected void onDraw(Canvas canvas) {
- if (mTotalScroll == 0 || mNumPagesFloat == 0 || mIsLandscapeUi) {
+ if (mTotalScroll == 0 || mNumPagesFloat == 0) {
return;
}
// Compute and draw line rect.
float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
- int availableWidth = canvas.getWidth();
+ int availableWidth = getWidth();
int lineWidth = (int) (availableWidth / mNumPagesFloat);
int lineLeft = (int) (progress * (availableWidth - lineWidth));
int lineRight = lineLeft + lineWidth;
- canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(),
- mLinePaint);
+
+ canvas.drawRoundRect(lineLeft, getHeight() / 2 - mLineHeight / 2, lineRight,
+ getHeight() / 2 + mLineHeight / 2, mLineHeight, mLineHeight, mLinePaint);
}
@Override
@@ -171,7 +157,7 @@
} else if (mTotalScroll != totalScroll) {
animateToTotalScroll(totalScroll);
} else {
- invalidateIfPortrait();
+ invalidate();
}
if (mShouldAutoHide) {
@@ -185,14 +171,24 @@
}
@Override
- public void setActiveMarker(int activePage) {
+ public void setActiveMarker(int activePage) { }
+
+ @Override
+ public void setMarkersCount(int numMarkers) {
+ if (Float.compare(numMarkers, mNumPagesFloat) != 0) {
+ setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numMarkers),
+ NUM_PAGES_ANIMATOR_INDEX);
+ } else {
+ if (mAnimators[NUM_PAGES_ANIMATOR_INDEX] != null) {
+ mAnimators[NUM_PAGES_ANIMATOR_INDEX].cancel();
+ mAnimators[NUM_PAGES_ANIMATOR_INDEX] = null;
+ }
+ }
}
@Override
- protected void onPageCountChanged() {
- if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
- animateToNumPages(mNumPages);
- }
+ public void setPageDescription(CharSequence description) {
+ setContentDescription(description);
}
public void setShouldAutoHide(boolean shouldAutoHide) {
@@ -214,11 +210,6 @@
LINE_ALPHA_ANIMATOR_INDEX);
}
- private void animateToNumPages(int numPages) {
- setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
- NUM_PAGES_ANIMATOR_INDEX);
- }
-
private void animateToTotalScroll(int totalScroll) {
setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
TOTAL_SCROLL_ANIMATOR_INDEX);
@@ -249,54 +240,18 @@
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = mLauncher.getDeviceProfile();
- mIsLandscapeUi = grid.isVerticalBarLayout();
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- if (mIsLandscapeUi) {
- if (grid.isSeascape()) {
- lp.leftMargin = grid.hotseatBarSidePaddingPx;
- lp.rightMargin = insets.right;
- lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;
- } else {
- lp.leftMargin = insets.left;
- lp.rightMargin = grid.hotseatBarSidePaddingPx;
- lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
- }
- lp.bottomMargin = grid.workspacePadding.bottom;
- lp.width = lp.height = getResources()
- .getDimensionPixelSize(R.dimen.dynamic_grid_min_page_indicator_size);
-
- setBackgroundResource(R.drawable.all_apps_handle_landscape);
- setOnFocusChangeListener(mLauncher.mFocusHandler);
- setOnClickListener(this);
-
+ if (grid.isVerticalBarLayout()) {
+ Rect padding = grid.workspacePadding;
+ lp.leftMargin = padding.left + grid.workspaceCellPaddingXPx;
+ lp.rightMargin = padding.right + grid.workspaceCellPaddingXPx;
+ lp.bottomMargin = padding.bottom;
} else {
lp.leftMargin = lp.rightMargin = 0;
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.height = grid.pageIndicatorSizePx;
lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
- lp.width = MATCH_PARENT;
-
- setBackgroundResource(0);
- setOnFocusChangeListener(null);
- setOnClickListener(mAccessibilityManager.isTouchExplorationEnabled() ? this : null);
}
-
setLayoutParams(lp);
}
-
- private void invalidateIfPortrait() {
- if (!mIsLandscapeUi) {
- invalidate();
- }
- }
-
- @Override
- public void onClick(View view) {
- if (!mLauncher.isInState(ALL_APPS)) {
- mLauncher.getUserEventDispatcher().logActionOnControl(
- Action.Touch.TAP, ControlType.ALL_APPS_BUTTON);
- mLauncher.getStateManager().goToState(ALL_APPS);
- }
- }
}
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
new file mode 100644
index 0000000..bd08aaa
--- /dev/null
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -0,0 +1,470 @@
+/*
+ * 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.popup;
+
+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.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.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AccelerateDecelerateInterpolator;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+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 java.util.ArrayList;
+import java.util.Collections;
+
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+
+/**
+ * A container for shortcuts to deep links and notifications associated with an app.
+ */
+public abstract class ArrowPopup extends AbstractFloatingView {
+
+ private final Rect mTempRect = new Rect();
+
+ protected final LayoutInflater mInflater;
+ private final float mOutlineRadius;
+ protected final Launcher mLauncher;
+ protected final boolean mIsRtl;
+
+ private final int mArrayOffset;
+ private final View mArrow;
+
+ protected boolean mIsLeftAligned;
+ protected boolean mIsAboveIcon;
+ private int mGravity;
+
+ protected Animator mOpenCloseAnimator;
+ protected boolean mDeferContainerRemoval;
+ private final Rect mStartRect = new Rect();
+ private final Rect mEndRect = new Rect();
+
+ public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mInflater = LayoutInflater.from(context);
+ mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
+ mLauncher = Launcher.getLauncher(context);
+ mIsRtl = Utilities.isRtl(getResources());
+
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
+ }
+ });
+
+ // 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);
+ mArrow = new View(context);
+ mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
+ mArrayOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
+ }
+
+ public ArrowPopup(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ArrowPopup(Context context) {
+ this(context, null, 0);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ public <T extends View> T inflateAndAdd(int resId, ViewGroup container) {
+ View view = mInflater.inflate(resId, container, false);
+ container.addView(view);
+ return (T) view;
+ }
+
+ /**
+ * Called when all view inflation and reordering in complete.
+ */
+ protected void onInflationComplete(boolean isReversed) { }
+
+ /**
+ * Shows the popup at the desired location, optionally reversing the children.
+ * @param viewsToFlip number of views from the top to to flip in case of reverse order
+ */
+ protected void reorderAndShow(int viewsToFlip) {
+ setVisibility(View.INVISIBLE);
+ mIsOpen = true;
+ mLauncher.getDragLayer().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));
+ }
+ Collections.reverse(allViews);
+ removeAllViews();
+ for (int i = 0; i < count; i++) {
+ addView(allViews.get(i));
+ }
+
+ orientAboutObject();
+ }
+ onInflationComplete(reverseOrder);
+
+ // 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;
+ mLauncher.getDragLayer().addView(mArrow);
+ DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
+ if (mIsLeftAligned) {
+ mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
+ } else {
+ mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
+ }
+
+ 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(mLauncher, 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);
+ mArrow.setElevation(getElevation());
+ }
+
+ mArrow.setPivotX(arrowLp.width / 2);
+ mArrow.setPivotY(mIsAboveIcon ? 0 : arrowLp.height);
+
+ animateOpen();
+ }
+
+ protected boolean isAlignedWithStart() {
+ return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+ }
+
+ /**
+ * Provide the location of the target object relative to the dragLayer.
+ */
+ protected abstract void getTargetObjectLocation(Rect outPos);
+
+ /**
+ * Orients this container above or below the given icon, aligning with the left or right.
+ *
+ * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+ * - Above and left-aligned
+ * - Above and right-aligned
+ * - Below and left-aligned
+ * - Below and right-aligned
+ *
+ * So we always align left if there is enough horizontal space
+ * and align above if there is enough vertical space.
+ */
+ protected void orientAboutObject() {
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ int width = getMeasuredWidth();
+ int extraVerticalSpace = mArrow.getLayoutParams().height + mArrayOffset
+ + getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
+ int height = getMeasuredHeight() + extraVerticalSpace;
+
+ getTargetObjectLocation(mTempRect);
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ Rect insets = dragLayer.getInsets();
+
+ // Align left (right in RTL) if there is room.
+ int leftAlignedX = mTempRect.left;
+ int rightAlignedX = mTempRect.right - width;
+ int x = leftAlignedX;
+ boolean canBeLeftAligned = leftAlignedX + width + insets.left
+ < dragLayer.getRight() - insets.right;
+ boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
+ if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
+ x = rightAlignedX;
+ }
+ mIsLeftAligned = x == leftAlignedX;
+
+ // 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;
+ }
+ x += mIsLeftAligned ? xOffset : -xOffset;
+
+ // Open above icon if there is room.
+ int iconHeight = mTempRect.height();
+ int y = mTempRect.top - height;
+ mIsAboveIcon = y > dragLayer.getTop() + insets.top;
+ if (!mIsAboveIcon) {
+ y = mTempRect.top + iconHeight + extraVerticalSpace;
+ }
+
+ // Insets are added later, so subtract them now.
+ if (mIsRtl) {
+ x += insets.right;
+ } else {
+ x -= insets.left;
+ }
+ y -= insets.top;
+
+ mGravity = 0;
+ if (y + height > dragLayer.getBottom() - insets.bottom) {
+ // The container is opening off the screen, so just center it in the drag layer instead.
+ mGravity = Gravity.CENTER_VERTICAL;
+ // Put the container next to the icon, preferring the right side in ltr (left in rtl).
+ int rightSide = leftAlignedX + iconWidth - insets.left;
+ int leftSide = rightAlignedX - iconWidth - insets.left;
+ if (!mIsRtl) {
+ if (rightSide + width < dragLayer.getRight()) {
+ x = rightSide;
+ mIsLeftAligned = true;
+ } else {
+ x = leftSide;
+ mIsLeftAligned = false;
+ }
+ } else {
+ if (leftSide > dragLayer.getLeft()) {
+ x = leftSide;
+ mIsLeftAligned = false;
+ } else {
+ x = rightSide;
+ mIsLeftAligned = true;
+ }
+ }
+ mIsAboveIcon = true;
+ }
+
+ setX(x);
+ if (Gravity.isVertical(mGravity)) {
+ return;
+ }
+
+ DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
+ if (mIsAboveIcon) {
+ arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
+ lp.bottomMargin =
+ mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top;
+ arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
+ } else {
+ arrowLp.gravity = lp.gravity = Gravity.TOP;
+ lp.topMargin = y + insets.top;
+ arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrayOffset;
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ // enforce contained is within screen
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) {
+ // If we are still off screen, center horizontally too.
+ mGravity |= Gravity.CENTER_HORIZONTAL;
+ }
+
+ if (Gravity.isHorizontal(mGravity)) {
+ setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
+ mArrow.setVisibility(INVISIBLE);
+ }
+ if (Gravity.isVertical(mGravity)) {
+ setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
+ }
+ }
+
+ private void animateOpen() {
+ setVisibility(View.VISIBLE);
+
+ final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+ final Resources res = getResources();
+ final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal.
+ final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, false);
+ revealAnim.setDuration(revealDuration);
+ revealAnim.setInterpolator(revealInterpolator);
+
+ Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
+ fadeIn.setDuration(revealDuration);
+ fadeIn.setInterpolator(revealInterpolator);
+ openAnim.play(fadeIn);
+
+ // Animate the arrow.
+ mArrow.setScaleX(0);
+ mArrow.setScaleY(0);
+ Animator arrowScale = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
+ .setDuration(res.getInteger(R.integer.config_popupArrowOpenDuration));
+
+ openAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ sendCustomAccessibilityEvent(
+ ArrowPopup.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ getContext().getString(R.string.action_deep_shortcut));
+ }
+ });
+
+ mOpenCloseAnimator = openAnim;
+ openAnim.playSequentially(revealAnim, arrowScale);
+ openAnim.start();
+ }
+
+ protected void animateClose() {
+ if (!mIsOpen) {
+ return;
+ }
+ mEndRect.setEmpty();
+ if (getOutlineProvider() instanceof RevealOutlineAnimation) {
+ ((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
+ }
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ }
+ mIsOpen = false;
+
+ final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+ // Hide the arrow
+ closeAnim.play(ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0));
+ closeAnim.play(ObjectAnimator.ofFloat(mArrow, ALPHA, 0));
+
+ final Resources res = getResources();
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal (reversed).
+ final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, true);
+ revealAnim.setInterpolator(revealInterpolator);
+ closeAnim.play(revealAnim);
+
+ Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+ fadeOut.setInterpolator(revealInterpolator);
+ closeAnim.play(fadeOut);
+
+ onCreateCloseAnimation(closeAnim);
+ closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
+ closeAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ if (mDeferContainerRemoval) {
+ setVisibility(INVISIBLE);
+ } else {
+ closeComplete();
+ }
+ }
+ });
+ mOpenCloseAnimator = closeAnim;
+ closeAnim.start();
+ }
+
+ /**
+ * Called when creating the close transition allowing subclass can add additional animations.
+ */
+ protected void onCreateCloseAnimation(AnimatorSet anim) { }
+
+ private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+ int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+ R.dimen.popup_arrow_horizontal_center_start:
+ R.dimen.popup_arrow_horizontal_center_end);
+ if (!mIsLeftAligned) {
+ arrowCenterX = getMeasuredWidth() - arrowCenterX;
+ }
+ int arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
+
+ mStartRect.set(arrowCenterX, arrowCenterY, arrowCenterX, arrowCenterY);
+ if (mEndRect.isEmpty()) {
+ mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ }
+
+ return new RoundedRectRevealOutlineProvider
+ (mOutlineRadius, mOutlineRadius, mStartRect, mEndRect);
+ }
+
+ /**
+ * Closes the popup without animation.
+ */
+ protected void closeComplete() {
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ mOpenCloseAnimator = null;
+ }
+ mIsOpen = false;
+ mDeferContainerRemoval = false;
+ mLauncher.getDragLayer().removeView(this);
+ mLauncher.getDragLayer().removeView(mArrow);
+ }
+}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b3ef7bb..422a4ec 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -16,36 +16,28 @@
package com.android.launcher3.popup;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+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 android.animation.AnimatorSet;
import android.animation.LayoutTransition;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.CornerPathEffect;
-import android.graphics.Outline;
-import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.ViewOutlineProvider;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.ImageView;
import com.android.launcher3.AbstractFloatingView;
@@ -56,20 +48,15 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherModel;
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.anim.RevealOutlineAnimation;
-import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.badge.BadgeInfo;
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.graphics.TriangleShape;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationItemView;
@@ -77,86 +64,40 @@
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Themes;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-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;
-
/**
* A container for shortcuts to deep links and notifications associated with an app.
*/
@TargetApi(Build.VERSION_CODES.N)
-public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
+public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
DragController.DragListener, View.OnLongClickListener,
View.OnTouchListener {
private final List<DeepShortcutView> mShortcuts = new ArrayList<>();
private final PointF mInterceptTouchDown = new PointF();
- private final Rect mTempRect = new Rect();
private final Point mIconLastTouchPos = new Point();
private final int mStartDragThreshold;
- private final LayoutInflater mInflater;
- private final float mOutlineRadius;
- private final Launcher mLauncher;
private final LauncherAccessibilityDelegate mAccessibilityDelegate;
- private final boolean mIsRtl;
-
- private final int mArrayOffset;
- private final View mArrow;
private BubbleTextView mOriginalIcon;
private NotificationItemView mNotificationItemView;
+ private int mNumNotifications;
private ViewGroup mSystemShortcutContainer;
- private boolean mIsLeftAligned;
- protected boolean mIsAboveIcon;
- private int mNumNotifications;
- private int mGravity;
-
- protected Animator mOpenCloseAnimator;
- protected boolean mDeferContainerRemoval;
- private final Rect mStartRect = new Rect();
- private final Rect mEndRect = new Rect();
-
public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mStartDragThreshold = getResources().getDimensionPixelSize(
R.dimen.deep_shortcuts_start_drag_threshold);
- mInflater = LayoutInflater.from(context);
- mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
- mLauncher = Launcher.getLauncher(context);
mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
- mIsRtl = Utilities.isRtl(getResources());
-
- setClipToOutline(true);
- setOutlineProvider(new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
- }
- });
-
- // 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);
- mArrow = new View(context);
- mArrow.setLayoutParams(new DragLayer.LayoutParams(arrowWidth, arrowHeight));
- mArrayOffset = resources.getDimensionPixelSize(R.dimen.popup_arrow_vertical_offset);
}
public PopupContainerWithArrow(Context context, AttributeSet attrs) {
@@ -221,21 +162,6 @@
return false;
}
- @Override
- protected void handleClose(boolean animate) {
- if (animate) {
- animateClose();
- } else {
- closeComplete();
- }
- }
-
- public <T extends View> T inflateAndAdd(int resId) {
- View view = mInflater.inflate(resId, this, false);
- addView(view);
- return (T) view;
- }
-
/**
* Shows the notifications and deep shortcuts associated with {@param icon}.
* @return the container if shown or null.
@@ -266,13 +192,30 @@
return container;
}
+ @Override
+ protected void onInflationComplete(boolean isReversed) {
+ if (isReversed && mNotificationItemView != null) {
+ mNotificationItemView.inverseGutterMargin();
+ }
+
+ // Update dividers
+ int count = getChildCount();
+ DeepShortcutView lastView = null;
+ for (int i = 0; i < count; i++) {
+ View view = getChildAt(i);
+ if (view.getVisibility() == VISIBLE && view instanceof DeepShortcutView) {
+ if (lastView != null) {
+ lastView.setDividerVisibility(VISIBLE);
+ }
+ lastView = (DeepShortcutView) view;
+ lastView.setDividerVisibility(INVISIBLE);
+ }
+ }
+ }
+
private void populateAndShow(final BubbleTextView originalIcon, final List<String> shortcutIds,
final List<NotificationKeyData> notificationKeys, List<SystemShortcut> systemShortcuts) {
mNumNotifications = notificationKeys.size();
-
- setVisibility(View.INVISIBLE);
- mLauncher.getDragLayer().addView(this);
-
mOriginalIcon = originalIcon;
// Add views
@@ -294,17 +237,15 @@
}
for (int i = shortcutIds.size(); i > 0; i--) {
- mShortcuts.add(inflateAndAdd(R.layout.deep_shortcut));
+ mShortcuts.add(inflateAndAdd(R.layout.deep_shortcut, this));
}
updateHiddenShortcuts();
if (!systemShortcuts.isEmpty()) {
- mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons);
+ mSystemShortcutContainer = inflateAndAdd(R.layout.system_shortcut_icons, this);
for (SystemShortcut shortcut : systemShortcuts) {
- View view = mInflater.inflate(R.layout.system_shortcut_icon_only,
- mSystemShortcutContainer, false);
- mSystemShortcutContainer.addView(view);
- initializeSystemShortcut(view, shortcut);
+ initializeSystemShortcut(
+ R.layout.system_shortcut_icon_only, mSystemShortcutContainer, shortcut);
}
}
} else if (!systemShortcuts.isEmpty()) {
@@ -313,68 +254,11 @@
}
for (SystemShortcut shortcut : systemShortcuts) {
- initializeSystemShortcut(inflateAndAdd(R.layout.system_shortcut), shortcut);
+ initializeSystemShortcut(R.layout.system_shortcut, this, shortcut);
}
}
- orientAboutIcon();
- 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));
- }
- Collections.reverse(allViews);
- removeAllViews();
- for (int i = 0; i < count; i++) {
- addView(allViews.get(i));
- }
- if (mNotificationItemView != null) {
- mNotificationItemView.inverseGutterMargin();
- }
-
- orientAboutIcon();
- }
- updateDividers();
-
- // 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;
- mLauncher.getDragLayer().addView(mArrow);
- DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
- if (mIsLeftAligned) {
- mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
- } else {
- mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
- }
-
- 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 show 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(mLauncher, 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);
- mArrow.setElevation(getElevation());
- }
-
- mArrow.setPivotX(arrowLp.width / 2);
- mArrow.setPivotY(mIsAboveIcon ? 0 : arrowLp.height);
-
- animateOpen();
+ reorderAndShow(viewsToFlip);
ItemInfo originalItemInfo = (ItemInfo) originalIcon.getTag();
int numShortcuts = mShortcuts.size() + systemShortcuts.size();
@@ -400,189 +284,15 @@
this, shortcutIds, mShortcuts, notificationKeys));
}
- protected boolean isAlignedWithStart() {
- return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
- }
-
- /**
- * Orients this container above or below the given icon, aligning with the left or right.
- *
- * These are the preferred orientations, in order (RTL prefers right-aligned over left):
- * - Above and left-aligned
- * - Above and right-aligned
- * - Below and left-aligned
- * - Below and right-aligned
- *
- * So we always align left if there is enough horizontal space
- * and align above if there is enough vertical space.
- */
- protected void orientAboutIcon() {
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- int width = getMeasuredWidth();
- int extraVerticalSpace = mArrow.getLayoutParams().height + mArrayOffset
- + getResources().getDimensionPixelSize(R.dimen.popup_vertical_padding);
- int height = getMeasuredHeight() + extraVerticalSpace;
-
- DragLayer dragLayer = mLauncher.getDragLayer();
- dragLayer.getDescendantRectRelativeToSelf(mOriginalIcon, mTempRect);
- Rect insets = dragLayer.getInsets();
-
- // Align left (right in RTL) if there is room.
- int leftAlignedX = mTempRect.left + mOriginalIcon.getPaddingLeft();
- int rightAlignedX = mTempRect.right - width - mOriginalIcon.getPaddingRight();
- int x = leftAlignedX;
- boolean canBeLeftAligned = leftAlignedX + width + insets.left
- < dragLayer.getRight() - insets.right;
- boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
- if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
- x = rightAlignedX;
- }
- mIsLeftAligned = x == leftAlignedX;
-
- // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
- int iconWidth = mOriginalIcon.getWidth()
- - mOriginalIcon.getTotalPaddingLeft() - mOriginalIcon.getTotalPaddingRight();
- iconWidth *= mOriginalIcon.getScaleX();
- 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;
- }
- x += mIsLeftAligned ? xOffset : -xOffset;
-
- // Open above icon if there is room.
- int iconHeight = getIconHeightForPopupPlacement();
- int y = mTempRect.top + mOriginalIcon.getPaddingTop() - height;
- mIsAboveIcon = y > dragLayer.getTop() + insets.top;
- if (!mIsAboveIcon) {
- y = mTempRect.top + mOriginalIcon.getPaddingTop() + iconHeight + extraVerticalSpace;
- }
-
- // Insets are added later, so subtract them now.
- if (mIsRtl) {
- x += insets.right;
- } else {
- x -= insets.left;
- }
- y -= insets.top;
-
- mGravity = 0;
- if (y + height > dragLayer.getBottom() - insets.bottom) {
- // The container is opening off the screen, so just center it in the drag layer instead.
- mGravity = Gravity.CENTER_VERTICAL;
- // Put the container next to the icon, preferring the right side in ltr (left in rtl).
- int rightSide = leftAlignedX + iconWidth - insets.left;
- int leftSide = rightAlignedX - iconWidth - insets.left;
- if (!mIsRtl) {
- if (rightSide + width < dragLayer.getRight()) {
- x = rightSide;
- mIsLeftAligned = true;
- } else {
- x = leftSide;
- mIsLeftAligned = false;
- }
- } else {
- if (leftSide > dragLayer.getLeft()) {
- x = leftSide;
- mIsLeftAligned = false;
- } else {
- x = rightSide;
- mIsLeftAligned = true;
- }
- }
- mIsAboveIcon = true;
- }
-
- setX(x);
- if (Gravity.isVertical(mGravity)) {
- return;
- }
-
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
- if (mIsAboveIcon) {
- arrowLp.gravity = lp.gravity = Gravity.BOTTOM;
- lp.bottomMargin =
- mLauncher.getDragLayer().getHeight() - y - getMeasuredHeight() - insets.top;
- arrowLp.bottomMargin = lp.bottomMargin - arrowLp.height - mArrayOffset - insets.bottom;
- } else {
- arrowLp.gravity = lp.gravity = Gravity.TOP;
- lp.topMargin = y + insets.top;
- arrowLp.topMargin = lp.topMargin - insets.top - arrowLp.height - mArrayOffset;
- }
- }
-
@Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
-
- // enforce contained is within screen
- DragLayer dragLayer = mLauncher.getDragLayer();
- if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) {
- // If we are still off screen, center horizontally too.
- mGravity |= Gravity.CENTER_HORIZONTAL;
- }
-
- if (Gravity.isHorizontal(mGravity)) {
- setX(dragLayer.getWidth() / 2 - getMeasuredWidth() / 2);
- mArrow.setVisibility(INVISIBLE);
- }
- if (Gravity.isVertical(mGravity)) {
- setY(dragLayer.getHeight() / 2 - getMeasuredHeight() / 2);
- }
- }
-
- protected void animateOpen() {
- setVisibility(View.VISIBLE);
- mIsOpen = true;
-
- final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
- final Resources res = getResources();
- final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
- final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
-
- // Rectangular reveal.
- final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
- .createRevealAnimator(this, false);
- revealAnim.setDuration(revealDuration);
- revealAnim.setInterpolator(revealInterpolator);
-
- Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
- fadeIn.setDuration(revealDuration);
- fadeIn.setInterpolator(revealInterpolator);
- openAnim.play(fadeIn);
-
- // Animate the arrow.
- mArrow.setScaleX(0);
- mArrow.setScaleY(0);
- Animator arrowScale = ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 1)
- .setDuration(res.getInteger(R.integer.config_popupArrowOpenDuration));
-
- openAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- sendCustomAccessibilityEvent(
- PopupContainerWithArrow.this,
- AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
- getContext().getString(R.string.action_deep_shortcut));
- }
- });
-
- mOpenCloseAnimator = openAnim;
- openAnim.playSequentially(revealAnim, arrowScale);
- openAnim.start();
+ protected void getTargetObjectLocation(Rect outPos) {
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mOriginalIcon, outPos);
+ outPos.top += mOriginalIcon.getPaddingTop();
+ outPos.left += mOriginalIcon.getPaddingLeft();
+ outPos.right -= mOriginalIcon.getPaddingRight();
+ outPos.bottom = outPos.top + (mOriginalIcon.getIcon() != null
+ ? mOriginalIcon.getIcon().getBounds().height()
+ : mOriginalIcon.getHeight());
}
public void applyNotificationInfos(List<NotificationInfo> notificationInfos) {
@@ -641,10 +351,8 @@
if (onClickListener != null && widgetsView == null) {
// We didn't have any widgets cached but now there are some, so enable the shortcut.
if (mSystemShortcutContainer != this) {
- View view = mInflater.inflate(R.layout.system_shortcut_icon_only,
- mSystemShortcutContainer, false);
- mSystemShortcutContainer.addView(view);
- initializeSystemShortcut(view, widgetInfo);
+ initializeSystemShortcut(
+ R.layout.system_shortcut_icon_only, mSystemShortcutContainer, widgetInfo);
} else {
// If using the expanded system shortcut (as opposed to just the icon), we need to
// reopen the container to ensure measurements etc. all work out. While this could
@@ -664,7 +372,8 @@
}
}
- private void initializeSystemShortcut(View view, SystemShortcut info) {
+ private void initializeSystemShortcut(int resId, ViewGroup container, SystemShortcut info) {
+ View view = inflateAndAdd(resId, container);
if (view instanceof DeepShortcutView) {
// Expanded system shortcut, with both icon and text shown on white background.
final DeepShortcutView shortcutView = (DeepShortcutView) view;
@@ -681,12 +390,6 @@
(ItemInfo) mOriginalIcon.getTag()));
}
- protected int getIconHeightForPopupPlacement() {
- return mOriginalIcon.getIcon() != null
- ? mOriginalIcon.getIcon().getBounds().height()
- : mOriginalIcon.getHeight();
- }
-
/**
* Determines when the deferred drag should be started.
*
@@ -744,7 +447,7 @@
private void updateNotificationHeader() {
ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
- BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
+ BadgeInfo badgeInfo = mLauncher.getBadgeInfoForItem(itemInfo);
if (mNotificationItemView != null && badgeInfo != null) {
mNotificationItemView.updateHeader(
badgeInfo.getNotificationCount(), itemInfo.iconColor);
@@ -806,91 +509,11 @@
targetParent.containerType = ContainerType.DEEPSHORTCUTS;
}
- protected void animateClose() {
- if (!mIsOpen) {
- return;
- }
- mEndRect.setEmpty();
- if (getOutlineProvider() instanceof RevealOutlineAnimation) {
- ((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
- }
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel();
- }
- mIsOpen = false;
-
- final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
- // Hide the arrow
- closeAnim.play(ObjectAnimator.ofFloat(mArrow, LauncherAnimUtils.SCALE_PROPERTY, 0));
- closeAnim.play(ObjectAnimator.ofFloat(mArrow, ALPHA, 0));
-
+ @Override
+ protected void onCreateCloseAnimation(AnimatorSet anim) {
// Animate original icon's text back in.
- closeAnim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
+ anim.play(mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */));
mOriginalIcon.forceHideBadge(false);
-
- final Resources res = getResources();
- final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
-
- // Rectangular reveal (reversed).
- final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
- .createRevealAnimator(this, true);
- revealAnim.setInterpolator(revealInterpolator);
- closeAnim.play(revealAnim);
-
- Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
- fadeOut.setInterpolator(revealInterpolator);
- closeAnim.play(fadeOut);
- closeAnim.setDuration((long) res.getInteger(R.integer.config_popupOpenCloseDuration));
-
- closeAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenCloseAnimator = null;
- if (mDeferContainerRemoval) {
- setVisibility(INVISIBLE);
- } else {
- closeComplete();
- }
- }
- });
- mOpenCloseAnimator = closeAnim;
- closeAnim.start();
- }
-
- private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
- int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
- R.dimen.popup_arrow_horizontal_center_start:
- R.dimen.popup_arrow_horizontal_center_end);
- if (!mIsLeftAligned) {
- arrowCenterX = getMeasuredWidth() - arrowCenterX;
- }
- int arrowCenterY = mIsAboveIcon ? getMeasuredHeight() : 0;
-
- mStartRect.set(arrowCenterX, arrowCenterY, arrowCenterX, arrowCenterY);
- if (mEndRect.isEmpty()) {
- mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
- }
-
- return new RoundedRectRevealOutlineProvider
- (mOutlineRadius, mOutlineRadius, mStartRect, mEndRect);
- }
-
- /**
- * Closes the popup without animation.
- */
- private void closeComplete() {
- mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
- mOriginalIcon.forceHideBadge(false);
-
- mLauncher.getDragController().removeDragListener(this);
- if (mOpenCloseAnimator != null) {
- mOpenCloseAnimator.cancel();
- mOpenCloseAnimator = null;
- }
- mIsOpen = false;
- mDeferContainerRemoval = false;
- mLauncher.getDragLayer().removeView(this);
- mLauncher.getDragLayer().removeView(mArrow);
}
@Override
@@ -907,12 +530,9 @@
@Override
public boolean onLongClick(View v) {
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
// Return early if not the correct view
if (!(v.getParent() instanceof DeepShortcutView)) return false;
- // Return early if global dragging is not enabled
- if (!mLauncher.isDraggingEnabled()) return false;
- // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
- if (mLauncher.getDragController().isDragging()) return false;
// Long clicked on a shortcut.
DeepShortcutView sv = (DeepShortcutView) v.getParent();
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 335426c..f1b8ec0 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -206,7 +206,7 @@
if (notificationListener == null) {
return;
}
- notificationListener.cancelNotification(notificationKey);
+ notificationListener.cancelNotificationFromLauncher(notificationKey);
}
public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index d2bcd18..a20149e 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -9,7 +9,7 @@
import android.view.View;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.InfoDropTarget;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
@@ -28,7 +28,7 @@
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
*/
-public abstract class SystemShortcut extends ItemInfo {
+public abstract class SystemShortcut<T extends BaseDraggingActivity> extends ItemInfo {
public final int iconResId;
public final int labelResId;
@@ -37,10 +37,9 @@
this.labelResId = labelResId;
}
- public abstract View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo);
+ public abstract View.OnClickListener getOnClickListener(T activity, ItemInfo itemInfo);
- public static class Widgets extends SystemShortcut {
+ public static class Widgets extends SystemShortcut<Launcher> {
public Widgets() {
super(R.drawable.ic_widget, R.string.widget_button_text);
@@ -55,17 +54,14 @@
if (widgets == null) {
return null;
}
- return new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- AbstractFloatingView.closeAllOpenViews(launcher);
- WidgetsBottomSheet widgetsBottomSheet =
- (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
- widgetsBottomSheet.populateAndShow(itemInfo);
- launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.WIDGETS_BUTTON, view);
- }
+ return (view) -> {
+ AbstractFloatingView.closeAllOpenViews(launcher);
+ WidgetsBottomSheet widgetsBottomSheet =
+ (WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
+ R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
+ widgetsBottomSheet.populateAndShow(itemInfo);
+ launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.WIDGETS_BUTTON, view);
};
}
}
@@ -76,17 +72,15 @@
}
@Override
- public View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo) {
- return new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Rect sourceBounds = launcher.getViewBounds(view);
- Bundle opts = launcher.getActivityLaunchOptions(view, false);
- InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts);
- launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.APPINFO_TARGET, view);
- }
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
+ return (view) -> {
+ Rect sourceBounds = activity.getViewBounds(view);
+ Bundle opts = activity.getActivityLaunchOptionsAsBundle(view, false);
+ new PackageManagerHelper(activity).startDetailsActivityForInfo(
+ itemInfo, sourceBounds, opts);
+ activity.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.APPINFO_TARGET, view);
};
}
}
@@ -97,28 +91,29 @@
}
@Override
- public View.OnClickListener getOnClickListener(final Launcher launcher,
- final ItemInfo itemInfo) {
+ public View.OnClickListener getOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
boolean supportsWebUI = (itemInfo instanceof ShortcutInfo) &&
((ShortcutInfo) itemInfo).hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI);
boolean isInstantApp = false;
if (itemInfo instanceof com.android.launcher3.AppInfo) {
com.android.launcher3.AppInfo appInfo = (com.android.launcher3.AppInfo) itemInfo;
- isInstantApp = InstantAppResolver.newInstance(launcher).isInstantApp(appInfo);
+ isInstantApp = InstantAppResolver.newInstance(activity).isInstantApp(appInfo);
}
boolean enabled = supportsWebUI || isInstantApp;
if (!enabled) {
return null;
}
- return createOnClickListener(launcher, itemInfo);
+ return createOnClickListener(activity, itemInfo);
}
- public View.OnClickListener createOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ public View.OnClickListener createOnClickListener(
+ BaseDraggingActivity activity, ItemInfo itemInfo) {
return view -> {
Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
itemInfo.getTargetComponent().getPackageName());
- launcher.startActivitySafely(view, intent, itemInfo);
- AbstractFloatingView.closeAllOpenViews(launcher);
+ activity.startActivitySafely(view, intent, itemInfo);
+ AbstractFloatingView.closeAllOpenViews(activity);
};
}
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
index 1a5297d..c809f27 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -22,6 +22,7 @@
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
+import android.widget.Toast;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
@@ -33,7 +34,9 @@
public class DeepShortcutTextView extends BubbleTextView {
private final Rect mDragHandleBounds = new Rect();
private final int mDragHandleWidth;
- private boolean mShouldPerformClick = true;
+ private boolean mShowInstructionToast = false;
+
+ private Toast mInstructionToast;
public DeepShortcutTextView(Context context) {
this(context, null, 0);
@@ -70,14 +73,29 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- // Ignore clicks on the drag handle (long clicks still start the drag).
- mShouldPerformClick = !mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY());
+ // Show toast if user touches the drag handle (long clicks still start the drag).
+ mShowInstructionToast = mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY());
}
return super.onTouchEvent(ev);
}
@Override
public boolean performClick() {
- return mShouldPerformClick && super.performClick();
+ if (mShowInstructionToast) {
+ showToast();
+ return true;
+ }
+ return super.performClick();
+ }
+
+ private void showToast() {
+ if (mInstructionToast != null) {
+ mInstructionToast.cancel();
+ }
+ CharSequence msg = Utilities.wrapForTts(
+ getContext().getText(R.string.long_press_shortcut_to_add),
+ getContext().getString(R.string.long_accessible_way_to_add_shortcut));
+ mInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
+ mInstructionToast.show();
}
}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
index 450a690..9ad266b 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutView.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -30,6 +30,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.popup.PopupContainerWithArrow;
+import com.android.launcher3.touch.ItemClickHandler;
/**
* A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
@@ -120,7 +121,7 @@
mBubbleText.setText(usingLongLabel ? longLabel : mDetail.getShortLabel());
// TODO: Add the click handler to this view directly and not the child view.
- mBubbleText.setOnClickListener(Launcher.getLauncher(getContext()));
+ mBubbleText.setOnClickListener(ItemClickHandler.INSTANCE);
mBubbleText.setOnLongClickListener(container);
mBubbleText.setOnTouchListener(container);
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index cfb9258..ee97641 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -50,12 +50,10 @@
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(b);
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.translate(blurSizeOutline / 2, blurSizeOutline / 2);
canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
canvas.translate(bounds.left, bounds.top);
d.draw(canvas);
- canvas.restore();
return b;
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index 704d82f..cbef85a 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -1,7 +1,6 @@
package com.android.launcher3.shortcuts;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
@@ -18,8 +17,8 @@
super(new ComponentName(packageName, id), user);
}
- public ShortcutKey(Context context, String componentKeyStr) {
- super(context, componentKeyStr);
+ public ShortcutKey(ComponentName componentName, UserHandle user) {
+ super(componentName, user);
}
public String getId() {
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index 7298383..0a2c3e4 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -24,7 +24,6 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.util.Preconditions;
import java.lang.ref.WeakReference;
@@ -38,7 +37,7 @@
public static final String EXTRA_STATE_HANDLER = "launcher.state_handler";
- private static WeakReference<InternalStateHandler> sPendingHandler = new WeakReference<>(null);
+ private static final Scheduler sScheduler = new Scheduler();
/**
* Initializes the handler when the launcher is ready.
@@ -53,30 +52,12 @@
return intent;
}
- public final void initWhenReady(MainThreadExecutor executor) {
- sPendingHandler = new WeakReference<>(this);
- executor.execute(this::initIfReadOnUIThread);
+ public final void initWhenReady() {
+ sScheduler.schedule(this);
}
- private void initIfReadOnUIThread() {
- LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- return;
- }
- Callbacks cb = app.getModel().getCallback();
- if (!(cb instanceof Launcher)) {
- return;
- }
- Launcher launcher = (Launcher) cb;
- if (!init(launcher, launcher.isStarted())) {
- sPendingHandler.clear();
- }
- }
-
- public void clearReference() {
- if (sPendingHandler.get() == this) {
- sPendingHandler.clear();
- }
+ public boolean clearReference() {
+ return sScheduler.clearReference(this);
}
public static boolean handleCreate(Launcher launcher, Intent intent) {
@@ -101,14 +82,55 @@
}
}
if (!result && !explicitIntent) {
- InternalStateHandler pendingHandler = sPendingHandler.get();
- if (pendingHandler != null) {
- if (!pendingHandler.init(launcher, alreadyOnHome)) {
- sPendingHandler.clear();
- }
- result = true;
- }
+ result = sScheduler.initIfPending(launcher, alreadyOnHome);
}
return result;
}
+
+ private static class Scheduler implements Runnable {
+
+ private WeakReference<InternalStateHandler> mPendingHandler = new WeakReference<>(null);
+ private MainThreadExecutor mMainThreadExecutor;
+
+ public synchronized void schedule(InternalStateHandler handler) {
+ mPendingHandler = new WeakReference<>(handler);
+ if (mMainThreadExecutor == null) {
+ mMainThreadExecutor = new MainThreadExecutor();
+ }
+ mMainThreadExecutor.execute(this);
+ }
+
+ @Override
+ public void run() {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ return;
+ }
+ Callbacks cb = app.getModel().getCallback();
+ if (!(cb instanceof Launcher)) {
+ return;
+ }
+ Launcher launcher = (Launcher) cb;
+ initIfPending(launcher, launcher.isStarted());
+ }
+
+ public synchronized boolean initIfPending(Launcher launcher, boolean alreadyOnHome) {
+ InternalStateHandler pendingHandler = mPendingHandler.get();
+ if (pendingHandler != null) {
+ if (!pendingHandler.init(launcher, alreadyOnHome)) {
+ mPendingHandler.clear();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public synchronized boolean clearReference(InternalStateHandler handler) {
+ if (mPendingHandler.get() == handler) {
+ mPendingHandler.clear();
+ return true;
+ }
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
new file mode 100644
index 0000000..8f83648
--- /dev/null
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -0,0 +1,139 @@
+/*
+ * 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.states;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.provider.Settings.System.ACCELEROMETER_ROTATION;
+import static android.provider.Settings.System.getUriFor;
+
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
+
+import com.android.launcher3.R;
+
+/**
+ * Utility class to manage launcher rotation
+ */
+public class RotationHelper extends ContentObserver {
+
+ public static final int REQUEST_NONE = 0;
+ public static final int REQUEST_ROTATE = 1;
+ public static final int REQUEST_LOCK = 2;
+
+ private final Activity mActivity;
+ private final ContentResolver mCr;
+
+ private final boolean mIgnoreAutoRotateSettings;
+ private boolean mAutoRotateEnabled;
+
+ /**
+ * Rotation request made by {@link InternalStateHandler}. This supersedes any other request.
+ */
+ private int mStateHandlerRequest = REQUEST_NONE;
+ /**
+ * Rotation request made by a Launcher State
+ */
+ private int mCurrentStateRequest = REQUEST_NONE;
+
+ // This is used to defer setting rotation flags until the activity is being created
+ private boolean mInitialized;
+ public boolean mDestroyed;
+
+ private int mLastActivityFlags = -1;
+
+ public RotationHelper(Activity activity) {
+ super(new Handler());
+ mActivity = activity;
+
+ // On large devices we do not handle auto-rotate differently.
+ mIgnoreAutoRotateSettings = mActivity.getResources().getBoolean(R.bool.allow_rotation);
+ if (!mIgnoreAutoRotateSettings) {
+ mCr = mActivity.getContentResolver();
+ mCr.registerContentObserver(getUriFor(ACCELEROMETER_ROTATION), false, this);
+ mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+ } else {
+ mCr = null;
+ }
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ mAutoRotateEnabled = Settings.System.getInt(mCr, ACCELEROMETER_ROTATION, 1) == 1;
+ notifyChange();
+ }
+
+ public void setStateHandlerRequest(int request) {
+ if (mStateHandlerRequest != request) {
+ mStateHandlerRequest = request;
+ notifyChange();
+ }
+ }
+
+ public void setCurrentStateRequest(int request) {
+ if (mCurrentStateRequest != request) {
+ mCurrentStateRequest = request;
+ notifyChange();
+ }
+ }
+
+ public void initialize() {
+ if (!mInitialized) {
+ mInitialized = true;
+ notifyChange();
+ }
+ }
+
+ public void destroy() {
+ if (!mDestroyed) {
+ mDestroyed = true;
+ if (mCr != null) {
+ mCr.unregisterContentObserver(this);
+ }
+ }
+ }
+
+ private void notifyChange() {
+ if (!mInitialized || mDestroyed) {
+ return;
+ }
+
+ final int activityFlags;
+ if (mStateHandlerRequest != REQUEST_NONE) {
+ activityFlags = mStateHandlerRequest == REQUEST_LOCK ?
+ SCREEN_ORIENTATION_LOCKED : SCREEN_ORIENTATION_UNSPECIFIED;
+ } else if (mCurrentStateRequest == REQUEST_LOCK) {
+ activityFlags = SCREEN_ORIENTATION_LOCKED;
+ } else if (mIgnoreAutoRotateSettings || mCurrentStateRequest == REQUEST_ROTATE) {
+ activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
+ } else if (mAutoRotateEnabled) {
+ // If auto rotation is on, lock to device orientation
+ activityFlags = SCREEN_ORIENTATION_NOSENSOR;
+ } else {
+ // If auto rotation is off, allow rotation on the activity, in case the user is using
+ // forced rotation.
+ activityFlags = SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+ if (activityFlags != mLastActivityFlags) {
+ mLastActivityFlags = activityFlags;
+ mActivity.setRequestedOrientation(mLastActivityFlags);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 6d584cd..90d3821 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -16,10 +16,9 @@
package com.android.launcher3.states;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
-import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import android.os.Handler;
import android.view.View;
import com.android.launcher3.DeviceProfile;
@@ -36,11 +35,7 @@
private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE |
FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED |
- FLAG_DISABLE_PAGE_CLIPPING;
-
- // Determines how long to wait after a rotation before restoring the screen orientation to
- // match the sensor state.
- private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
+ FLAG_DISABLE_PAGE_CLIPPING | FLAG_PAGE_BACKGROUNDS | FLAG_HIDE_BACK_BUTTON;
public SpringLoadedState(int id) {
super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS);
@@ -84,30 +79,16 @@
ws.showPageIndicatorAtCurrentScroll();
ws.getPageIndicator().setShouldAutoHide(false);
- // Lock the orientation:
- if (launcher.isRotationEnabled()) {
- launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
- }
-
// Prevent any Un/InstallShortcutReceivers from updating the db while we are
// in spring loaded mode
InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
+ launcher.getRotationHelper().setCurrentStateRequest(REQUEST_LOCK);
}
@Override
public void onStateDisabled(final Launcher launcher) {
launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
- // Unlock rotation lock
- if (launcher.isRotationEnabled()) {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- }
- }, RESTORE_SCREEN_ORIENTATION_DELAY);
- }
-
// Re-enable any Un/InstallShortcutReceiver and now process any queued items
InstallShortcutReceiver.disableAndFlushInstallQueue(
InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
new file mode 100644
index 0000000..9726704
--- /dev/null
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -0,0 +1,283 @@
+/*
+ * 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.touch;
+
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.MotionEvent;
+
+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.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.PendingAnimation;
+
+/**
+ * TouchController for handling state changes
+ */
+public abstract class AbstractStateChangeTouchController extends AnimatorListenerAdapter
+ implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "ASCTouchController";
+ public static final float RECATCH_REJECTION_FRACTION = .0875f;
+ public static final int SINGLE_FRAME_MS = 16;
+
+ // 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 Launcher mLauncher;
+ protected final SwipeDetector mDetector;
+
+ private boolean mNoIntercept;
+ protected int mStartContainerType;
+
+ protected LauncherState mFromState;
+ protected LauncherState mToState;
+ protected AnimatorPlaybackController mCurrentAnimation;
+ protected PendingAnimation mPendingAnimation;
+
+ private float mStartProgress;
+ // Ratio of transition process [0, 1] to drag displacement (px)
+ private float mProgressMultiplier;
+ private float mDisplacementShift;
+
+ public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
+ mLauncher = l;
+ mDetector = new SwipeDetector(l, this, dir);
+ }
+
+ protected abstract boolean canInterceptTouch(MotionEvent ev);
+
+ /**
+ * Initializes the {@code mFromState} and {@code mToState} and swipe direction to use for
+ * the detector. In case of disabling swipe, return 0.
+ */
+ protected abstract int getSwipeDirection(MotionEvent ev);
+
+ @Override
+ public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch(ev);
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ final int directionsToDetectScroll;
+ boolean ignoreSlopWhenSettling = false;
+
+ if (mCurrentAnimation != null) {
+ if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
+ } else {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ ignoreSlopWhenSettling = true;
+ }
+ } else {
+ directionsToDetectScroll = getSwipeDirection(ev);
+ if (directionsToDetectScroll == 0) {
+ mNoIntercept = true;
+ return false;
+ }
+ }
+ mDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, ignoreSlopWhenSettling);
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public final boolean onControllerTouchEvent(MotionEvent ev) {
+ return mDetector.onTouchEvent(ev);
+ }
+
+ protected float getShiftRange() {
+ return mLauncher.getAllAppsController().getShiftRange();
+ }
+
+ protected abstract LauncherState getTargetState(LauncherState fromState,
+ boolean isDragTowardPositive);
+
+ protected abstract float initCurrentAnimation();
+
+ private boolean reinitCurrentAnimation(boolean reachedToState, boolean isDragTowardPositive) {
+ LauncherState newFromState = mFromState == null ? mLauncher.getStateManager().getState()
+ : reachedToState ? mToState : mFromState;
+ LauncherState newToState = getTargetState(newFromState, isDragTowardPositive);
+
+ if (newFromState == mFromState && newToState == mToState || (newFromState == newToState)) {
+ return false;
+ }
+
+ mFromState = newFromState;
+ mToState = newToState;
+
+ mStartProgress = 0;
+ mProgressMultiplier = initCurrentAnimation();
+ mCurrentAnimation.getTarget().addListener(this);
+ mCurrentAnimation.dispatchOnStart();
+ return true;
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ if (mCurrentAnimation == null) {
+ mFromState = mToState = null;
+ reinitCurrentAnimation(false, mDetector.wasInitialTouchPositive());
+ mDisplacementShift = 0;
+ } else {
+ mCurrentAnimation.pause();
+ mStartProgress = mCurrentAnimation.getProgressFraction();
+ }
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
+ float progress = deltaProgress + mStartProgress;
+ updateProgress(progress);
+ boolean isDragTowardPositive = (displacement - mDisplacementShift) < 0;
+ if (progress <= 0) {
+ if (reinitCurrentAnimation(false, isDragTowardPositive)) {
+ mDisplacementShift = displacement;
+ }
+ } else if (progress >= 1) {
+ if (reinitCurrentAnimation(true, isDragTowardPositive)) {
+ mDisplacementShift = displacement;
+ }
+ }
+ return true;
+ }
+
+ protected void updateProgress(float fraction) {
+ mCurrentAnimation.setPlayFraction(fraction);
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ final int logAction;
+ final LauncherState targetState;
+ final float progress = mCurrentAnimation.getProgressFraction();
+
+ if (fling) {
+ logAction = Touch.FLING;
+ targetState =
+ Float.compare(Math.signum(velocity), Math.signum(mProgressMultiplier)) == 0
+ ? mToState : mFromState;
+ // snap to top or bottom using the release velocity
+ } else {
+ logAction = Touch.SWIPE;
+ targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
+ }
+
+
+ final float endProgress;
+ final float startProgress;
+ final long duration;
+
+ if (targetState == mToState) {
+ endProgress = 1;
+ if (progress >= 1) {
+ duration = 0;
+ startProgress = 1;
+ } else {
+ startProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
+ duration = SwipeDetector.calculateDuration(velocity,
+ endProgress - Math.max(progress, 0));
+ }
+ } else {
+ endProgress = 0;
+ if (progress <= 0) {
+ duration = 0;
+ startProgress = 0;
+ } else {
+ startProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
+ duration = SwipeDetector.calculateDuration(velocity,
+ Math.min(progress, 1) - endProgress);
+ }
+ }
+
+ mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ anim.setFloatValues(startProgress, endProgress);
+ updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
+ anim.start();
+ }
+
+ protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
+ LauncherState targetState, float velocity, boolean isFling) {
+ animator.setDuration(expectedDuration)
+ .setInterpolator(scrollInterpolatorForVelocity(velocity));
+ }
+
+ protected int getDirectionForLog() {
+ return mToState.ordinal > mFromState.ordinal ? Direction.UP : Direction.DOWN;
+ }
+
+ protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+ if (targetState != mFromState) {
+ // Transition complete. log the action
+ mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
+ getDirectionForLog(),
+ mStartContainerType,
+ mFromState.containerType,
+ mToState.containerType,
+ mLauncher.getWorkspace().getCurrentPage());
+ }
+ clearState();
+ boolean shouldGoToTargetState = true;
+ if (mPendingAnimation != null) {
+ boolean reachedTarget = mToState == targetState;
+ mPendingAnimation.finish(reachedTarget);
+ mPendingAnimation = null;
+ shouldGoToTargetState = !reachedTarget;
+ }
+ if (shouldGoToTargetState) {
+ mLauncher.getStateManager().goToState(targetState, false /* animated */);
+ }
+ }
+
+ protected void clearState() {
+ mCurrentAnimation = null;
+ mDetector.finishedScrolling();
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) {
+ Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
+ clearState();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
new file mode 100644
index 0000000..f2f5592
--- /dev/null
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -0,0 +1,232 @@
+/*
+ * 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.touch;
+
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SAFEMODE;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
+import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
+import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.os.Process;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
+import com.android.launcher3.widget.WidgetAddFlowHandler;
+
+/**
+ * Class for handling clicks on workspace and all-apps items
+ */
+public class ItemClickHandler {
+
+ /**
+ * Instance used for click handling on items
+ */
+ public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
+
+ 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;
+ }
+
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!launcher.getWorkspace().isFinishedSwitchingState()) {
+ return;
+ }
+
+ Object tag = v.getTag();
+ if (tag instanceof ShortcutInfo) {
+ onClickAppShortcut(v, (ShortcutInfo) tag, launcher);
+ } else if (tag instanceof FolderInfo) {
+ if (v instanceof FolderIcon) {
+ onClickFolderIcon(v);
+ }
+ } else if (tag instanceof AppInfo) {
+ startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
+ } else if (tag instanceof LauncherAppWidgetInfo) {
+ if (v instanceof PendingAppWidgetHostView) {
+ onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
+ }
+ }
+ }
+
+ /**
+ * Event handler for a folder icon click.
+ *
+ * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
+ */
+ private static void onClickFolderIcon(View v) {
+ Folder folder = ((FolderIcon) v).getFolder();
+ if (!folder.isOpen() && !folder.isDestroyed()) {
+ // Open the requested folder
+ folder.animateOpen();
+ }
+ }
+
+ /**
+ * Event handler for the app widget view which has not fully restored.
+ */
+ private static void onClickPendingWidget(PendingAppWidgetHostView v, Launcher launcher) {
+ if (launcher.getPackageManager().isSafeMode()) {
+ Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
+ if (v.isReadyForClickSetup()) {
+ LauncherAppWidgetProviderInfo appWidgetInfo = AppWidgetManagerCompat
+ .getInstance(launcher).findProvider(info.providerName, info.user);
+ if (appWidgetInfo == null) {
+ return;
+ }
+ WidgetAddFlowHandler addFlowHandler = new WidgetAddFlowHandler(appWidgetInfo);
+
+ if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+ // This should not happen, as we make sure that an Id is allocated during bind.
+ return;
+ }
+ addFlowHandler.startBindFlow(launcher, info.appWidgetId, info,
+ REQUEST_BIND_PENDING_APPWIDGET);
+ } else {
+ addFlowHandler.startConfigActivity(launcher, info, REQUEST_RECONFIGURE_APPWIDGET);
+ }
+ } else {
+ final String packageName = info.providerName.getPackageName();
+ onClickPendingAppItem(v, launcher, packageName, info.installProgress >= 0);
+ }
+ }
+
+ private static void onClickPendingAppItem(View v, Launcher launcher, String packageName,
+ boolean downloadStarted) {
+ if (downloadStarted) {
+ // If the download has started, simply direct to the market app.
+ startMarketIntentForPackage(v, launcher, packageName);
+ return;
+ }
+ new AlertDialog.Builder(launcher)
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search,
+ (d, i) -> startMarketIntentForPackage(v, launcher, packageName))
+ .setNeutralButton(R.string.abandoned_clean_this,
+ (d, i) -> launcher.getWorkspace()
+ .removeAbandonedPromise(packageName, Process.myUserHandle()))
+ .create().show();
+ }
+
+ private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
+ ItemInfo item = (ItemInfo) v.getTag();
+ Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
+ launcher.startActivitySafely(v, intent, item);
+ }
+
+ /**
+ * Event handler for an app shortcut click.
+ *
+ * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+ */
+ private static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
+ if (shortcut.isDisabled()) {
+ final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
+ if ((disabledFlags &
+ ~FLAG_DISABLED_SUSPENDED &
+ ~FLAG_DISABLED_QUIET_USER) == 0) {
+ // If the app is only disabled because of the above flags, launch activity anyway.
+ // Framework will tell the user why the app is suspended.
+ } else {
+ if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
+ // Use a message specific to this shortcut, if it has one.
+ Toast.makeText(launcher, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ // Otherwise just use a generic error message.
+ int error = R.string.activity_not_available;
+ if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_SAFEMODE) != 0) {
+ error = R.string.safemode_shortcut_error;
+ } else if ((shortcut.runtimeStatusFlags & FLAG_DISABLED_BY_PUBLISHER) != 0 ||
+ (shortcut.runtimeStatusFlags & FLAG_DISABLED_LOCKED_USER) != 0) {
+ error = R.string.shortcut_not_available;
+ }
+ Toast.makeText(launcher, error, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ // Check for abandoned promise
+ if ((v instanceof BubbleTextView) && shortcut.hasPromiseIconUi()) {
+ String packageName = shortcut.intent.getComponent() != null ?
+ shortcut.intent.getComponent().getPackageName() : shortcut.intent.getPackage();
+ if (!TextUtils.isEmpty(packageName)) {
+ onClickPendingAppItem(v, launcher, packageName,
+ shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE));
+ return;
+ }
+ }
+
+ // Start activities
+ startAppShortcutOrInfoActivity(v, shortcut, launcher);
+ }
+
+ private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
+ Intent intent;
+ if (item instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+ intent = promiseAppInfo.getMarketIntent(launcher);
+ } else {
+ intent = item.getIntent();
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("Input must have a valid intent");
+ }
+ if (item instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) item;
+ if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
+ && intent.getAction() == Intent.ACTION_VIEW) {
+ // make a copy of the intent that has the package set to null
+ // we do this because the platform sometimes disables instant
+ // apps temporarily (triggered by the user) and fallbacks to the
+ // web ui. This only works though if the package isn't set
+ intent = new Intent(intent);
+ intent.setPackage(null);
+ }
+ }
+ launcher.startActivitySafely(v, intent, item);
+ }
+}
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
new file mode 100644
index 0000000..6f012f6
--- /dev/null
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -0,0 +1,119 @@
+/*
+ * 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.touch;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+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.View;
+import android.view.View.OnLongClickListener;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+
+/**
+ * Class to handle long-clicks on workspace items and start drag as a result.
+ */
+public class ItemLongClickListener {
+
+ public static OnLongClickListener INSTANCE_WORKSPACE =
+ ItemLongClickListener::onWorkspaceItemLongClick;
+
+ public static OnLongClickListener INSTANCE_ALL_APPS =
+ ItemLongClickListener::onAllAppsItemLongClick;
+
+ private static boolean onWorkspaceItemLongClick(View v) {
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!canStartDrag(launcher)) return false;
+ if (!launcher.isInState(NORMAL) && !launcher.isInState(OVERVIEW)) return false;
+ if (!(v.getTag() instanceof ItemInfo)) return false;
+
+ launcher.setWaitingForResult(null);
+ beginDrag(v, launcher, (ItemInfo) v.getTag(), new DragOptions());
+ return true;
+ }
+
+ public static void beginDrag(View v, Launcher launcher, ItemInfo info,
+ DragOptions dragOptions) {
+ if (info.container >= 0) {
+ Folder folder = Folder.getOpen(launcher);
+ if (folder != null) {
+ if (!folder.getItemsInReadingOrder().contains(v)) {
+ folder.close(true);
+ } else {
+ folder.startDrag(v, dragOptions);
+ return;
+ }
+ }
+ }
+
+ CellLayout.CellInfo longClickCellInfo = new CellLayout.CellInfo(v, info);
+ launcher.getWorkspace().startDrag(longClickCellInfo, dragOptions);
+ }
+
+ private static boolean onAllAppsItemLongClick(View v) {
+ Launcher launcher = Launcher.getLauncher(v.getContext());
+ if (!canStartDrag(launcher)) return false;
+ // When we have exited all apps or are in transition, disregard long clicks
+ if (!launcher.isInState(ALL_APPS) && !launcher.isInState(OVERVIEW)) return false;
+ if (launcher.getWorkspace().isSwitchingState()) return false;
+
+ // Start the drag
+ final DragController dragController = launcher.getDragController();
+ dragController.addDragListener(new DragController.DragListener() {
+ @Override
+ public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+ v.setVisibility(INVISIBLE);
+ }
+
+ @Override
+ public void onDragEnd() {
+ v.setVisibility(VISIBLE);
+ dragController.removeDragListener(this);
+ }
+ });
+
+ DeviceProfile grid = launcher.getDeviceProfile();
+ DragOptions options = new DragOptions();
+ options.intrinsicIconScaleFactor = (float) grid.allAppsIconSizePx / grid.iconSizePx;
+ launcher.getWorkspace().beginDragShared(v, launcher.getAppsView(), options);
+ return false;
+ }
+
+ public static boolean canStartDrag(Launcher launcher) {
+ if (launcher == null) {
+ return false;
+ }
+ // We prevent dragging when we are loading the workspace as it is possible to pick up a view
+ // that is subsequently removed from the workspace in startBinding().
+ if (launcher.isWorkspaceLocked()) return false;
+ // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
+ if (launcher.getDragController().isDragging()) return false;
+
+ return true;
+ }
+}
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
new file mode 100644
index 0000000..23f55aa
--- /dev/null
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -0,0 +1,150 @@
+/*
+ * 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.touch;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.ViewConfiguration.getLongPressTimeout;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.view.HapticFeedbackConstants;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.views.OptionsPopupView;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Helper class to handle touch on empty space in workspace and show options popup on long press
+ */
+public class WorkspaceTouchListener implements OnTouchListener, Runnable {
+
+ /**
+ * STATE_PENDING_PARENT_INFORM is the state between longPress performed & the next motionEvent.
+ * This next event is used to send an ACTION_CANCEL to Workspace, to that it clears any
+ * temporary scroll state. After that, the state is set to COMPLETED, and we just eat up all
+ * subsequent motion events.
+ */
+ private static final int STATE_CANCELLED = 0;
+ private static final int STATE_REQUESTED = 1;
+ private static final int STATE_PENDING_PARENT_INFORM = 2;
+ private static final int STATE_COMPLETED = 3;
+
+ private final Rect mTempRect = new Rect();
+ private final Launcher mLauncher;
+ private final Workspace mWorkspace;
+ private final PointF mTouchDownPoint = new PointF();
+
+ private int mLongPressState = STATE_CANCELLED;
+
+ public WorkspaceTouchListener(Launcher launcher, Workspace workspace) {
+ mLauncher = launcher;
+ mWorkspace = workspace;
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == ACTION_DOWN) {
+ // Check if we can handle long press.
+ boolean handleLongPress = AbstractFloatingView.getTopOpenView(mLauncher) == null
+ && mLauncher.isInState(NORMAL);
+
+ if (handleLongPress) {
+ // Check if the event is not near the edges
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ DragLayer dl = mLauncher.getDragLayer();
+ Rect insets = dp.getInsets();
+
+ mTempRect.set(insets.left, insets.top, dl.getWidth() - insets.right,
+ dl.getHeight() - insets.bottom);
+ mTempRect.inset(dp.edgeMarginPx, dp.edgeMarginPx);
+ handleLongPress = mTempRect.contains((int) ev.getX(), (int) ev.getY());
+ }
+
+ cancelLongPress();
+ if (handleLongPress) {
+ mLongPressState = STATE_REQUESTED;
+ mTouchDownPoint.set(ev.getX(), ev.getY());
+ mWorkspace.postDelayed(this, getLongPressTimeout());
+ }
+
+ mWorkspace.onTouchEvent(ev);
+ // Return true to keep receiving touch events
+ return true;
+ }
+
+ if (mLongPressState == STATE_PENDING_PARENT_INFORM) {
+ // Inform the workspace to cancel touch handling
+ ev.setAction(ACTION_CANCEL);
+ mWorkspace.onTouchEvent(ev);
+
+ ev.setAction(action);
+ mLongPressState = STATE_COMPLETED;
+ }
+
+ final boolean result;
+ if (mLongPressState == STATE_COMPLETED) {
+ // We have handled the touch, so workspace does not need to know anything anymore.
+ result = true;
+ } else if (mLongPressState == STATE_REQUESTED) {
+ mWorkspace.onTouchEvent(ev);
+ if (mWorkspace.isHandlingTouch()) {
+ cancelLongPress();
+ }
+
+ result = true;
+ } else {
+ // We don't want to handle touch, let workspace handle it as usual.
+ result = false;
+ }
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ cancelLongPress();
+ }
+ return result;
+ }
+
+ private void cancelLongPress() {
+ mWorkspace.removeCallbacks(this);
+ mLongPressState = STATE_CANCELLED;
+ }
+
+ @Override
+ public void run() {
+ if (mLongPressState == STATE_REQUESTED) {
+ mLongPressState = STATE_PENDING_PARENT_INFORM;
+ mWorkspace.getParent().requestDisallowInterceptTouchEvent(true);
+
+ mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
+ Action.Direction.NONE, ContainerType.WORKSPACE,
+ mWorkspace.getCurrentPage());
+ OptionsPopupView.showDefaultOptions(mLauncher, mTouchDownPoint.x, mTouchDownPoint.y);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java
index 242df2e..d478ffa 100644
--- a/src/com/android/launcher3/util/ComponentKey.java
+++ b/src/com/android/launcher3/util/ComponentKey.java
@@ -17,12 +17,8 @@
*/
import android.content.ComponentName;
-import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
-import com.android.launcher3.compat.UserManagerCompat;
-
import java.util.Arrays;
public class ComponentKey {
@@ -41,29 +37,6 @@
}
- /**
- * Creates a new component key from an encoded component key string in the form of
- * [flattenedComponentString#userId]. If the userId is not present, then it defaults
- * to the current user.
- */
- public ComponentKey(Context context, String componentKeyStr) {
- int userDelimiterIndex = componentKeyStr.indexOf("#");
- if (userDelimiterIndex != -1) {
- String componentStr = componentKeyStr.substring(0, userDelimiterIndex);
- Long componentUser = Long.valueOf(componentKeyStr.substring(userDelimiterIndex + 1));
- componentName = ComponentName.unflattenFromString(componentStr);
- user = UserManagerCompat.getInstance(context)
- .getUserForSerialNumber(componentUser.longValue());
- } else {
- // No user provided, default to the current user
- componentName = ComponentName.unflattenFromString(componentKeyStr);
- user = Process.myUserHandle();
- }
- Preconditions.assertNotNull(componentName);
- Preconditions.assertNotNull(user);
- mHashCode = Arrays.hashCode(new Object[] {componentName, user});
- }
-
@Override
public int hashCode() {
return mHashCode;
diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java
deleted file mode 100644
index a7f0d76..0000000
--- a/src/com/android/launcher3/util/ComponentKeyMapper.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.android.launcher3.util;
-
-/**
- * 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.
- */
-
-import android.support.annotation.Nullable;
-
-import java.util.Map;
-
-public class ComponentKeyMapper<T> {
-
- protected final ComponentKey mComponentKey;
-
- public ComponentKeyMapper(ComponentKey key) {
- this.mComponentKey = key;
- }
-
- public @Nullable T getItem(Map<ComponentKey, T> map) {
- return map.get(mComponentKey);
- }
-
- public String getPackage() {
- return mComponentKey.componentName.getPackageName();
- }
-
- public String getComponentClass() {
- return mComponentKey.componentName.getClassName();
- }
-
- @Override
- public String toString() {
- return mComponentKey.toString();
- }
-
-}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index b80e94d..b793f54 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -177,7 +177,10 @@
}
int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
- matrix[invert ? (m - cx - 1) : cx][cy] = i;
+ 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);
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 601a5ab..4485427 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -22,7 +22,6 @@
import android.util.Log;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -47,8 +46,8 @@
return false;
}
- public boolean isInstantApp(Launcher launcher, String packageName) {
- PackageManager packageManager = launcher.getPackageManager();
+ public boolean isInstantApp(Context context, String packageName) {
+ PackageManager packageManager = context.getPackageManager();
try {
return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
diff --git a/src/com/android/launcher3/util/ListViewHighlighter.java b/src/com/android/launcher3/util/ListViewHighlighter.java
new file mode 100644
index 0000000..ecad2af
--- /dev/null
+++ b/src/com/android/launcher3/util/ListViewHighlighter.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.ColorUtils;
+import android.view.View;
+import android.view.View.OnLayoutChangeListener;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AbsListView.RecyclerListener;
+import android.widget.ListView;
+
+import com.android.launcher3.R;
+
+/**
+ * Utility class to scroll and highlight a list view item
+ */
+public class ListViewHighlighter implements OnScrollListener, RecyclerListener,
+ OnLayoutChangeListener {
+
+ private final ListView mListView;
+ private int mPosHighlight;
+
+ private boolean mColorAnimated = false;
+
+ public ListViewHighlighter(ListView listView, int posHighlight) {
+ mListView = listView;
+ mPosHighlight = posHighlight;
+ mListView.setOnScrollListener(this);
+ mListView.setRecyclerListener(this);
+ mListView.addOnLayoutChangeListener(this);
+
+ mListView.post(this::tryHighlight);
+ }
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ mListView.post(this::tryHighlight);
+ }
+
+ private void tryHighlight() {
+ if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
+ return;
+ }
+ if (!highlightIfVisible(mListView.getFirstVisiblePosition(),
+ mListView.getLastVisiblePosition())) {
+ mListView.smoothScrollToPosition(mPosHighlight);
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView absListView, int i) { }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem,
+ int visibleItemCount, int totalItemCount) {
+ highlightIfVisible(firstVisibleItem, firstVisibleItem + visibleItemCount);
+ }
+
+ private boolean highlightIfVisible(int start, int end) {
+ if (mPosHighlight < 0 || mListView.getChildCount() == 0) {
+ return false;
+ }
+ if (start > mPosHighlight || mPosHighlight > end) {
+ return false;
+ }
+ highlightView(mListView.getChildAt(mPosHighlight - start));
+
+ // finish highlight
+ mListView.setOnScrollListener(null);
+ mListView.removeOnLayoutChangeListener(this);
+
+ mPosHighlight = -1;
+ return true;
+ }
+
+ @Override
+ public void onMovedToScrapHeap(View view) {
+ unhighlightView(view);
+ }
+
+ private void highlightView(View view) {
+ if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
+ // already highlighted
+ } else {
+ view.setTag(R.id.view_highlighted, true);
+ view.setTag(R.id.view_unhighlight_background, view.getBackground());
+ view.setBackground(getHighlightBackground());
+ view.postDelayed(() -> {
+ unhighlightView(view);
+ }, 15000L);
+ }
+ }
+
+ private void unhighlightView(View view) {
+ if (Boolean.TRUE.equals(view.getTag(R.id.view_highlighted))) {
+ Object background = view.getTag(R.id.view_unhighlight_background);
+ if (background instanceof Drawable) {
+ view.setBackground((Drawable) background);
+ }
+ view.setTag(R.id.view_unhighlight_background, null);
+ view.setTag(R.id.view_highlighted, false);
+ }
+ }
+
+ private ColorDrawable getHighlightBackground() {
+ int color = ColorUtils.setAlphaComponent(Themes.getColorAccent(mListView.getContext()), 26);
+ if (mColorAnimated) {
+ return new ColorDrawable(color);
+ }
+ mColorAnimated = true;
+ ColorDrawable bg = new ColorDrawable(Color.WHITE);
+ ObjectAnimator anim = ObjectAnimator.ofInt(bg, "color", Color.WHITE, color);
+ anim.setEvaluator(new ArgbEvaluator());
+ anim.setDuration(200L);
+ anim.setRepeatMode(ValueAnimator.REVERSE);
+ anim.setRepeatCount(4);
+ anim.start();
+ return bg;
+ }
+}
diff --git a/src/com/android/launcher3/util/LooperExecutor.java b/src/com/android/launcher3/util/LooperExecutor.java
index 5b7c20b..cc07469 100644
--- a/src/com/android/launcher3/util/LooperExecutor.java
+++ b/src/com/android/launcher3/util/LooperExecutor.java
@@ -33,6 +33,10 @@
mHandler = new Handler(looper);
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
@Override
public void execute(Runnable runnable) {
if (mHandler.getLooper() == Looper.myLooper()) {
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
deleted file mode 100644
index 009aee7..0000000
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ /dev/null
@@ -1,254 +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.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.LauncherActivityInfo;
-import android.os.Handler;
-import android.os.Process;
-import android.os.UserHandle;
-
-import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InstallShortcutReceiver;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherFiles;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
-import com.android.launcher3.SessionCommitReceiver;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.model.BgDataModel;
-import com.android.launcher3.model.ModelWriter;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * Handles addition of app shortcuts for managed profiles.
- * Methods of class should only be called on {@link LauncherModel#sWorkerThread}.
- */
-public class ManagedProfileHeuristic {
-
- private static final String USER_FOLDER_ID_PREFIX = "user_folder_";
-
- /**
- * Duration (in milliseconds) for which app shortcuts will be added to work folder.
- */
- private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
-
- public static void onAllAppsLoaded(final Context context,
- List<LauncherActivityInfo> apps, UserHandle user) {
- if (Process.myUserHandle().equals(user)) {
- return;
- }
-
- UserFolderInfo ufi = new UserFolderInfo(context, user, null);
- // We only handle folder creation once. Later icon additions are handled using package
- // or session events.
- if (ufi.folderAlreadyCreated) {
- return;
- }
-
- if (Utilities.ATLEAST_OREO && !SessionCommitReceiver.isEnabled(context)) {
- // Just mark the folder id preference to avoid new folder creation later.
- ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply();
- return;
- }
-
- InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_BULK_ADD);
- for (LauncherActivityInfo app : apps) {
- // Queue all items which should go in the work folder.
- if (app.getFirstInstallTime() < ufi.addIconToFolderTime) {
- InstallShortcutReceiver.queueActivityInfo(app, context);
- }
- }
- // Post the queue update on next frame, so that the loader gets finished.
- new Handler(LauncherModel.getWorkerLooper()).post(new Runnable() {
- @Override
- public void run() {
- InstallShortcutReceiver.disableAndFlushInstallQueue(
- InstallShortcutReceiver.FLAG_BULK_ADD, context);
- }
- });
- }
-
-
- /**
- * Utility class to help workspace icon addition.
- */
- public static class UserFolderInfo {
-
- final ArrayList<ShortcutInfo> pendingShortcuts = new ArrayList<>();
-
- final UserHandle user;
-
- final long userSerial;
- // Time until which icons will be added to folder instead.
- final long addIconToFolderTime;
-
- final String folderIdKey;
- final SharedPreferences prefs;
-
- final boolean folderAlreadyCreated;
- final FolderInfo folderInfo;
-
- boolean folderPendingAddition;
-
- public UserFolderInfo(Context context, UserHandle user, BgDataModel dataModel) {
- this.user = user;
-
- UserManagerCompat um = UserManagerCompat.getInstance(context);
- userSerial = um.getSerialNumberForUser(user);
- addIconToFolderTime = um.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
-
- folderIdKey = USER_FOLDER_ID_PREFIX + userSerial;
- prefs = prefs(context);
-
- folderAlreadyCreated = prefs.contains(folderIdKey);
- if (dataModel != null) {
- if (folderAlreadyCreated) {
- long folderId = prefs.getLong(folderIdKey, ItemInfo.NO_ID);
- folderInfo = dataModel.folders.get(folderId);
- } else {
- folderInfo = new FolderInfo();
- folderInfo.title = context.getText(R.string.work_folder_name);
- folderInfo.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
- folderPendingAddition = true;
- }
- } else {
- folderInfo = null;
- }
- }
-
- /**
- * Returns the ItemInfo which should be added to the workspace. In case the the provided
- * {@link ShortcutInfo} or a wrapped {@link FolderInfo} or null.
- */
- public ItemInfo convertToWorkspaceItem(
- ShortcutInfo shortcut, LauncherActivityInfo activityInfo) {
- if (activityInfo.getFirstInstallTime() >= addIconToFolderTime) {
- return shortcut;
- }
-
- if (folderAlreadyCreated) {
- if (folderInfo == null) {
- // Work folder was deleted by user, add icon to home screen.
- return shortcut;
- } else {
- // Add item to work folder instead. Nothing needs to be added
- // on the homescreen.
- pendingShortcuts.add(shortcut);
- return null;
- }
- }
-
- pendingShortcuts.add(shortcut);
- folderInfo.add(shortcut, false);
- if (folderPendingAddition) {
- folderPendingAddition = false;
- return folderInfo;
- } else {
- // WorkFolder already requested to be added. Nothing new needs to be added.
- return null;
- }
- }
-
- public void applyPendingState(ModelWriter writer) {
- if (folderInfo == null) {
- return;
- }
-
- int startingRank = 0;
- if (folderAlreadyCreated) {
- startingRank = folderInfo.contents.size();
- }
-
- for (ShortcutInfo info : pendingShortcuts) {
- info.rank = startingRank++;
- writer.addItemToDatabase(info, folderInfo.id, 0, 0, 0);
- }
-
- if (folderAlreadyCreated) {
- // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
- new MainThreadExecutor().execute(new Runnable() {
-
- @Override
- public void run() {
- folderInfo.prepareAutoUpdate();
- for (ShortcutInfo info : pendingShortcuts) {
- folderInfo.add(info, false);
- }
- }
- });
- } else {
- prefs.edit().putLong(folderIdKey, folderInfo.id).apply();
- }
- }
- }
-
- /**
- * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
- */
- public static void processAllUsers(List<UserHandle> users, Context context) {
- UserManagerCompat userManager = UserManagerCompat.getInstance(context);
- HashSet<String> validKeys = new HashSet<>();
- for (UserHandle user : users) {
- validKeys.add(USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user));
- }
-
- SharedPreferences prefs = prefs(context);
- SharedPreferences.Editor editor = prefs.edit();
- for (String key : prefs.getAll().keySet()) {
- if (!validKeys.contains(key)) {
- editor.remove(key);
- }
- }
- editor.apply();
- }
-
- /**
- * For each user, if a work folder has not been created, mark it such that the folder will
- * never get created.
- */
- public static void markExistingUsersForNoFolderCreation(Context context) {
- UserManagerCompat userManager = UserManagerCompat.getInstance(context);
- UserHandle myUser = Process.myUserHandle();
-
- SharedPreferences prefs = null;
- for (UserHandle user : userManager.getUserProfiles()) {
- if (myUser.equals(user)) {
- continue;
- }
- if (prefs == null) {
- prefs = prefs(context);
- }
- String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user);
- if (!prefs.contains(folderIdKey)) {
- prefs.edit().putLong(folderIdKey, ItemInfo.NO_ID).apply();
- }
- }
- }
-
- public static SharedPreferences prefs(Context context) {
- return context.getSharedPreferences(
- LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE);
- }
-}
diff --git a/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java b/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java
new file mode 100644
index 0000000..05a7d27
--- /dev/null
+++ b/src/com/android/launcher3/util/NoLocaleSQLiteHelper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import static android.database.sqlite.SQLiteDatabase.NO_LOCALIZED_COLLATORS;
+
+import static com.android.launcher3.Utilities.ATLEAST_P;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.sqlite.SQLiteDatabase.OpenParams;
+import android.database.sqlite.SQLiteOpenHelper;
+
+/**
+ * Extension of {@link SQLiteOpenHelper} which avoids creating default locale table by
+ * A context wrapper which creates databases without support for localized collators.
+ */
+public abstract class NoLocaleSQLiteHelper extends SQLiteOpenHelper {
+
+ public NoLocaleSQLiteHelper(Context context, String name, int version) {
+ super(ATLEAST_P ? context : new NoLocalContext(context), name, null, version);
+ if (ATLEAST_P) {
+ setOpenParams(new OpenParams.Builder().addOpenFlags(NO_LOCALIZED_COLLATORS).build());
+ }
+ }
+
+ private static class NoLocalContext extends ContextWrapper {
+ public NoLocalContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public SQLiteDatabase openOrCreateDatabase(
+ String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
+ return super.openOrCreateDatabase(
+ name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/NoLocaleSqliteContext.java b/src/com/android/launcher3/util/NoLocaleSqliteContext.java
deleted file mode 100644
index c8a5ffb..0000000
--- a/src/com/android/launcher3/util/NoLocaleSqliteContext.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.database.DatabaseErrorHandler;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteDatabase.CursorFactory;
-
-/**
- * A context wrapper which creates databases without support for localized collators.
- */
-public class NoLocaleSqliteContext extends ContextWrapper {
-
- public NoLocaleSqliteContext(Context context) {
- super(context);
- }
-
- @Override
- public SQLiteDatabase openOrCreateDatabase(
- String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
- return super.openOrCreateDatabase(
- name, mode | Context.MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler);
- }
-}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 81df153..0b3b632 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -17,6 +17,8 @@
package com.android.launcher3.util;
import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@@ -24,13 +26,23 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Toast;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -42,6 +54,8 @@
*/
public class PackageManagerHelper {
+ private static final String TAG = "PackageManagerHelper";
+
private final Context mContext;
private final PackageManager mPm;
private final LauncherAppsCompat mLauncherApps;
@@ -169,4 +183,35 @@
throw new RuntimeException(e);
}
}
+
+
+ /**
+ * 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));
+ return;
+ }
+ ComponentName componentName = null;
+ if (info instanceof AppInfo) {
+ componentName = ((AppInfo) info).componentName;
+ } else if (info instanceof ShortcutInfo) {
+ componentName = info.getTargetComponent();
+ } else if (info instanceof PendingAddItemInfo) {
+ componentName = ((PendingAddItemInfo) info).componentName;
+ } else if (info instanceof LauncherAppWidgetInfo) {
+ componentName = ((LauncherAppWidgetInfo) info).providerName;
+ }
+ if (componentName != null) {
+ try {
+ mLauncherApps.showAppDetailsForProfile(
+ componentName, info.user, sourceBounds, opts);
+ } catch (SecurityException | ActivityNotFoundException e) {
+ Toast.makeText(mContext, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Unable to launch settings", e);
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/PendingAnimation.java b/src/com/android/launcher3/util/PendingAnimation.java
new file mode 100644
index 0000000..4116d56
--- /dev/null
+++ b/src/com/android/launcher3/util/PendingAnimation.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.animation.AnimatorSet;
+import android.annotation.TargetApi;
+import android.os.Build;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to keep track of a running animation.
+ *
+ * This class allows attaching end callbacks to an animation is intended to be used with
+ * {@link com.android.launcher3.anim.AnimatorPlaybackController}, since in that case
+ * AnimationListeners are not properly dispatched.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class PendingAnimation {
+
+ private final ArrayList<Consumer<Boolean>> mEndListeners = new ArrayList<>();
+
+ public final AnimatorSet anim;
+
+ public PendingAnimation(AnimatorSet anim) {
+ this.anim = anim;
+ }
+
+ public void finish(boolean isSuccess) {
+ for (Consumer<Boolean> listeners : mEndListeners) {
+ listeners.accept(isSuccess);
+ }
+ mEndListeners.clear();
+ }
+
+ public void addEndListener(Consumer<Boolean> listener) {
+ mEndListeners.add(listener);
+ }
+}
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
index dabd40d..b8bcfed 100644
--- a/src/com/android/launcher3/util/PendingRequestArgs.java
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -57,7 +57,7 @@
mArg1 = parcel.readInt();
mObjectType = parcel.readInt();
- mObject = parcel.readParcelable(null);
+ mObject = parcel.readParcelable(getClass().getClassLoader());
}
@Override
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index 9084bfb..44c1762 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -92,10 +92,10 @@
/**
* A private inner class to prevent direct DB access.
*/
- private class MySQLiteOpenHelper extends SQLiteOpenHelper {
+ private class MySQLiteOpenHelper extends NoLocaleSQLiteHelper {
public MySQLiteOpenHelper(Context context, String name, int version) {
- super(new NoLocaleSqliteContext(context), name, null, version);
+ super(context, name, version);
}
@Override
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index bde9c0a..4aa2f37 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -15,6 +15,9 @@
*/
package com.android.launcher3.util;
+import static android.util.Log.VERBOSE;
+import static android.util.Log.isLoggable;
+
import android.os.SystemClock;
import android.os.Trace;
import android.util.ArrayMap;
@@ -40,7 +43,7 @@
if (ENABLED) {
MutableLong time = sUpTimes.get(sectionName);
if (time == null) {
- time = new MutableLong(Log.isLoggable(sectionName, Log.VERBOSE) ? 0 : -1);
+ time = new MutableLong(isLoggable(sectionName, VERBOSE) ? 0 : -1);
sUpTimes.put(sectionName, time);
}
if (time.value >= 0) {
diff --git a/src/com/android/launcher3/util/VerticalSwipeController.java b/src/com/android/launcher3/util/VerticalSwipeController.java
deleted file mode 100644
index 735fb2f..0000000
--- a/src/com/android/launcher3/util/VerticalSwipeController.java
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.support.animation.SpringAnimation;
-import android.util.Log;
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.SpringAnimationHandler;
-import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.touch.SwipeDetector.Direction;
-
-import java.util.ArrayList;
-
-/**
- * Handles vertical touch gesture on the DragLayer allowing transitioning from
- * {@link #mBaseState} to {@link LauncherState#ALL_APPS} and vice-versa.
- */
-public abstract class VerticalSwipeController extends AnimatorListenerAdapter
- implements TouchController, SwipeDetector.Listener {
-
- private static final String TAG = "VerticalSwipeController";
-
- private static final float RECATCH_REJECTION_FRACTION = .0875f;
- private static final int SINGLE_FRAME_MS = 16;
-
- // Progress after which the transition is assumed to be a success in case user does not fling
- private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
-
- protected final Launcher mLauncher;
- protected final SwipeDetector mDetector;
- private final LauncherState mBaseState;
- private final LauncherState mTargetState;
-
- private boolean mNoIntercept;
-
- private AnimatorPlaybackController mCurrentAnimation;
- private LauncherState mToState;
-
- private float mStartProgress;
- // Ratio of transition process [0, 1] to drag displacement (px)
- private float mProgressMultiplier;
-
- protected SpringAnimationHandler[] mSpringHandlers;
-
- public VerticalSwipeController(Launcher l, LauncherState baseState) {
- this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL);
- }
-
- public VerticalSwipeController(
- Launcher l, LauncherState baseState, LauncherState targetState, Direction dir) {
- mLauncher = l;
- mDetector = new SwipeDetector(l, this, dir);
- mBaseState = baseState;
- mTargetState = targetState;
- }
-
- private 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 shouldInterceptTouch(ev);
- }
-
- protected abstract boolean shouldInterceptTouch(MotionEvent ev);
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
- Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
- mDetector.finishedScrolling();
- mCurrentAnimation = null;
- }
- }
-
- protected void initSprings() {
- AllAppsContainerView appsView = mLauncher.getAppsView();
-
- SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
- if (handler == null) {
- mSpringHandlers = new SpringAnimationHandler[0];
- return;
- }
-
- ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
- handlers.add(handler);
-
- SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
- if (searchSpring != null) {
- SpringAnimationHandler searchHandler =
- new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
- searchHandler.add(searchSpring, true /* setDefaultValues */);
- handlers.add(searchHandler);
- }
-
- mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
- }
-
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mNoIntercept = !canInterceptTouch(ev);
- if (mNoIntercept) {
- return false;
- }
-
- // Now figure out which direction scroll events the controller will start
- // calling the callbacks.
- final int directionsToDetectScroll;
- boolean ignoreSlopWhenSettling = false;
-
- if (mCurrentAnimation != null) {
- if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
- } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
- } else {
- directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
- ignoreSlopWhenSettling = true;
- }
- } else {
- directionsToDetectScroll = getSwipeDirection(ev);
- }
-
- mDetector.setDetectableScrollConditions(
- directionsToDetectScroll, ignoreSlopWhenSettling);
-
- if (mSpringHandlers == null) {
- initSprings();
- }
- }
-
- if (mNoIntercept) {
- return false;
- }
-
- onControllerTouchEvent(ev);
- return mDetector.isDraggingOrSettling();
- }
-
- protected abstract int getSwipeDirection(MotionEvent ev);
-
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.addMovement(ev);
- }
- return mDetector.onTouchEvent(ev);
- }
-
- @Override
- public void onDragStart(boolean start) {
- if (mCurrentAnimation == null) {
- float range = getShiftRange();
- long maxAccuracy = (long) (2 * range);
-
- // Build current animation
- mToState = mLauncher.isInState(mTargetState) ? mBaseState : mTargetState;
- mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, maxAccuracy);
- mCurrentAnimation.getTarget().addListener(this);
- mStartProgress = 0;
- mProgressMultiplier =
- (mLauncher.isInState(mTargetState) ^ isTransitionFlipped() ? 1 : -1) / range;
- mCurrentAnimation.dispatchOnStart();
- } else {
- mCurrentAnimation.pause();
- mStartProgress = mCurrentAnimation.getProgressFraction();
- }
-
- for (SpringAnimationHandler h : mSpringHandlers) {
- h.skipToEnd();
- }
- }
-
- protected boolean isTransitionFlipped() {
- return false;
- }
-
- protected float getShiftRange() {
- return mLauncher.getAllAppsController().getShiftRange();
- }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- float deltaProgress = mProgressMultiplier * displacement;
- mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress);
- return true;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- final long animationDuration;
- final LauncherState targetState;
- final float progress = mCurrentAnimation.getProgressFraction();
-
- if (fling) {
- if (velocity < 0 ^ isTransitionFlipped()) {
- targetState = mTargetState;
- } else {
- targetState = mBaseState;
- }
- animationDuration = SwipeDetector.calculateDuration(velocity,
- mToState == targetState ? (1 - progress) : progress);
- // snap to top or bottom using the release velocity
- } else {
- if (progress > SUCCESS_TRANSITION_PROGRESS) {
- targetState = mToState;
- animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
- } else {
- targetState = mToState == mTargetState ? mBaseState : mTargetState;
- animationDuration = SwipeDetector.calculateDuration(velocity, progress);
- }
- }
-
- if (fling && targetState == mTargetState) {
- for (SpringAnimationHandler h : mSpringHandlers) {
- // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
- h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
- }
- }
-
- mCurrentAnimation.setEndAction(() -> {
- mLauncher.getStateManager().goToState(targetState, false);
- onTransitionComplete(fling, targetState == mToState);
- mDetector.finishedScrolling();
- mCurrentAnimation = null;
- });
-
- float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
-
- ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
- anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f);
- anim.setDuration(animationDuration);
- anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
- anim.start();
- }
-
- protected abstract void onTransitionComplete(boolean wasFling, boolean stateChanged);
-}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index e5c1dd1..acce308 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -34,24 +34,25 @@
OnAttachStateChangeListener {
private final ArrayList<Runnable> mTasks = new ArrayList<>();
- private final Executor mExecutor;
private Launcher mLauncher;
private View mAttachedView;
private boolean mCompleted;
- private boolean mIsExecuting;
private boolean mLoadAnimationCompleted;
private boolean mFirstDrawCompleted;
- public ViewOnDrawExecutor(Executor executor) {
- mExecutor = executor;
+ public void attachTo(Launcher launcher) {
+ attachTo(launcher, launcher.getWorkspace(), true /* waitForLoadAnimation */);
}
- public void attachTo(Launcher launcher) {
+ public void attachTo(Launcher launcher, View attachedView, boolean waitForLoadAnimation) {
mLauncher = launcher;
- mAttachedView = launcher.getWorkspace();
+ mAttachedView = attachedView;
mAttachedView.addOnAttachStateChangeListener(this);
+ if (!waitForLoadAnimation) {
+ mLoadAnimationCompleted = true;
+ }
attachObserver();
}
@@ -74,7 +75,7 @@
}
@Override
- public void onViewDetachedFromWindow(View v) { }
+ public void onViewDetachedFromWindow(View v) {}
@Override
public void onDraw() {
@@ -82,13 +83,6 @@
mAttachedView.post(this);
}
- /**
- * Returns whether the executor is still queuing tasks and hasn't yet executed them.
- */
- public boolean canQueue() {
- return !mIsExecuting && !mCompleted;
- }
-
public void onLoadAnimationCompleted() {
mLoadAnimationCompleted = true;
if (mAttachedView != null) {
@@ -100,18 +94,13 @@
public void run() {
// Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
- mIsExecuting = true;
- for (final Runnable r : mTasks) {
- mExecutor.execute(r);
- }
- markCompleted();
+ runAllTasks();
}
}
public void markCompleted() {
mTasks.clear();
mCompleted = true;
- mIsExecuting = false;
if (mAttachedView != null) {
mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
mAttachedView.removeOnAttachStateChangeListener(this);
@@ -121,4 +110,15 @@
}
LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT);
}
+
+ protected boolean isCompleted() {
+ return mCompleted;
+ }
+
+ protected void runAllTasks() {
+ for (final Runnable r : mTasks) {
+ r.run();
+ }
+ markCompleted();
+ }
}
diff --git a/src/com/android/launcher3/views/AllAppsScrim.java b/src/com/android/launcher3/views/AllAppsScrim.java
deleted file mode 100644
index 0ef9c8f..0000000
--- a/src/com/android/launcher3/views/AllAppsScrim.java
+++ /dev/null
@@ -1,233 +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.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.support.v4.graphics.ColorUtils;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.View;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.dynamicui.WallpaperColorInfo;
-import com.android.launcher3.dynamicui.WallpaperColorInfo.OnChangeListener;
-import com.android.launcher3.graphics.NinePatchDrawHelper;
-import com.android.launcher3.graphics.ShadowGenerator;
-import com.android.launcher3.util.Themes;
-
-import static com.android.launcher3.graphics.NinePatchDrawHelper.EXTENSION_PX;
-
-public class AllAppsScrim extends View implements OnChangeListener, Insettable {
-
- private static final int MAX_ALPHA = 235;
- private static final int MIN_ALPHA_PORTRAIT = 100;
- private static final int MIN_ALPHA_LANDSCAPE = MAX_ALPHA;
-
- protected final WallpaperColorInfo mWallpaperColorInfo;
- private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- private final Rect mDrawRect = new Rect();
- private final Rect mPadding = new Rect();
- private final Rect mInsets = new Rect();
-
- private final float mRadius;
- private final int mScrimColor;
-
- private int mMinAlpha;
- private int mAlphaRange;
-
- private final float mShadowBlur;
- private final Bitmap mShadowBitmap;
-
- private final NinePatchDrawHelper mShadowHelper = new NinePatchDrawHelper();
-
- private int mFillAlpha;
-
- private float mDrawHeight;
- private float mDrawOffsetY;
-
- public AllAppsScrim(Context context) {
- this(context, null);
- }
-
- public AllAppsScrim(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AllAppsScrim(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- mWallpaperColorInfo = WallpaperColorInfo.getInstance(context);
- mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
- mRadius = getResources().getDimension(R.dimen.all_apps_scrim_radius);
- mShadowBlur = getResources().getDimension(R.dimen.all_apps_scrim_blur);
-
- initDefaults();
- mFillAlpha = mMinAlpha;
- mShadowBitmap = generateShadowBitmap();
-
- updateColors(mWallpaperColorInfo);
- }
-
- private DeviceProfile initDefaults() {
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- mMinAlpha = grid.isVerticalBarLayout()
- ? MIN_ALPHA_LANDSCAPE : MIN_ALPHA_PORTRAIT;
- mAlphaRange = MAX_ALPHA - mMinAlpha;
- return grid;
- }
-
- private Bitmap generateShadowBitmap() {
- float curveBot = mRadius + mShadowBlur;
-
- ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
- builder.radius = mRadius;
- builder.shadowBlur = mShadowBlur;
-
- // Create the bitmap such that only the top half is drawn in the bitmap.
- int bitmapWidth = 2 * Math.round(curveBot) + EXTENSION_PX;
- int bitmapHeight = bitmapWidth / 2;
- Bitmap result = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
-
- float fullSize = 2 * curveBot + EXTENSION_PX - mShadowBlur;
- builder.bounds.set(mShadowBlur, mShadowBlur, fullSize, fullSize);
- builder.drawShadow(new Canvas(result));
- return result;
- }
-
- public Bitmap getShadowBitmap() {
- return mShadowBitmap;
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- mWallpaperColorInfo.addOnChangeListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- mWallpaperColorInfo.removeOnChangeListener(this);
- }
-
- @Override
- public void onExtractedColorsChanged(WallpaperColorInfo info) {
- updateColors(info);
- invalidate();
- }
-
- private void updateColors(WallpaperColorInfo info) {
- mFillPaint.setColor(ColorUtils.compositeColors(mScrimColor,
- ColorUtils.compositeColors(mScrimColor, info.getMainColor())));
- mFillPaint.setAlpha(mFillAlpha);
- invalidate();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- float edgeTop = getHeight() + mDrawOffsetY - mDrawHeight + mPadding.top;
- float edgeRight = getWidth() - mPadding.right;
-
- if (mPadding.left > 0 || mPadding.right > 0) {
- mShadowHelper.drawVerticallyStretched(mShadowBitmap, canvas,
- mPadding.left - mShadowBlur,
- edgeTop - mShadowBlur,
- edgeRight + mShadowBlur,
- getHeight());
- } else {
- mShadowHelper.draw(mShadowBitmap, canvas, mPadding.left - mShadowBlur,
- edgeTop - mShadowBlur, edgeRight + mShadowBlur);
- }
- canvas.drawRoundRect(mPadding.left, edgeTop, edgeRight,
- getHeight() + mRadius, mRadius, mRadius, mFillPaint);
- }
-
- public void setProgress(float translateY, float alpha) {
- int newAlpha = Math.round(alpha * mAlphaRange + mMinAlpha);
- // Negative translation means the scrim is moving up. For negative translation, we change
- // draw offset as it requires redraw (since more area of the scrim needs to be shown). For
- // position translation, we simply translate the scrim down as it avoids invalidate and
- // hence could be optimized by the platform.
- float drawOffsetY = Math.min(translateY, 0);
-
- if (newAlpha != mFillAlpha || drawOffsetY != mDrawOffsetY) {
- invalidateDrawRect();
-
- mFillAlpha = newAlpha;
- mFillPaint.setAlpha(mFillAlpha);
- mDrawOffsetY = drawOffsetY;
- invalidateDrawRect();
- }
-
- setTranslationY(Math.max(translateY, 0));
- }
-
- private void invalidateDrawRect() {
- mDrawRect.top = (int) (getHeight()
- + mDrawOffsetY - mDrawHeight + mPadding.top - mShadowBlur - 0.5f);
- invalidate(mDrawRect);
- }
-
- public void setDrawRegion(float height) {
- mDrawHeight = height;
- }
-
- @Override
- public void setInsets(Rect insets) {
- mInsets.set(insets);
- DeviceProfile grid = initDefaults();
- if (grid.isVerticalBarLayout()) {
- mPadding.set(grid.workspacePadding);
- mPadding.bottom = 0;
- mPadding.left += mInsets.left;
- mPadding.top = mInsets.top;
- mPadding.right += mInsets.right;
- setDrawRegion(0);
- } else {
- mPadding.setEmpty();
- float scrimMargin = getResources().getDimension(R.dimen.all_apps_scrim_margin);
- setDrawRegion(grid.hotseatBarSizePx + insets.bottom + scrimMargin);
- }
- updateDrawRect(grid);
- invalidate();
- }
-
- @Override
- protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- super.onLayout(changed, left, top, right, bottom);
- updateDrawRect(Launcher.getLauncher(getContext()).getDeviceProfile());
- }
-
- private void updateDrawRect(DeviceProfile grid) {
- mDrawRect.bottom = getHeight();
- if (grid.isVerticalBarLayout()) {
- mDrawRect.left = (int) (mPadding.left - mShadowBlur - 0.5f);
- mDrawRect.right = (int) (getWidth() - mPadding.right + 0.5f);
- } else {
- mDrawRect.left = 0;
- mDrawRect.right = getWidth();
- }
- }
-}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
new file mode 100644
index 0000000..489e59e
--- /dev/null
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -0,0 +1,325 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseActivity;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.TouchController;
+
+import java.util.ArrayList;
+
+/**
+ * A viewgroup with utility methods for drag-n-drop and touch interception
+ */
+public abstract class BaseDragLayer<T extends BaseDraggingActivity> extends InsettableFrameLayout {
+
+ protected final int[] mTmpXY = new int[2];
+ protected final Rect mHitRect = new Rect();
+
+ protected final T mActivity;
+
+ protected TouchController[] mControllers;
+ protected TouchController mActiveController;
+ private TouchCompleteListener mTouchCompleteListener;
+
+ public BaseDragLayer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mActivity = (T) BaseActivity.fromContext(context);
+ }
+
+
+ public boolean isEventOverView(View view, MotionEvent ev) {
+ getDescendantRectRelativeToSelf(view, mHitRect);
+ return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (mTouchCompleteListener != null) {
+ mTouchCompleteListener.onTouchComplete();
+ }
+ mTouchCompleteListener = null;
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ mActivity.finishAutoCancelActionMode();
+ }
+ return findActiveController(ev);
+ }
+
+ protected boolean findActiveController(MotionEvent ev) {
+ mActiveController = null;
+
+ AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
+ mActiveController = topView;
+ return true;
+ }
+
+ for (TouchController controller : mControllers) {
+ if (controller.onControllerInterceptTouchEvent(ev)) {
+ mActiveController = controller;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ // Shortcuts can appear above folder
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ if (child == topView) {
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+ // Skip propagating onRequestSendAccessibilityEvent for all other children
+ // which are not topView
+ return false;
+ }
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+
+ @Override
+ public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ // Only add the top view as a child for accessibility when it is open
+ childrenForAccessibility.add(topView);
+ } else {
+ super.addChildrenForAccessibility(childrenForAccessibility);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ int action = ev.getAction();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (mTouchCompleteListener != null) {
+ mTouchCompleteListener.onTouchComplete();
+ }
+ mTouchCompleteListener = null;
+ }
+
+ if (mActiveController != null) {
+ return mActiveController.onControllerTouchEvent(ev);
+ } else {
+ // In case no child view handled the touch event, we may not get onIntercept anymore
+ return findActiveController(ev);
+ }
+ }
+
+ /**
+ * Determine the rect of the descendant in this DragLayer's coordinates
+ *
+ * @param descendant The descendant whose coordinates we want to find.
+ * @param r The rect into which to place the results.
+ * @return The factor by which this descendant is scaled relative to this DragLayer.
+ */
+ public float getDescendantRectRelativeToSelf(View descendant, Rect r) {
+ mTmpXY[0] = 0;
+ mTmpXY[1] = 0;
+ float scale = getDescendantCoordRelativeToSelf(descendant, mTmpXY);
+
+ r.set(mTmpXY[0], mTmpXY[1],
+ (int) (mTmpXY[0] + scale * descendant.getMeasuredWidth()),
+ (int) (mTmpXY[1] + scale * descendant.getMeasuredHeight()));
+ return scale;
+ }
+
+ public float getLocationInDragLayer(View child, int[] loc) {
+ loc[0] = 0;
+ loc[1] = 0;
+ return getDescendantCoordRelativeToSelf(child, loc);
+ }
+
+ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord) {
+ return getDescendantCoordRelativeToSelf(descendant, coord, false);
+ }
+
+ /**
+ * Given a coordinate relative to the descendant, find the coordinate in this DragLayer's
+ * coordinates.
+ *
+ * @param descendant The descendant to which the passed coordinate is relative.
+ * @param coord The coordinate that we want mapped.
+ * @param includeRootScroll Whether or not to account for the scroll of the root descendant:
+ * sometimes this is relevant as in a child's coordinates within the root descendant.
+ * @return The factor by which this descendant is scaled relative to this DragLayer. Caution
+ * this scale factor is assumed to be equal in X and Y, and so if at any point this
+ * assumption fails, we will need to return a pair of scale factors.
+ */
+ public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
+ boolean includeRootScroll) {
+ return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
+ coord, includeRootScroll);
+ }
+
+ /**
+ * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
+ */
+ public void mapCoordInSelfToDescendant(View descendant, int[] coord) {
+ Utilities.mapCoordInSelfToDescendant(descendant, this, coord);
+ }
+
+ public void getViewRectRelativeToSelf(View v, Rect r) {
+ int[] loc = new int[2];
+ getLocationInWindow(loc);
+ int x = loc[0];
+ int y = loc[1];
+
+ v.getLocationInWindow(loc);
+ int vX = loc[0];
+ int vY = loc[1];
+
+ int left = vX - x;
+ int top = vY - y;
+ r.set(left, top, left + v.getMeasuredWidth(), top + v.getMeasuredHeight());
+ }
+
+ @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) {
+ return topView.requestFocus(direction, previouslyFocusedRect);
+ } else {
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+ }
+
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ View topView = AbstractFloatingView.getTopOpenView(mActivity);
+ if (topView != null) {
+ topView.addFocusables(views, direction);
+ } else {
+ super.addFocusables(views, direction, focusableMode);
+ }
+ }
+
+ public void setTouchCompleteListener(TouchCompleteListener listener) {
+ mTouchCompleteListener = listener;
+ }
+
+ public interface TouchCompleteListener {
+ void onTouchComplete();
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ // Override to allow type-checking of LayoutParams.
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new LayoutParams(p);
+ }
+
+ public static class LayoutParams extends InsettableFrameLayout.LayoutParams {
+ public int x, y;
+ public boolean customPosition = false;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams lp) {
+ super(lp);
+ }
+
+ public void setWidth(int width) {
+ this.width = width;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public void setHeight(int height) {
+ this.height = height;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public void setX(int x) {
+ this.x = x;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public void setY(int y) {
+ this.y = y;
+ }
+
+ public int getY() {
+ return y;
+ }
+ }
+
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ int count = getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = getChildAt(i);
+ final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
+ if (flp instanceof LayoutParams) {
+ final LayoutParams lp = (LayoutParams) flp;
+ if (lp.customPosition) {
+ child.layout(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/BottomUserEducationView.java b/src/com/android/launcher3/views/BottomUserEducationView.java
index d79d0ce..a291fc6 100644
--- a/src/com/android/launcher3/views/BottomUserEducationView.java
+++ b/src/com/android/launcher3/views/BottomUserEducationView.java
@@ -20,12 +20,17 @@
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.TouchDelegate;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+
public class BottomUserEducationView extends AbstractSlideInView implements Insettable {
private static final String KEY_SHOWED_BOTTOM_USER_EDUCATION = "showed_bottom_user_education";
@@ -34,6 +39,8 @@
private final Rect mInsets = new Rect();
+ private View mCloseButton;
+
public BottomUserEducationView(Context context, AttributeSet attr) {
this(context, attr, 0);
}
@@ -45,9 +52,17 @@
}
@Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mCloseButton = findViewById(R.id.close_bottom_user_tip);
+ mCloseButton.setOnClickListener(view -> handleClose(true));
+ }
+
+ @Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
setTranslationShift(mTranslationShift);
+ expandTouchAreaOfCloseButton();
}
@Override
@@ -79,6 +94,10 @@
// close action.
mLauncher.getSharedPrefs().edit()
.putBoolean(KEY_SHOWED_BOTTOM_USER_EDUCATION, true).apply();
+ sendCustomAccessibilityEvent(
+ BottomUserEducationView.this,
+ AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+ getContext().getString(R.string.bottom_work_tab_user_education_closed));
}
}
@@ -110,4 +129,15 @@
launcher.getDragLayer().addView(bottomUserEducationView);
bottomUserEducationView.open(true);
}
+
+ private void expandTouchAreaOfCloseButton() {
+ Rect hitRect = new Rect();
+ mCloseButton.getHitRect(hitRect);
+ hitRect.left -= mCloseButton.getWidth();
+ hitRect.top -= mCloseButton.getHeight();
+ hitRect.right += mCloseButton.getWidth();
+ hitRect.bottom += mCloseButton.getHeight();
+ View parent = (View) mCloseButton.getParent();
+ parent.setTouchDelegate(new TouchDelegate(hitRect, mCloseButton));
+ }
}
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
index c8203f7..a11a8c5 100644
--- a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -20,7 +20,6 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Region;
import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.widget.TextView;
@@ -63,10 +62,10 @@
ColorUtils.setAlphaComponent(mShadowInfo.ambientShadowColor, alpha));
drawWithoutBadge(canvas);
- canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.save();
canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
getScrollX() + getWidth(),
- getScrollY() + getHeight(), Region.Op.INTERSECT);
+ getScrollY() + getHeight());
getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffset,
ColorUtils.setAlphaComponent(mShadowInfo.keyShadowColor, alpha));
diff --git a/src/com/android/launcher3/views/LauncherDragIndicator.java b/src/com/android/launcher3/views/LauncherDragIndicator.java
new file mode 100644
index 0000000..986e4be
--- /dev/null
+++ b/src/com/android/launcher3/views/LauncherDragIndicator.java
@@ -0,0 +1,129 @@
+/*
+ * 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.views;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+
+public class LauncherDragIndicator extends ImageView implements Insettable, OnClickListener {
+
+ 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;
+
+ protected final Launcher mLauncher;
+
+ public LauncherDragIndicator(Context context) {
+ this(context, null);
+ }
+
+ public LauncherDragIndicator(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public LauncherDragIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+ setOnClickListener(this);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+
+ if (grid.isVerticalBarLayout()) {
+ if (grid.isSeascape()) {
+ lp.leftMargin = grid.hotseatBarSidePaddingPx;
+ lp.rightMargin = insets.right;
+ lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;
+ } else {
+ lp.leftMargin = insets.left;
+ lp.rightMargin = grid.hotseatBarSidePaddingPx;
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ }
+ lp.bottomMargin = grid.workspacePadding.bottom;
+ setImageResource(R.drawable.all_apps_handle_landscape);
+ } else {
+ lp.leftMargin = lp.rightMargin = 0;
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.bottomMargin = getPortraitBottomMargin(grid, insets);
+ setImageResource(R.drawable.ic_drag_indicator);
+ }
+
+ lp.width = lp.height = grid.pageIndicatorSizePx;
+ setLayoutParams(lp);
+ }
+
+ protected int getPortraitBottomMargin(DeviceProfile grid, Rect insets) {
+ return grid.hotseatBarSizePx + insets.bottom - grid.pageIndicatorSizePx;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ initCustomActions(info);
+ }
+
+ protected void initCustomActions(AccessibilityNodeInfo info) {
+ Context context = getContext();
+ if (Utilities.isWallpaperAllowed(context)) {
+ info.addAction(new AccessibilityAction(WALLPAPERS, context.getText(WALLPAPERS)));
+ }
+ info.addAction(new AccessibilityAction(WIDGETS, context.getText(WIDGETS)));
+ info.addAction(new AccessibilityAction(SETTINGS, context.getText(SETTINGS)));
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ if (action == WALLPAPERS) {
+ return OptionsPopupView.startWallpaperPicker(this);
+ } else if (action == WIDGETS) {
+ return OptionsPopupView.onWidgetsClicked(this);
+ } else if (action == SETTINGS) {
+ return OptionsPopupView.startSettings(this);
+ }
+ return super.performAccessibilityAction(action, arguments);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (!mLauncher.isInState(ALL_APPS)) {
+ mLauncher.getUserEventDispatcher().logActionOnControl(
+ Action.Touch.TAP, ControlType.ALL_APPS_BUTTON);
+ mLauncher.getStateManager().goToState(ALL_APPS);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
new file mode 100644
index 0000000..56b92c7
--- /dev/null
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -0,0 +1,213 @@
+/*
+ * 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.views;
+
+import static com.android.launcher3.BaseDraggingActivity.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION;
+import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.Toast;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.popup.ArrowPopup;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.widget.WidgetsFullSheet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Popup shown on long pressing an empty space in launcher
+ */
+public class OptionsPopupView extends ArrowPopup
+ implements OnClickListener, OnLongClickListener {
+
+ private final ArrayMap<View, OptionItem> mItemMap = new ArrayMap<>();
+ private RectF mTargetRect;
+
+ public OptionsPopupView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void onClick(View view) {
+ handleViewClick(view, Action.Touch.TAP);
+ }
+
+ @Override
+ public boolean onLongClick(View view) {
+ return handleViewClick(view, Action.Touch.LONGPRESS);
+ }
+
+ private boolean handleViewClick(View view, int action) {
+ OptionItem item = mItemMap.get(view);
+ if (item == null) {
+ return false;
+ }
+ if (item.mControlTypeForLog > 0) {
+ logTap(action, item.mControlTypeForLog);
+ }
+ if (item.mClickListener.onLongClick(view)) {
+ close(true);
+ return true;
+ }
+ return false;
+ }
+
+ private void logTap(int action, int controlType) {
+ mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ if (mLauncher.getDragLayer().isEventOverView(this, ev)) {
+ return false;
+ }
+ close(true);
+ return true;
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // TODO:
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_OPTIONS_POPUP) != 0;
+ }
+
+ @Override
+ protected void getTargetObjectLocation(Rect outPos) {
+ mTargetRect.roundOut(outPos);
+ }
+
+ public static void 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;
+
+ for (OptionItem item : items) {
+ DeepShortcutView view = popup.inflateAndAdd(R.layout.system_shortcut, popup);
+ view.getIconView().setBackgroundResource(item.mIconRes);
+ view.getBubbleText().setText(item.mLabelRes);
+ view.setDividerVisibility(View.INVISIBLE);
+ view.setOnClickListener(popup);
+ view.setOnLongClickListener(popup);
+ popup.mItemMap.put(view, item);
+ }
+ popup.reorderAndShow(popup.getChildCount());
+ }
+
+ public static void showDefaultOptions(Launcher launcher, float x, float y) {
+ float halfSize = launcher.getResources().getDimension(R.dimen.options_menu_thumb_size) / 2;
+ if (x < 0 || y < 0) {
+ x = launcher.getDragLayer().getWidth() / 2;
+ y = launcher.getDragLayer().getHeight() / 2;
+ }
+ RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
+
+ ArrayList<OptionItem> options = new ArrayList<>();
+ options.add(new OptionItem(R.string.wallpaper_button_text, R.drawable.ic_wallpaper,
+ ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
+ options.add(new OptionItem(R.string.widget_button_text, R.drawable.ic_widget,
+ ControlType.WIDGETS_BUTTON, OptionsPopupView::onWidgetsClicked));
+ options.add(new OptionItem(R.string.settings_button_text, R.drawable.ic_setting,
+ ControlType.SETTINGS_BUTTON, OptionsPopupView::startSettings));
+
+ show(launcher, target, options);
+ }
+
+ public static boolean onWidgetsClicked(View view) {
+ Launcher launcher = Launcher.getLauncher(view.getContext());
+ if (launcher.getPackageManager().isSafeMode()) {
+ Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
+ return false;
+ } else {
+ WidgetsFullSheet.show(launcher, true /* animated */);
+ return true;
+ }
+ }
+
+ public static boolean startSettings(View view) {
+ Launcher launcher = Launcher.getLauncher(view.getContext());
+ launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+ .setPackage(launcher.getPackageName())
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ return true;
+ }
+
+ /**
+ * Event handler for the wallpaper picker button that appears after a long press
+ * on the home screen.
+ */
+ public 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();
+ return false;
+ }
+ Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
+ .putExtra(EXTRA_WALLPAPER_OFFSET,
+ launcher.getWorkspace().getWallpaperOffsetForCenterPage());
+
+ String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
+ if (!TextUtils.isEmpty(pickerPackage)) {
+ intent.setPackage(pickerPackage);
+ } else {
+ // If there is no target package, use the default intent chooser animation
+ intent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
+ }
+ return launcher.startActivitySafely(v, intent, null);
+ }
+
+ public static class OptionItem {
+
+ private final int mLabelRes;
+ private final int mIconRes;
+ private final int mControlTypeForLog;
+ private final OnLongClickListener mClickListener;
+
+ public OptionItem(int labelRes, int iconRes, int controlTypeForLog,
+ OnLongClickListener clickListener) {
+ mLabelRes = labelRes;
+ mIconRes = iconRes;
+ mControlTypeForLog = controlTypeForLog;
+ mClickListener = clickListener;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index fc121d3..1cd6699 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -22,6 +22,8 @@
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Property;
@@ -43,6 +45,7 @@
public class RecyclerViewFastScroller extends View {
private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+ private static final Rect sTempRect = new Rect();
private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
@@ -204,9 +207,9 @@
* Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing).
*/
- public boolean handleTouchEvent(MotionEvent ev) {
- int x = (int) ev.getX();
- int y = (int) ev.getY();
+ public boolean handleTouchEvent(MotionEvent ev, Point offset) {
+ int x = (int) ev.getX() - offset.x;
+ int y = (int) ev.getY() - offset.y;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// Keep track of the down positions
@@ -260,7 +263,6 @@
}
private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
- mRv.getParent().requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
if (mCanThumbDetach) {
mIsThumbDetached = true;
@@ -289,7 +291,7 @@
if (mThumbOffsetY < 0) {
return;
}
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ int saveCount = canvas.save();
canvas.translate(getWidth() / 2, mRv.getScrollBarTop());
// Draw the track
float halfW = mWidth / 2;
@@ -358,4 +360,16 @@
mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
mPopupView.setTranslationY(top);
}
+
+ public boolean isHitInParent(float x, float y, Point outOffset) {
+ if (mThumbOffsetY < 0) {
+ return false;
+ }
+ getHitRect(sTempRect);
+ sTempRect.top += mRv.getScrollBarTop();
+ if (outOffset != null) {
+ outOffset.set(sTempRect.left, sTempRect.top);
+ }
+ return sTempRect.contains((int) x, (int) y);
+ }
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
new file mode 100644
index 0000000..090b3e6
--- /dev/null
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -0,0 +1,157 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.support.animation.FloatPropertyCompat;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
+import android.support.annotation.NonNull;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.EdgeEffectFactory;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.view.View;
+import android.widget.EdgeEffect;
+import android.widget.RelativeLayout;
+
+import static android.support.animation.SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
+import static android.support.animation.SpringForce.STIFFNESS_LOW;
+import static android.support.animation.SpringForce.STIFFNESS_MEDIUM;
+
+public class SpringRelativeLayout extends RelativeLayout {
+
+ private static final float STIFFNESS = (STIFFNESS_MEDIUM + STIFFNESS_LOW) / 2;
+ private static final float DAMPING_RATIO = DAMPING_RATIO_MEDIUM_BOUNCY;
+ private static final float VELOCITY_MULTIPLIER = 0.3f;
+
+ private static final FloatPropertyCompat<SpringRelativeLayout> DAMPED_SCROLL =
+ new FloatPropertyCompat<SpringRelativeLayout>("value") {
+
+ @Override
+ public float getValue(SpringRelativeLayout object) {
+ return object.mDampedScrollShift;
+ }
+
+ @Override
+ public void setValue(SpringRelativeLayout object, float value) {
+ object.setDampedScrollShift(value);
+ }
+ };
+
+ private final SparseBooleanArray mSpringViews = new SparseBooleanArray();
+ private final SpringAnimation mSpring;
+
+ private float mDampedScrollShift = 0;
+
+ public SpringRelativeLayout(Context context) {
+ this(context, null);
+ }
+
+ public SpringRelativeLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public SpringRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mSpring = new SpringAnimation(this, DAMPED_SCROLL, 0);
+ mSpring.setSpring(new SpringForce(0)
+ .setStiffness(STIFFNESS)
+ .setDampingRatio(DAMPING_RATIO));
+ }
+
+ public void addSpringView(int id) {
+ mSpringViews.put(id, true);
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
+ canvas.translate(0, mDampedScrollShift);
+ boolean result = super.drawChild(canvas, child, drawingTime);
+ canvas.translate(0, -mDampedScrollShift);
+ return result;
+ }
+ return super.drawChild(canvas, child, drawingTime);
+ }
+
+ private void setDampedScrollShift(float shift) {
+ if (shift != mDampedScrollShift) {
+ mDampedScrollShift = shift;
+ invalidate();
+ }
+ }
+
+ private void finishScrollWithVelocity(float velocity) {
+ mSpring.setStartVelocity(velocity);
+ mSpring.setStartValue(mDampedScrollShift);
+ mSpring.start();
+ }
+
+ public EdgeEffectFactory createEdgeEffectFactory() {
+ return new SpringEdgeEffectFactory();
+ }
+
+ private class SpringEdgeEffectFactory extends EdgeEffectFactory {
+
+ @NonNull @Override
+ protected EdgeEffect createEdgeEffect(RecyclerView view, int direction) {
+ switch (direction) {
+ case DIRECTION_TOP:
+ return new SpringEdgeEffect(getContext(), +VELOCITY_MULTIPLIER);
+ case DIRECTION_BOTTOM:
+ return new SpringEdgeEffect(getContext(), -VELOCITY_MULTIPLIER);
+ }
+ return super.createEdgeEffect(view, direction);
+ }
+ }
+
+ private class SpringEdgeEffect extends EdgeEffect {
+
+ private final float mVelocityMultiplier;
+
+ private float mDistance;
+
+ public SpringEdgeEffect(Context context, float velocityMultiplier) {
+ super(context);
+ mVelocityMultiplier = velocityMultiplier;
+ }
+
+ @Override
+ public boolean draw(Canvas canvas) {
+ return false;
+ }
+
+ @Override
+ public void onAbsorb(int velocity) {
+ finishScrollWithVelocity(velocity * mVelocityMultiplier);
+ }
+
+ @Override
+ public void onPull(float deltaDistance, float displacement) {
+ mDistance += deltaDistance * (mVelocityMultiplier / 3f);
+ setDampedScrollShift(mDistance * getHeight());
+ }
+
+ @Override
+ public void onRelease() {
+ mDistance = 0;
+ finishScrollWithVelocity(0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/TopRoundedCornerView.java b/src/com/android/launcher3/views/TopRoundedCornerView.java
new file mode 100644
index 0000000..7888b08
--- /dev/null
+++ b/src/com/android/launcher3/views/TopRoundedCornerView.java
@@ -0,0 +1,82 @@
+/*
+ * 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.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+/**
+ * View with top rounded corners.
+ */
+public class TopRoundedCornerView extends SpringRelativeLayout {
+
+ private final RectF mRect = new RectF();
+ private final Path mClipPath = new Path();
+ private float[] mRadii;
+
+ private final Paint mNavBarScrimPaint;
+ private int mNavBarScrimHeight = 0;
+
+ public TopRoundedCornerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ int radius = getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
+ mRadii = new float[] {radius, radius, radius, radius, 0, 0, 0, 0};
+
+ mNavBarScrimPaint = new Paint();
+ mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
+ }
+
+ public TopRoundedCornerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public void setNavBarScrimHeight(int height) {
+ if (mNavBarScrimHeight != height) {
+ mNavBarScrimHeight = height;
+ invalidate();
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.save();
+ canvas.clipPath(mClipPath);
+ super.draw(canvas);
+ canvas.restore();
+
+ if (mNavBarScrimHeight > 0) {
+ canvas.drawRect(0, getHeight() - mNavBarScrimHeight, getWidth(), getHeight(),
+ mNavBarScrimPaint);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ mClipPath.reset();
+ mClipPath.addRoundRect(mRect, mRadii, Path.Direction.CW);
+ }
+}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index b22509c..10708d6 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -31,7 +31,8 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragOptions;
-import com.android.launcher3.graphics.GradientView;
+import com.android.launcher3.graphics.ColorScrim;
+import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.SystemUiController;
@@ -48,10 +49,11 @@
/* Touch handling related member variables. */
private Toast mWidgetInstructionToast;
- protected GradientView mGradientView;
+ protected final ColorScrim mColorScrim;
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ mColorScrim = ColorScrim.createExtractedColorScrim(this);
}
@Override
@@ -70,7 +72,7 @@
@Override
public final boolean onLongClick(View v) {
- if (!mLauncher.isDraggingEnabled()) return false;
+ if (!ItemLongClickListener.canStartDrag(mLauncher)) return false;
if (v instanceof WidgetCell) {
return beginDraggingWidget((WidgetCell) v);
@@ -80,7 +82,7 @@
protected void setTranslationShift(float translationShift) {
super.setTranslationShift(translationShift);
- mGradientView.setAlpha(1 - mTranslationShift);
+ mColorScrim.setProgress(1 - mTranslationShift);
}
private boolean beginDraggingWidget(WidgetCell v) {
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
index 37e5efc..3a24c3d 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -44,7 +44,8 @@
mPaint = new TextPaint();
mPaint.setColor(Color.WHITE);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
- mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+ mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx,
+ getResources().getDisplayMetrics()));
setBackgroundResource(R.drawable.bg_deferred_app_widget);
}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 0b1474a..12859c7 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -47,7 +47,7 @@
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
+import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
import java.util.ArrayList;
@@ -488,13 +488,14 @@
// Only reinflate when the final configuration is same as the required configuration
if (mReinflateOnConfigChange && isSameOrientation()) {
mReinflateOnConfigChange = false;
- if (isAttachedToWindow()) {
- reInflate();
- }
+ reInflate();
}
}
public void reInflate() {
+ if (!isAttachedToWindow()) {
+ return;
+ }
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
// Remove and rebind the current widget (which was inflated in the wrong
// orientation), but don't delete it from the database
diff --git a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index 6970833..961799d 100644
--- a/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -43,6 +43,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
@@ -83,7 +84,7 @@
setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
updateAppWidget(null);
- setOnClickListener(mLauncher);
+ setOnClickListener(ItemClickHandler.INSTANCE);
if (info.pendingItemInfo == null) {
info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 6a9013d..a258485 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -32,7 +32,6 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.graphics.GradientView;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.PackageUserKey;
@@ -55,10 +54,6 @@
super(context, attrs, defStyleAttr);
setWillNotDraw(false);
mInsets = new Rect();
-
- mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate(
- R.layout.widgets_bottom_sheet_scrim, mLauncher.getDragLayer(), false);
- mGradientView.setProgress(1, false);
mContent = this;
}
@@ -75,7 +70,6 @@
onWidgetsBound();
- mLauncher.getDragLayer().addView(mGradientView);
mLauncher.getDragLayer().addView(this);
mIsOpen = false;
open(true);
@@ -157,12 +151,6 @@
}
@Override
- protected void onCloseComplete() {
- super.onCloseComplete();
- mLauncher.getDragLayer().removeView(mGradientView);
- }
-
- @Override
protected boolean isOfType(@FloatingViewType int type) {
return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
}
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index d62a909..a622624 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -23,7 +23,6 @@
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.View;
import android.view.animation.AnimationUtils;
import com.android.launcher3.Insettable;
@@ -31,6 +30,8 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetHost.ProviderChangedListener;
import com.android.launcher3.R;
+import com.android.launcher3.views.RecyclerViewFastScroller;
+import com.android.launcher3.views.TopRoundedCornerView;
/**
* Popup for showing the full list of available widgets
@@ -46,7 +47,6 @@
private final WidgetsListAdapter mAdapter;
- private View mNavBarScrim;
private WidgetsRecyclerView mRecyclerView;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -65,15 +65,14 @@
protected void onFinishInflate() {
super.onFinishInflate();
mContent = findViewById(R.id.container);
- mNavBarScrim = findViewById(R.id.nav_bar_bg);
mRecyclerView = findViewById(R.id.widgets_list_view);
mRecyclerView.setAdapter(mAdapter);
mAdapter.setApplyBitmapDeferred(true, mRecyclerView);
- mGradientView = findViewById(R.id.gradient_bg);
- mGradientView.setProgress(1, false);
-
+ TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
+ springLayout.addSpringView(R.id.widgets_list_view);
+ mRecyclerView.setEdgeEffectFactory(springLayout.createEdgeEffectFactory());
onWidgetsBound();
}
@@ -94,7 +93,6 @@
public void setInsets(Rect insets) {
mInsets.set(insets);
- mNavBarScrim.getLayoutParams().height = insets.bottom;
mRecyclerView.setPadding(
mRecyclerView.getPaddingLeft(), mRecyclerView.getPaddingTop(),
mRecyclerView.getPaddingRight(), insets.bottom);
@@ -103,6 +101,8 @@
} else {
clearNavBarColor();
}
+
+ ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
requestLayout();
}
@@ -110,9 +110,6 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthUsed;
if (mInsets.bottom > 0) {
- // If we have bottom insets, we do not show the scrim as it would overlap
- // with the navbar scrim
- mGradientView.setVisibility(View.INVISIBLE);
widthUsed = 0;
} else {
Rect padding = mLauncher.getDeviceProfile().workspacePadding;
@@ -123,15 +120,14 @@
int heightUsed = mInsets.top + mLauncher.getDeviceProfile().edgeMarginPx;
measureChildWithMargins(mContent, widthMeasureSpec,
widthUsed, heightMeasureSpec, heightUsed);
- measureChild(mGradientView, widthMeasureSpec, heightMeasureSpec);
- setMeasuredDimension(mGradientView.getMeasuredWidth(), mGradientView.getMeasuredHeight());
+ 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;
- mGradientView.layout(0, 0, width, height);
// Content is laid out as center bottom aligned
int contentWidth = mContent.getMeasuredWidth();
@@ -176,13 +172,10 @@
mOpenCloseAnimator.removeListener(this);
}
});
- post(new Runnable() {
- @Override
- public void run() {
- mRecyclerView.setLayoutFrozen(true);
- mOpenCloseAnimator.start();
- mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
- }
+ post(() -> {
+ mRecyclerView.setLayoutFrozen(true);
+ mOpenCloseAnimator.start();
+ mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
});
} else {
setTranslationShift(TRANSLATION_SHIFT_OPENED);
@@ -205,7 +198,11 @@
// Disable swipe down when recycler view is scrolling
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = false;
- if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
+ RecyclerViewFastScroller scroller = mRecyclerView.getScrollbar();
+ if (scroller.getThumbOffsetY() >= 0 &&
+ mLauncher.getDragLayer().isEventOverView(scroller, ev)) {
+ mNoIntercept = true;
+ } else if (mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
mNoIntercept = !mRecyclerView.shouldContainerScroll(ev, mLauncher.getDragLayer());
}
}
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 89c88a4..124058e 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -17,8 +17,12 @@
package com.android.launcher3.widget;
import android.content.Context;
+import android.graphics.Point;
import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.OnItemTouchListener;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
@@ -27,13 +31,15 @@
/**
* The widgets recycler view.
*/
-public class WidgetsRecyclerView extends BaseRecyclerView {
+public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouchListener {
- private static final String TAG = "WidgetsRecyclerView";
private WidgetsListAdapter mAdapter;
private final int mScrollbarTop;
+ private final Point mFastScrollerOffset = new Point();
+ private boolean mTouchDownOnScroller;
+
public WidgetsRecyclerView(Context context) {
this(context, null);
}
@@ -46,6 +52,7 @@
// API 21 and below only support 3 parameter ctor.
super(context, attrs, defStyleAttr);
mScrollbarTop = getResources().getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+ addOnItemTouchListener(this);
}
public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
@@ -56,7 +63,6 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- addOnItemTouchListener(this);
// create a layout manager with Launcher's context so that scroll position
// can be preserved during screen rotation.
setLayoutManager(new LinearLayoutManager(getContext()));
@@ -145,4 +151,26 @@
public int getScrollBarTop() {
return mScrollbarTop;
}
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_DOWN) {
+ mTouchDownOnScroller =
+ mScrollbar.isHitInParent(e.getX(), e.getY(), mFastScrollerOffset);
+ }
+ if (mTouchDownOnScroller) {
+ return mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ }
+ return false;
+ }
+
+ @Override
+ public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+ if (mTouchDownOnScroller) {
+ mScrollbar.handleTouchEvent(e, mFastScrollerOffset);
+ }
+ }
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { }
}
\ No newline at end of file
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
index fd33ee1..49a9dc7 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
@@ -25,7 +25,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -33,7 +32,9 @@
*/
public class AllAppsState extends LauncherState {
- private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+ private static final float PARALLAX_COEFFICIENT = .125f;
+
+ private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY | FLAG_ALL_APPS_SCRIM;
private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
@Override
@@ -62,8 +63,8 @@
}
@Override
- public float getHoseatAlpha(Launcher launcher) {
- return 0;
+ public int getVisibleElements(Launcher launcher) {
+ return ALL_APPS_HEADER | ALL_APPS_CONTENT;
}
@Override
@@ -74,8 +75,7 @@
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
return new float[] { 1f, 0,
- -launcher.getAllAppsController().getShiftRange()
- * AllAppsTransitionController.PARALLAX_COEFFICIENT};
+ -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT};
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index 76b7e0d..c97c3cc 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -1,19 +1,3 @@
-/*
- * 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;
import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -21,31 +5,35 @@
import android.view.MotionEvent;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SwipeDetector;
-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.VerticalSwipeController;
/**
- * Extension of {@link VerticalSwipeController} to switch between NORMAL and ALL_APPS state.
+ * TouchController to switch between NORMAL and ALL_APPS state.
*/
-public class AllAppsSwipeController extends VerticalSwipeController {
-
- private int mStartContainerType;
+public class AllAppsSwipeController extends AbstractStateChangeTouchController {
public AllAppsSwipeController(Launcher l) {
- super(l, NORMAL);
+ super(l, SwipeDetector.VERTICAL);
}
@Override
- protected boolean shouldInterceptTouch(MotionEvent ev) {
+ 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(NORMAL) && !mLauncher.isInState(ALL_APPS)) {
// Don't listen for the swipe gesture if we are already in some other state.
return false;
}
-
if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) {
return false;
}
@@ -65,14 +53,19 @@
}
@Override
- protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
- if (stateChanged) {
- // Transition complete. log the action
- mLauncher.getUserEventDispatcher().logActionOnContainer(
- wasFling ? Touch.FLING : Touch.SWIPE,
- mLauncher.isInState(ALL_APPS) ? Direction.UP : Direction.DOWN,
- mStartContainerType,
- mLauncher.getWorkspace().getCurrentPage());
- }
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ return fromState == ALL_APPS ? NORMAL : ALL_APPS;
+ }
+
+ @Override
+ protected float initCurrentAnimation() {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(mToState, maxAccuracy);
+ float startVerticalShift = mFromState.getVerticalProgress(mLauncher) * range;
+ float endVerticalShift = mToState.getVerticalProgress(mLauncher) * range;
+ float totalShift = endVerticalShift - startVerticalShift;
+ return 1 / totalShift;
}
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java
new file mode 100644
index 0000000..147d194
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -0,0 +1,26 @@
+/*
+ * 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.uioverrides;
+
+/**
+ * A dummy overview state
+ */
+public class FastOverviewState extends OverviewState {
+
+ public FastOverviewState(int id) {
+ super(id);
+ }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java
deleted file mode 100644
index 88a1e10..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewAccessibilityDelegate.java
+++ /dev/null
@@ -1,74 +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.uioverrides;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-
-/**
- * Accessibility delegate with actions pointing to various Overview entry points.
- */
-public class OverviewAccessibilityDelegate extends AccessibilityDelegate {
-
- private static final int OVERVIEW = R.string.accessibility_action_overview;
- 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;
-
- @Override
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(host, info);
-
- Context context = host.getContext();
- info.addAction(new AccessibilityAction(OVERVIEW, context.getText(OVERVIEW)));
-
- if (Utilities.isWallpaperAllowed(context)) {
- info.addAction(new AccessibilityAction(WALLPAPERS, context.getText(WALLPAPERS)));
- }
- info.addAction(new AccessibilityAction(WIDGETS, context.getText(WIDGETS)));
- info.addAction(new AccessibilityAction(SETTINGS, context.getText(SETTINGS)));
- }
-
- @Override
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
- Launcher launcher = Launcher.getLauncher(host.getContext());
- OverviewPanel overviewPanel = launcher.findViewById(R.id.overview_panel);
- if (action == OVERVIEW) {
- launcher.getStateManager().goToState(LauncherState.OVERVIEW);
- return true;
- } else if (action == WALLPAPERS) {
- launcher.onClickWallpaperPicker(host);
- return true;
- } else if (action == WIDGETS) {
- overviewPanel.onClickAddWidgetButton();
- return true;
- } else if (action == SETTINGS) {
- overviewPanel.onClickSettingsButton(host);
- return true;
- }
- return super.performAccessibilityAction(host, action, args);
- }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
deleted file mode 100644
index 2d39505..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
+++ /dev/null
@@ -1,192 +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;
-
-import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.WorkspaceStateTransitionAnimation.AnimatedPropertySetter;
-import com.android.launcher3.WorkspaceStateTransitionAnimation.PropertySetter;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-import com.android.launcher3.widget.WidgetsFullSheet;
-
-public class OverviewPanel extends LinearLayout implements Insettable, View.OnClickListener,
- View.OnLongClickListener, LauncherStateManager.StateHandler {
-
- // Out of 100, the percent of space the overview bar should try and take vertically.
- private static final float OVERVIEW_ICON_ZONE_RATIO = 0.22f;
-
- private final Launcher mLauncher;
-
- public OverviewPanel(Context context) {
- this(context, null);
- }
-
- public OverviewPanel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public OverviewPanel(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
- setAlpha(0);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- int visibleChildCount = 3;
- // Attach buttons.
- attachListeners(findViewById(R.id.wallpaper_button));
- attachListeners(findViewById(R.id.widget_button));
-
- View settingsButton = findViewById(R.id.settings_button);
- if (mLauncher.hasSettings()) {
- attachListeners(settingsButton);
- } else {
- settingsButton.setVisibility(GONE);
- visibleChildCount--;
- }
-
- // Init UI
- Resources res = getResources();
- int itemWidthPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_item_width);
- int spacerWidthPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width);
-
- int totalItemWidth = visibleChildCount * itemWidthPx;
- int maxWidth = totalItemWidth + (visibleChildCount - 1) * spacerWidthPx;
-
- getLayoutParams().width = Math.min(mLauncher.getDeviceProfile().availableWidthPx, maxWidth);
- getLayoutParams().height = getButtonBarHeight(mLauncher);
- }
-
- private void attachListeners(View view) {
- view.setOnClickListener(this);
- view.setOnLongClickListener(this);
- }
-
- @Override
- public void setInsets(Rect insets) {
- ((FrameLayout.LayoutParams) getLayoutParams()).bottomMargin = insets.bottom;
- }
-
- @Override
- public void onClick(View view) {
- handleViewClick(view, Action.Touch.TAP);
- }
-
- @Override
- public boolean onLongClick(View view) {
- return handleViewClick(view, Action.Touch.LONGPRESS);
- }
-
- private boolean handleViewClick(View view, int action) {
- if (mLauncher.getWorkspace().isSwitchingState()) {
- return false;
- }
-
- final int controlType;
- if (view.getId() == R.id.wallpaper_button) {
- mLauncher.onClickWallpaperPicker(view);
- controlType = ControlType.WALLPAPER_BUTTON;
- } else if (view.getId() == R.id.widget_button) {
- onClickAddWidgetButton();
- controlType = ControlType.WIDGETS_BUTTON;
- } else if (view.getId() == R.id.settings_button) {
- onClickSettingsButton(view);
- controlType = ControlType.SETTINGS_BUTTON;
- } else {
- return false;
- }
-
- mLauncher.getUserEventDispatcher().logActionOnControl(action, controlType);
- return true;
- }
-
- /**
- * Event handler for the (Add) Widgets button that appears after a long press
- * on the home screen.
- */
- public void onClickAddWidgetButton() {
- if (getContext().getPackageManager().isSafeMode()) {
- Toast.makeText(mLauncher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- } else {
- WidgetsFullSheet.show(mLauncher, true /* animated */);
- }
- }
-
- /**
- * Event handler for a click on the settings button that appears after a long press
- * on the home screen.
- */
- public void onClickSettingsButton(View v) {
- Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
- .setPackage(getContext().getPackageName());
- intent.setSourceBounds(mLauncher.getViewBounds(v));
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent, mLauncher.getActivityLaunchOptions(v, false));
- }
-
- @Override
- public void setState(LauncherState state) {
- setState(state, NO_ANIM_PROPERTY_SETTER);
- }
-
- @Override
- public void setStateWithAnimation(LauncherState toState,
- AnimatorSetBuilder builder, AnimationConfig config) {
- setState(toState, new AnimatedPropertySetter(config.duration, builder));
- }
-
- private void setState(LauncherState state, PropertySetter setter) {
- setter.setViewAlpha(this, 1f - state.getHoseatAlpha(mLauncher), Interpolators.ACCEL);
- }
-
- public static int getButtonBarHeight(Launcher launcher) {
- int zoneHeight = (int) (OVERVIEW_ICON_ZONE_RATIO *
- launcher.getDeviceProfile().availableHeightPx);
- Resources res = launcher.getResources();
- int overviewModeMinIconZoneHeightPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
- int overviewModeMaxIconZoneHeightPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_max_icon_zone_height);
- return Utilities.boundToRange(zoneHeight,
- overviewModeMinIconZoneHeightPx,
- overviewModeMaxIconZoneHeightPx);
- }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
index d18901d..8def0d3 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,16 +16,8 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
-import android.graphics.Rect;
-import android.view.View;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.Workspace;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -33,56 +25,7 @@
*/
public class OverviewState extends LauncherState {
- // The percent to shrink the workspace during overview mode
- private static final float SCALE_FACTOR = 0.7f;
-
- private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE |
- FLAG_DISABLE_PAGE_CLIPPING;
-
public OverviewState(int id) {
- super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
- }
-
- @Override
- public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
- DeviceProfile grid = launcher.getDeviceProfile();
- Workspace ws = launcher.getWorkspace();
- Rect insets = launcher.getDragLayer().getInsets();
-
- int overviewButtonBarHeight = OverviewPanel.getButtonBarHeight(launcher);
- int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight());
- int workspaceTop = insets.top + grid.workspacePadding.top;
- int workspaceBottom = ws.getHeight() - insets.bottom - grid.workspacePadding.bottom;
- int overviewTop = insets.top;
- int overviewBottom = ws.getHeight() - insets.bottom - overviewButtonBarHeight;
- int workspaceOffsetTopEdge =
- workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
- int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
- return new float[] {SCALE_FACTOR, 0, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
- }
-
- @Override
- public float getHoseatAlpha(Launcher launcher) {
- return 0;
- }
-
- @Override
- public void onStateEnabled(Launcher launcher) {
- launcher.getWorkspace().setPageRearrangeEnabled(true);
-
- if (isAccessibilityEnabled(launcher)) {
- launcher.getOverviewPanel().getChildAt(0).performAccessibilityAction(
- AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
- }
-
- @Override
- public void onStateDisabled(Launcher launcher) {
- launcher.getWorkspace().setPageRearrangeEnabled(false);
- }
-
- @Override
- public View getFinalFocus(Launcher launcher) {
- return launcher.getOverviewPanel();
+ super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, FLAG_DISABLE_RESTORE);
}
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java b/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java
deleted file mode 100644
index a7c8cee..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java
+++ /dev/null
@@ -1,164 +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.uioverrides;
-
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.ScaleGestureDetector.OnScaleGestureListener;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.util.TouchController;
-
-/**
- * Detects pinches and animates the Workspace to/from overview mode.
- */
-public class PinchToOverviewListener extends AnimatorListenerAdapter
- implements TouchController, OnScaleGestureListener {
-
- private static final float ACCEPT_THRESHOLD = 0.65f;
- /**
- * The velocity threshold at which a pinch will be completed instead of canceled,
- * even if the first threshold has not been passed. Measured in scale / millisecond
- */
- private static final float FLING_VELOCITY = 0.001f;
-
- private final ScaleGestureDetector mPinchDetector;
- private Launcher mLauncher;
- private Workspace mWorkspace = null;
- private boolean mPinchStarted = false;
-
- private AnimatorPlaybackController mCurrentAnimation;
- private float mCurrentScale;
- private boolean mShouldGoToFinalState;
-
- private LauncherState mToState;
-
- public PinchToOverviewListener(Launcher launcher) {
- mLauncher = launcher;
- mPinchDetector = new ScaleGestureDetector(mLauncher, this);
- }
-
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- mPinchDetector.onTouchEvent(ev);
- return mPinchStarted;
- }
-
- public boolean onControllerTouchEvent(MotionEvent ev) {
- return mPinchDetector.onTouchEvent(ev);
- }
-
- @Override
- public boolean onScaleBegin(ScaleGestureDetector detector) {
- if (isAccessibilityEnabled(mLauncher)) {
- return false;
- }
- if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(OVERVIEW)) {
- // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc.
- return false;
- }
- if (mCurrentAnimation != null) {
- // Don't listen for the pinch gesture if we are already animating from a previous one.
- return false;
- }
- if (mLauncher.isWorkspaceLocked()) {
- // Don't listen for the pinch gesture if the workspace isn't ready.
- return false;
- }
- if (mWorkspace == null) {
- mWorkspace = mLauncher.getWorkspace();
- }
- if (mWorkspace.isSwitchingState()) {
- // Don't listen for the pinch gesture while switching state, as it will cause a jump
- // once the state switching animation is complete.
- return false;
- }
- if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
- // Don't listen for the pinch gesture if a floating view is open.
- return false;
- }
-
- if (mLauncher.getDragController().isDragging()) {
- mLauncher.getDragController().cancelDrag();
- }
-
- mToState = mLauncher.isInState(OVERVIEW) ? NORMAL : OVERVIEW;
- mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, OVERVIEW_TRANSITION_MS);
- mCurrentAnimation.getTarget().addListener(this);
- mPinchStarted = true;
- mCurrentScale = 1;
- mShouldGoToFinalState = false;
-
- mCurrentAnimation.dispatchOnStart();
- return true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- mCurrentAnimation = null;
- mPinchStarted = false;
- }
-
- @Override
- public void onScaleEnd(ScaleGestureDetector detector) {
- if (mShouldGoToFinalState) {
- mCurrentAnimation.start();
- } else {
- mCurrentAnimation.setEndAction(new Runnable() {
- @Override
- public void run() {
- mLauncher.getStateManager().goToState(
- mToState == OVERVIEW ? NORMAL : OVERVIEW, false);
- }
- });
- mCurrentAnimation.reverse();
- }
- }
-
- @Override
- public boolean onScale(ScaleGestureDetector detector) {
- mCurrentScale = detector.getScaleFactor() * mCurrentScale;
-
- // If we are zooming out, inverse the mCurrentScale so that animationFraction = [0, 1]
- // 0 => Animation complete
- // 1=> Animation started
- float animationFraction = mToState == OVERVIEW ? mCurrentScale : (1 / mCurrentScale);
-
- float velocity = (1 - detector.getScaleFactor()) / detector.getTimeDelta();
- if (Math.abs(velocity) >= FLING_VELOCITY) {
- LauncherState toState = velocity > 0 ? OVERVIEW : NORMAL;
- mShouldGoToFinalState = toState == mToState;
- } else {
- mShouldGoToFinalState = animationFraction <= ACCEPT_THRESHOLD;
- }
-
- // Move the transition animation to that duration.
- mCurrentAnimation.setPlayFraction(1 - animationFraction);
- return true;
- }
-}
\ No newline at end of file
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 744125e..be9d5b7 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,49 +16,29 @@
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.PointF;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.AccessibilityDelegate;
-
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
public class UiFactory {
- public static final boolean USE_HARDWARE_BITMAP = false;
-
public static TouchController[] createTouchControllers(Launcher launcher) {
return new TouchController[] {
- new AllAppsSwipeController(launcher), new PinchToOverviewListener(launcher)};
- }
-
- public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
- return new OverviewAccessibilityDelegate();
+ launcher.getDragController(), new AllAppsSwipeController(launcher)};
}
public static StateHandler[] getStateHandler(Launcher launcher) {
return new StateHandler[] {
- (OverviewPanel) launcher.getOverviewPanel(),
launcher.getAllAppsController(), launcher.getWorkspace() };
}
- public static void onWorkspaceLongPress(Launcher launcher, PointF touchPoint) {
- launcher.getStateManager().goToState(OVERVIEW);
- }
-
- public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
- BitmapRenderer renderer) {
- Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- renderer.render(new Canvas(result));
- return result;
- }
-
public static void resetOverview(Launcher launcher) { }
+
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
+
+ public static void onStart(Launcher launcher) { }
+
+ public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
+
+ public static void onTrimMemory(Launcher launcher, int level) { }
}
diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml
index 0a29147..a54268a 100644
--- a/tests/AndroidManifest-common.xml
+++ b/tests/AndroidManifest-common.xml
@@ -33,6 +33,17 @@
android:resource="@xml/appwidget_no_config" />
</receiver>
+
+ <receiver
+ android:name="com.android.launcher3.testcomponent.AppWdigetHidden"
+ android:label="Hidden widget">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/appwidget_hidden" />
+ </receiver>
+
<receiver
android:name="com.android.launcher3.testcomponent.AppWidgetWithConfig"
android:label="With Config">
diff --git a/tests/res/xml/appwidget_hidden.xml b/tests/res/xml/appwidget_hidden.xml
new file mode 100644
index 0000000..6f0e006
--- /dev/null
+++ b/tests/res/xml/appwidget_hidden.xml
@@ -0,0 +1,11 @@
+<?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:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/test_layout_appwidget_blue"
+ android:resizeMode="horizontal|vertical"
+ android:widgetFeatures="hide_from_picker"
+ android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/res/xml/appwidget_with_config.xml b/tests/res/xml/appwidget_with_config.xml
index 3e96c6f..8403689 100644
--- a/tests/res/xml/appwidget_with_config.xml
+++ b/tests/res/xml/appwidget_with_config.xml
@@ -7,5 +7,6 @@
android:initialLayout="@layout/test_layout_appwidget_blue"
android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"
android:resizeMode="horizontal|vertical"
+ android:widgetFeatures="reconfigurable"
android:widgetCategory="home_screen">
</appwidget-provider>
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index cf90afd..b217847 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -1,5 +1,11 @@
package com.android.launcher3.model;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -24,8 +30,8 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherProvider;
import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.util.ComponentKey;
@@ -43,12 +49,6 @@
import java.util.List;
import java.util.concurrent.Executor;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
/**
* Base class for writing tests for Model update tasks.
*/
@@ -82,7 +82,7 @@
modelWriter = mock(ModelWriter.class);
when(appState.getModel()).thenReturn(model);
- when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
+ when(model.getWriter(anyBoolean(), anyBoolean())).thenReturn(modelWriter);
when(model.getCallback()).thenReturn(callbacks);
myUser = Process.myUserHandle();
diff --git a/tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java b/tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
new file mode 100644
index 0000000..83492bf
--- /dev/null
+++ b/tests/src/com/android/launcher3/testcomponent/AppWidgetHidden.java
@@ -0,0 +1,23 @@
+/*
+ * 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.testcomponent;
+
+import android.appwidget.AppWidgetProvider;
+
+/**
+ * A simple app widget without any configuration screen and is hidden in picker.
+ */
+public class AppWidgetHidden extends AppWidgetProvider { }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 1be33d2..011aa22 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -40,13 +40,11 @@
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.testcomponent.AppWidgetNoConfig;
import com.android.launcher3.testcomponent.AppWidgetWithConfig;
-import com.android.launcher3.util.ManagedProfileHeuristic;
import org.junit.Before;
@@ -67,6 +65,7 @@
public static final long DEFAULT_BROADCAST_TIMEOUT_SECS = 5;
public static final long DEFAULT_UI_TIMEOUT = 3000;
+ public static final long LARGE_UI_TIMEOUT = 10000;
public static final long DEFAULT_WORKER_TIMEOUT_SECS = 5;
protected MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -82,11 +81,6 @@
}
protected void lockRotation(boolean naturalOrientation) throws RemoteException {
- Utilities.getPrefs(mTargetContext)
- .edit()
- .putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
- .commit();
-
if (naturalOrientation) {
mDevice.setOrientationNatural();
} else {
@@ -104,8 +98,13 @@
protected UiObject2 openAllApps() {
mDevice.waitForIdle();
if (FeatureFlags.NO_ALL_APPS_ICON) {
- // clicking on the page indicator brings up all apps tray on non tablets.
- findViewById(R.id.page_indicator).click();
+ UiObject2 hotseat = mDevice.wait(
+ Until.findObject(getSelectorForId(R.id.hotseat)), 2500);
+ Point start = hotseat.getVisibleCenter();
+ int endY = (int) (mDevice.getDisplayHeight() * 0.1f);
+ // 100 px/step
+ mDevice.swipe(start.x, start.y, start.x, endY, (start.y - endY) / 100);
+
} else {
mDevice.wait(Until.findObject(
By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
@@ -221,7 +220,6 @@
mMainThreadExecutor.execute(new Runnable() {
@Override
public void run() {
- ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
LauncherAppState.getInstance(mTargetContext).getModel().forceReload();
}
});
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
new file mode 100644
index 0000000..ccee7da
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 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.ui;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.util.rule.LauncherActivityRule;
+import com.android.launcher3.util.rule.ShellCommandRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class WorkTabTest extends AbstractLauncherUiTest {
+ @Rule
+ public LauncherActivityRule mActivityMonitor = new LauncherActivityRule();
+ @Rule
+ public ShellCommandRule mDefaultLauncherRule = ShellCommandRule.setDefaultLauncher();
+
+ private int mProfileUserId;
+
+ @Before
+ public void createWorkProfile() throws Exception {
+ String output =
+ mDevice.executeShellCommand(
+ "pm create-user --profileOf 0 --managed TestProfile");
+ assertTrue("Failed to create work profile", output.startsWith("Success"));
+
+ String[] tokens = output.split("\\s+");
+ mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
+
+ mDevice.executeShellCommand("am start-user " + mProfileUserId);
+ }
+
+ @After
+ public void removeWorkProfile() throws Exception {
+ mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
+ }
+
+ @Test
+ public void workTabExists() {
+ mActivityMonitor.startLauncher();
+
+ // Open all apps and wait for load complete
+ final UiObject2 appsContainer = openAllApps();
+ assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+ assertTrue("Personal tab is missing",
+ mDevice.wait(Until.hasObject(getSelectorForId(R.id.tab_personal)),
+ LARGE_UI_TIMEOUT));
+ assertTrue("Work tab is missing",
+ mDevice.wait(Until.hasObject(getSelectorForId(R.id.tab_work)), LARGE_UI_TIMEOUT));
+ }
+}
\ No newline at end of file