Merge "Update widget full sheet / bottom sheet to have top rounded corners." into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 256d95d..a1cba4a2 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/protos/launcher_log.proto b/protos/launcher_log.proto
index de74fce..3b983d2 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -68,6 +68,7 @@
SEARCHBOX = 6;
EDITTEXT = 7;
NOTIFICATION = 8;
+ TASK = 9; // Each page of Recents UI (QuickStep)
}
// Used to define what type of container a Target would represent.
@@ -78,11 +79,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 +104,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 +146,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 +156,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/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 74d3cba..d5859a7 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
new file mode 100644
index 0000000..b4c735b
--- /dev/null
+++ b/quickstep/res/values-af/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
new file mode 100644
index 0000000..62d54db
--- /dev/null
+++ b/quickstep/res/values-am/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
new file mode 100644
index 0000000..de210b7
--- /dev/null
+++ b/quickstep/res/values-ar/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
new file mode 100644
index 0000000..91c5179
--- /dev/null
+++ b/quickstep/res/values-az/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..a4eb3e8
--- /dev/null
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
new file mode 100644
index 0000000..f798802
--- /dev/null
+++ b/quickstep/res/values-be/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
new file mode 100644
index 0000000..de88634
--- /dev/null
+++ b/quickstep/res/values-bg/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
new file mode 100644
index 0000000..028a475
--- /dev/null
+++ b/quickstep/res/values-bn/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
new file mode 100644
index 0000000..16b72ea
--- /dev/null
+++ b/quickstep/res/values-bs/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
new file mode 100644
index 0000000..6d65fd7
--- /dev/null
+++ b/quickstep/res/values-ca/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
new file mode 100644
index 0000000..b6894c0
--- /dev/null
+++ b/quickstep/res/values-cs/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
new file mode 100644
index 0000000..ff2d25d
--- /dev/null
+++ b/quickstep/res/values-da/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
new file mode 100644
index 0000000..e5242c7
--- /dev/null
+++ b/quickstep/res/values-de/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
new file mode 100644
index 0000000..9d7023d
--- /dev/null
+++ b/quickstep/res/values-el/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..694e73a
--- /dev/null
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..694e73a
--- /dev/null
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..694e73a
--- /dev/null
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..37a3168
--- /dev/null
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
new file mode 100644
index 0000000..60c87d3
--- /dev/null
+++ b/quickstep/res/values-es/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
new file mode 100644
index 0000000..33d1cba
--- /dev/null
+++ b/quickstep/res/values-et/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
new file mode 100644
index 0000000..d0e86c2
--- /dev/null
+++ b/quickstep/res/values-eu/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
new file mode 100644
index 0000000..198f33d
--- /dev/null
+++ b/quickstep/res/values-fa/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
new file mode 100644
index 0000000..f244586
--- /dev/null
+++ b/quickstep/res/values-fi/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..d9b2a51
--- /dev/null
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
new file mode 100644
index 0000000..8c1a5d2
--- /dev/null
+++ b/quickstep/res/values-fr/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
new file mode 100644
index 0000000..d6f26ac
--- /dev/null
+++ b/quickstep/res/values-gl/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
new file mode 100644
index 0000000..8c779d6
--- /dev/null
+++ b/quickstep/res/values-gu/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
new file mode 100644
index 0000000..65507a9
--- /dev/null
+++ b/quickstep/res/values-hi/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
new file mode 100644
index 0000000..eaa4416
--- /dev/null
+++ b/quickstep/res/values-hr/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
new file mode 100644
index 0000000..b72c760
--- /dev/null
+++ b/quickstep/res/values-hu/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
new file mode 100644
index 0000000..632ea3e
--- /dev/null
+++ b/quickstep/res/values-hy/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
new file mode 100644
index 0000000..d731bce
--- /dev/null
+++ b/quickstep/res/values-in/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
new file mode 100644
index 0000000..d4d54d3
--- /dev/null
+++ b/quickstep/res/values-is/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
new file mode 100644
index 0000000..6223ddb
--- /dev/null
+++ b/quickstep/res/values-it/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
new file mode 100644
index 0000000..1da258a
--- /dev/null
+++ b/quickstep/res/values-iw/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
new file mode 100644
index 0000000..bee863f
--- /dev/null
+++ b/quickstep/res/values-ja/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
new file mode 100644
index 0000000..4488cf8
--- /dev/null
+++ b/quickstep/res/values-ka/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
new file mode 100644
index 0000000..8c82ef1
--- /dev/null
+++ b/quickstep/res/values-kk/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
new file mode 100644
index 0000000..64136c9
--- /dev/null
+++ b/quickstep/res/values-km/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
new file mode 100644
index 0000000..7f12dee
--- /dev/null
+++ b/quickstep/res/values-kn/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
new file mode 100644
index 0000000..39fdf33
--- /dev/null
+++ b/quickstep/res/values-ko/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
new file mode 100644
index 0000000..2602b51
--- /dev/null
+++ b/quickstep/res/values-ky/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
new file mode 100644
index 0000000..7ba29d2
--- /dev/null
+++ b/quickstep/res/values-lo/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
new file mode 100644
index 0000000..2252381
--- /dev/null
+++ b/quickstep/res/values-lt/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
new file mode 100644
index 0000000..d5f5e9c
--- /dev/null
+++ b/quickstep/res/values-lv/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
new file mode 100644
index 0000000..6458aa0
--- /dev/null
+++ b/quickstep/res/values-mk/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
new file mode 100644
index 0000000..4d2745a
--- /dev/null
+++ b/quickstep/res/values-ml/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
new file mode 100644
index 0000000..4aec257
--- /dev/null
+++ b/quickstep/res/values-mn/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
new file mode 100644
index 0000000..3f85d77
--- /dev/null
+++ b/quickstep/res/values-mr/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
new file mode 100644
index 0000000..80e942c
--- /dev/null
+++ b/quickstep/res/values-ms/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
new file mode 100644
index 0000000..b6b0300
--- /dev/null
+++ b/quickstep/res/values-my/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
new file mode 100644
index 0000000..4dc144c
--- /dev/null
+++ b/quickstep/res/values-nb/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
new file mode 100644
index 0000000..a171b73
--- /dev/null
+++ b/quickstep/res/values-ne/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
new file mode 100644
index 0000000..4acddc5
--- /dev/null
+++ b/quickstep/res/values-nl/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
new file mode 100644
index 0000000..6bc1876
--- /dev/null
+++ b/quickstep/res/values-pa/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
new file mode 100644
index 0000000..174a96a
--- /dev/null
+++ b/quickstep/res/values-pl/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..c3df4f2
--- /dev/null
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
new file mode 100644
index 0000000..9730b98
--- /dev/null
+++ b/quickstep/res/values-pt/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
new file mode 100644
index 0000000..46671e8
--- /dev/null
+++ b/quickstep/res/values-ro/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
new file mode 100644
index 0000000..c2edf75
--- /dev/null
+++ b/quickstep/res/values-ru/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
new file mode 100644
index 0000000..7702543
--- /dev/null
+++ b/quickstep/res/values-si/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
new file mode 100644
index 0000000..84e7793
--- /dev/null
+++ b/quickstep/res/values-sk/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
new file mode 100644
index 0000000..c01fcd4
--- /dev/null
+++ b/quickstep/res/values-sl/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
new file mode 100644
index 0000000..ae6e6bc
--- /dev/null
+++ b/quickstep/res/values-sq/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
new file mode 100644
index 0000000..84debfd
--- /dev/null
+++ b/quickstep/res/values-sr/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
new file mode 100644
index 0000000..495bca7
--- /dev/null
+++ b/quickstep/res/values-sv/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
new file mode 100644
index 0000000..63594f2
--- /dev/null
+++ b/quickstep/res/values-sw/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
new file mode 100644
index 0000000..2d740ca
--- /dev/null
+++ b/quickstep/res/values-ta/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
new file mode 100644
index 0000000..36e5e3e
--- /dev/null
+++ b/quickstep/res/values-te/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
new file mode 100644
index 0000000..882b096
--- /dev/null
+++ b/quickstep/res/values-th/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
new file mode 100644
index 0000000..dbd03b1
--- /dev/null
+++ b/quickstep/res/values-tl/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
new file mode 100644
index 0000000..57cb349
--- /dev/null
+++ b/quickstep/res/values-tr/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
new file mode 100644
index 0000000..8c2c6f6
--- /dev/null
+++ b/quickstep/res/values-uk/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
new file mode 100644
index 0000000..ab5b38f
--- /dev/null
+++ b/quickstep/res/values-ur/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
new file mode 100644
index 0000000..280bd88
--- /dev/null
+++ b/quickstep/res/values-uz/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
new file mode 100644
index 0000000..2aca3b8
--- /dev/null
+++ b/quickstep/res/values-vi/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..165f141
--- /dev/null
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..6ba0244
--- /dev/null
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</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..9834038
--- /dev/null
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
new file mode 100644
index 0000000..a4ba48c
--- /dev/null
+++ b/quickstep/res/values-zu/strings.xml
@@ -0,0 +1,25 @@
+<?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>
+</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index d8504f1..0956048 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -37,4 +37,7 @@
<!-- Launcher app transition -->
<dimen name="content_trans_y">25dp</dimen>
<dimen name="workspace_trans_y">80dp</dimen>
+ <dimen name="recents_adjacent_trans_x">120dp</dimen>
+ <dimen name="recents_adjacent_trans_y">80dp</dimen>
+ <fraction name="recents_adjacent_scale">150%</fraction>
</resources>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 2f0cd78..0fe29e3 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.DRAWABLE_ALPHA;
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,13 +30,13 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
+import android.app.ActivityOptions;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Build;
-import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Surface;
@@ -45,6 +48,7 @@
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.quickstep.RecentsAnimationInterpolator;
@@ -79,8 +83,8 @@
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;
+ private static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
+ private static final float ALL_APPS_PROGRESS_OVERSHOOT = 0.99581414f;
private final DragLayer mDragLayer;
private final Launcher mLauncher;
@@ -88,6 +92,9 @@
private final float mContentTransY;
private final float mWorkspaceTransY;
+ private final float mRecentsTransX;
+ private final float mRecentsTransY;
+ private final float mRecentsScale;
private View mFloatingView;
private boolean mIsRtl;
@@ -104,6 +111,9 @@
Resources res = mLauncher.getResources();
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
+ mRecentsTransX = res.getDimensionPixelSize(R.dimen.recents_adjacent_trans_x);
+ mRecentsTransY = res.getDimensionPixelSize(R.dimen.recents_adjacent_trans_y);
+ mRecentsScale = res.getFraction(R.fraction.recents_adjacent_scale, 1, 1);
mLauncher.addOnDeviceProfileChangeListener(this);
registerRemoteAnimations();
@@ -115,7 +125,7 @@
}
private void setCurrentAnimator(LauncherTransitionAnimator animator) {
- if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ if (isAnimating()) {
mCurrentAnimator.cancel();
}
mCurrentAnimator = animator;
@@ -123,18 +133,23 @@
@Override
public void finishLauncherAnimation() {
- if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ if (isAnimating()) {
mCurrentAnimator.finishLauncherAnimation();
}
mCurrentAnimator = null;
}
+ @Override
+ public boolean isAnimating() {
+ return mCurrentAnimator != null && mCurrentAnimator.isRunning();
+ }
+
/**
- * @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) {
@@ -187,7 +202,7 @@
};
return ActivityOptionsCompat.makeRemoteAnimation(
- new RemoteAnimationAdapterCompat(runner, 500, 380)).toBundle();
+ new RemoteAnimationAdapterCompat(runner, 500, 380));
} catch (NoClassDefFoundError e) {
// Gracefully fall back to default launch options if the user's platform doesn't
// have the latest changes.
@@ -229,7 +244,93 @@
}
// Found a visible recents task that matches the opening app, lets launch the app from there
- return new LauncherTransitionAnimator(null, getRecentsWindowAnimator(taskView, targets));
+ return new LauncherTransitionAnimator(getRecentsLauncherAnimator(recentsView, taskView),
+ getRecentsWindowAnimator(taskView, targets));
+ }
+
+ /**
+ * Animate adjacent tasks off screen while scaling up, and translate hotseat off screen as well.
+ *
+ * If launching one of the adjacent tasks, parallax the center task and other adjacent task
+ * to the right.
+ */
+ private Animator getRecentsLauncherAnimator(RecentsView recentsView, TaskView v) {
+ AnimatorSet launcherAnimator = new AnimatorSet();
+
+ int launchedTaskIndex = recentsView.indexOfChild(v);
+ int centerTaskIndex = recentsView.getCurrentPage();
+ boolean launchingCenterTask = launchedTaskIndex == centerTaskIndex;
+ if (launchingCenterTask) {
+ if (launchedTaskIndex - 1 >= recentsView.getFirstTaskIndex()) {
+ TaskView adjacentPage1 = (TaskView) recentsView.getPageAt(launchedTaskIndex - 1);
+ ObjectAnimator adjacentTask1ScaleAndTranslate =
+ LauncherAnimUtils.ofPropertyValuesHolder(adjacentPage1,
+ new PropertyListBuilder()
+ .scale(adjacentPage1.getScaleX() * mRecentsScale)
+ .translationY(mRecentsTransY)
+ .translationX(mIsRtl ? mRecentsTransX : -mRecentsTransX)
+ .build());
+ launcherAnimator.play(adjacentTask1ScaleAndTranslate);
+ }
+ if (launchedTaskIndex + 1 < recentsView.getPageCount()) {
+ TaskView adjacentTask2 = (TaskView) recentsView.getPageAt(launchedTaskIndex + 1);
+ ObjectAnimator adjacentTask2ScaleAndTranslate =
+ LauncherAnimUtils.ofPropertyValuesHolder(adjacentTask2,
+ new PropertyListBuilder()
+ .scale(adjacentTask2.getScaleX() * mRecentsScale)
+ .translationY(mRecentsTransY)
+ .translationX(mIsRtl ? -mRecentsTransX : mRecentsTransX)
+ .build());
+ launcherAnimator.play(adjacentTask2ScaleAndTranslate);
+ }
+ } else if (centerTaskIndex >= recentsView.getFirstTaskIndex()) {
+ // We are launching an adjacent task, so parallax the center and other adjacent task.
+ TaskView centerTask = (TaskView) recentsView.getPageAt(centerTaskIndex);
+ float translationX = Math.abs(v.getTranslationX());
+ ObjectAnimator centerTaskParallaxToRight =
+ LauncherAnimUtils.ofPropertyValuesHolder(centerTask,
+ new PropertyListBuilder()
+ .scale(v.getScaleX())
+ .translationX(mIsRtl ? -translationX : translationX)
+ .build());
+ launcherAnimator.play(centerTaskParallaxToRight);
+ int otherAdjacentTaskIndex = centerTaskIndex + (centerTaskIndex - launchedTaskIndex);
+ if (otherAdjacentTaskIndex >= recentsView.getFirstTaskIndex()
+ && otherAdjacentTaskIndex < recentsView.getPageCount()) {
+ TaskView otherAdjacentTask = (TaskView) recentsView.getPageAt(
+ otherAdjacentTaskIndex);
+ ObjectAnimator otherAdjacentTaskParallaxToRight =
+ LauncherAnimUtils.ofPropertyValuesHolder(otherAdjacentTask,
+ new PropertyListBuilder()
+ .translationX(otherAdjacentTask.getTranslationX()
+ + (mIsRtl ? -translationX : translationX))
+ .build());
+ launcherAnimator.play(otherAdjacentTaskParallaxToRight);
+ }
+ }
+
+ Animator allAppsSlideOut = ObjectAnimator.ofFloat(mLauncher.getAllAppsController(),
+ ALL_APPS_PROGRESS, ALL_APPS_PROGRESS_OFF_SCREEN);
+ launcherAnimator.play(allAppsSlideOut);
+
+ Workspace workspace = mLauncher.getWorkspace();
+ float[] workspaceScaleAndTranslation = LauncherState.NORMAL
+ .getWorkspaceScaleAndTranslation(mLauncher);
+ Animator recenterWorkspace = LauncherAnimUtils.ofPropertyValuesHolder(
+ workspace, new PropertyListBuilder()
+ .translationX(workspaceScaleAndTranslation[1])
+ .translationY(workspaceScaleAndTranslation[2])
+ .build());
+ launcherAnimator.play(recenterWorkspace);
+ CellLayout currentWorkspacePage = (CellLayout) workspace.getPageAt(
+ workspace.getCurrentPage());
+ Animator hideWorkspaceScrim = ObjectAnimator.ofInt(
+ currentWorkspacePage.getScrimBackground(), DRAWABLE_ALPHA, 0);
+ launcherAnimator.play(hideWorkspaceScrim);
+
+ launcherAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ launcherAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+ return launcherAnimator;
}
/**
@@ -246,7 +347,10 @@
RecentsAnimationInterpolator recentsInterpolator = new RecentsAnimationInterpolator(
new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx),
v.getThumbnail().getInsets(),
- taskViewBounds, new Rect(0, v.getThumbnail().getTop(), 0, 0));
+ taskViewBounds,
+ new Rect(0, v.getThumbnail().getTop(), 0, 0),
+ v.getScaleX(),
+ v.getTranslationX());
Rect crop = new Rect();
Matrix matrix = new Matrix();
@@ -309,6 +413,13 @@
isFirstFrame = false;
}
});
+ appAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Make sure recents gets fixed up by resetting task alphas and scales, etc.
+ mLauncher.getStateManager().reapplyState();
+ }
+ });
return appAnimator;
}
@@ -317,17 +428,19 @@
*/
private LauncherTransitionAnimator composeAppLaunchAnimator(View v,
RemoteAnimationTargetCompat[] targets) {
- return new LauncherTransitionAnimator(getLauncherAnimators(v),
+ return new LauncherTransitionAnimator(getLauncherAnimators(v, targets),
getWindowAnimators(v, targets));
}
/**
* @return Animators that control the movements of the Launcher and icon of the opening target.
*/
- private AnimatorSet getLauncherAnimators(View v) {
+ private AnimatorSet getLauncherAnimators(View v, RemoteAnimationTargetCompat[] targets) {
AnimatorSet launcherAnimators = new AnimatorSet();
- launcherAnimators.play(getLauncherContentAnimator(false /* show */));
launcherAnimators.play(getIconAnimator(v));
+ if (launcherIsATargetWithMode(targets, MODE_CLOSING)) {
+ launcherAnimators.play(getLauncherContentAnimator(false /* show */));
+ }
return launcherAnimators;
}
@@ -388,8 +501,10 @@
mFloatingView = new View(mLauncher);
if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
// Create a copy of the app icon
- mFloatingView.setBackground(
- DrawableFactory.get(mLauncher).newIcon((ItemInfoWithIcon) v.getTag()));
+ ItemInfoWithIcon info = (ItemInfoWithIcon) v.getTag();
+ FastBitmapDrawable d = DrawableFactory.get(mLauncher).newIcon(info);
+ d.setIsDisabled(info.isDisabled());
+ mFloatingView.setBackground(d);
}
// Position the floating view exactly on top of the original
@@ -431,6 +546,8 @@
boolean isBelowCenterY = lp.topMargin < centerY;
x.setDuration(isBelowCenterY ? 500 : 233);
y.setDuration(isBelowCenterY ? 233 : 500);
+ x.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ y.setInterpolator(Interpolators.AGGRESSIVE_EASE);
appIconAnimatorSet.play(x);
appIconAnimatorSet.play(y);
@@ -443,18 +560,18 @@
ObjectAnimator sY = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_Y, 1f, scale);
sX.setDuration(500);
sY.setDuration(500);
+ sX.setInterpolator(Interpolators.EXAGGERATED_EASE);
+ sY.setInterpolator(Interpolators.EXAGGERATED_EASE);
appIconAnimatorSet.play(sX);
appIconAnimatorSet.play(sY);
// 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);
- }
return appIconAnimatorSet;
}
@@ -515,9 +632,9 @@
// Fade in the app window.
float alphaDelay = 0;
- float alphaDuration = 50;
+ float alphaDuration = 60;
float alpha = getValue(0f, 1f, alphaDelay, alphaDuration,
- appAnimator.getDuration() * percent, Interpolators.AGGRESSIVE_EASE);
+ appAnimator.getDuration() * percent, Interpolators.LINEAR);
// Animate the window crop so that it starts off as a square, and then reveals
// horizontally.
@@ -530,7 +647,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
@@ -573,6 +690,16 @@
}
}
+ 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.
@@ -584,9 +711,13 @@
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.
+ if ((Utilities.getPrefs(mLauncher)
+ .getBoolean("pref_use_screenshot_for_swipe_up", false)
+ && mLauncher.isInState(LauncherState.OVERVIEW))
+ || !launcherIsATargetWithMode(targets, MODE_OPENING)) {
+ // We use a separate transition for Overview mode. And we can skip the
+ // animation in cases where Launcher is not in the set of opening targets.
+ // This can happen when Launcher is already visible. ie. Closing a dialog.
setCurrentAnimator(null);
finishedCallback.run();
return;
@@ -691,9 +822,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);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
index e39430d..541c6bb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -24,16 +24,55 @@
import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
import android.graphics.Rect;
+import android.metrics.LogMaker;
import android.view.MotionEvent;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.VerticalSwipeController;
import com.android.quickstep.RecentsView;
+class EventLogTags {
+ private EventLogTags() {
+ } // don't instantiate
+
+ /** 524292 sysui_multi_action (content|4) */
+ public static final int SYSUI_MULTI_ACTION = 524292;
+
+ public static void writeSysuiMultiAction(Object[] content) {
+ android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content);
+ }
+}
+
+class MetricsLogger {
+ private static MetricsLogger sMetricsLogger;
+
+ private static MetricsLogger getLogger() {
+ if (sMetricsLogger == null) {
+ sMetricsLogger = new MetricsLogger();
+ }
+ return sMetricsLogger;
+ }
+
+ protected void saveLog(Object[] rep) {
+ EventLogTags.writeSysuiMultiAction(rep);
+ }
+
+ public void write(LogMaker content) {
+ if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) {
+ content.setType(4/*MetricsEvent.TYPE_ACTION*/);
+ }
+ saveLog(content.serialize());
+ }
+}
+
/**
* Extension of {@link VerticalSwipeController} to go from NORMAL to OVERVIEW.
*/
@@ -42,6 +81,8 @@
private static final Rect sTempRect = new Rect();
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
+
public EdgeSwipeController(Launcher l) {
super(l, NORMAL, OVERVIEW, l.getDeviceProfile().isVerticalBarLayout()
? HORIZONTAL : VERTICAL);
@@ -63,6 +104,10 @@
return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE;
}
+ public EdgeSwipeController(Launcher l, LauncherState baseState) {
+ super(l, baseState);
+ }
+
@Override
protected boolean isTransitionFlipped() {
return mLauncher.getDeviceProfile().isSeascape();
@@ -70,7 +115,31 @@
@Override
protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
- // TODO: Log something
+ if (stateChanged && mToState instanceof OverviewState) {
+ // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for
+ // "Recents" activity for app transition tests.
+ 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*/,
+ 0/* zero time */);
+ mMetricsLogger.write(builder);
+
+ // Add user event logging for launcher pipeline
+ int direction = Direction.UP;
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ direction = Direction.LEFT;
+ if (mLauncher.getDeviceProfile().isSeascape()) {
+ direction = Direction.RIGHT;
+ }
+ }
+ mLauncher.getUserEventDispatcher().logStateChangeAction(
+ wasFling ? Touch.FLING : Touch.SWIPE, direction,
+ ContainerType.NAVBAR, ContainerType.WORKSPACE, // src target
+ ContainerType.TASKSWITCHER, // dst target
+ mLauncher.getWorkspace().getCurrentPage());
+ }
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 5706d32..9ba2308 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -40,7 +40,7 @@
| FLAG_DISABLE_RESTORE;
public OverviewState(int id) {
- super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+ super(id, ContainerType.TASKSWITCHER, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
index 1fd541a..468a561 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -31,6 +31,10 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+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.quickstep.RecentsView;
import com.android.quickstep.TaskView;
@@ -70,6 +74,7 @@
private float mDisplacementShift;
private float mProgressMultiplier;
private float mEndDisplacement;
+ private int mStartingTarget;
private TaskView mTaskBeingDragged;
@@ -120,7 +125,6 @@
// calling the callbacks.
final int directionsToDetectScroll;
boolean ignoreSlopWhenSettling = false;
-
if (mCurrentAnimation != null) {
directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
ignoreSlopWhenSettling = true;
@@ -139,12 +143,15 @@
// The tile can be dragged down to open the task.
mTaskBeingDragged = (TaskView) view;
directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ mStartingTarget = LauncherLogProto.ItemType.TASK;
} else if (isEventOverHotseat(ev)) {
// The hotseat is being dragged
directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
mSwipeDownEnabled = false;
+ mStartingTarget = ContainerType.HOTSEAT;
} else {
mNoIntercept = true;
+ mStartingTarget = ContainerType.WORKSPACE;
return false;
}
}
@@ -249,8 +256,9 @@
@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 && !mSwipeDownEnabled) {
goingToEnd = false;
@@ -269,6 +277,7 @@
goingToEnd = true;
}
} else {
+ logAction = Touch.SWIPE;
goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
}
@@ -280,7 +289,7 @@
progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
- mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd));
+ mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
@@ -289,19 +298,28 @@
anim.start();
}
- private void onCurrentAnimationEnd(boolean wasSuccess) {
- // TODO: Might be a good time to log something.
+ private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
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);
+ mLauncher.getUserEventDispatcher().logTaskLaunch(logAction,
+ Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent());
}
}
+ if (mTaskBeingDragged == null || (wasSuccess && mCurrentAnimationIsGoingUp)) {
+ mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
+ mCurrentAnimationIsGoingUp ? Direction.UP : Direction.DOWN,
+ mStartingTarget, ContainerType.TASKSWITCHER,
+ mLauncher.getStateManager().getState().containerType,
+ mRecentsView.getCurrentPage());
+ }
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
index 3ae8f41..4fb3886 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
@@ -58,10 +58,12 @@
protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
if (stateChanged) {
// Transition complete. log the action
- mLauncher.getUserEventDispatcher().logActionOnContainer(
+ mLauncher.getUserEventDispatcher().logStateChangeAction(
wasFling ? Touch.FLING : Touch.SWIPE,
Direction.UP,
- ContainerType.OVERVIEW,
+ ContainerType.HOTSEAT,
+ ContainerType.TASKSWITCHER,
+ ContainerType.ALLAPPS,
mLauncher.getWorkspace().getCurrentPage());
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 9a5640b..7d371e9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -34,6 +34,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.RecentsView;
+import com.android.quickstep.TaskView;
public class RecentsViewStateController implements StateHandler {
@@ -62,6 +63,12 @@
mWorkspaceCard.setWorkspaceScrollingEnabled(state == OVERVIEW);
setVisibility(state == OVERVIEW);
setTransitionProgress(state == OVERVIEW ? 1 : 0);
+ if (state == OVERVIEW) {
+ for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) {
+ ((TaskView) mRecentsView.getPageAt(i)).resetVisualProperties();
+ }
+ mRecentsView.updateCurveProperties();
+ }
}
@Override
@@ -139,7 +146,9 @@
applyProgress();
if (mIsRecentsScrollingToFirstTask) {
int scrollForFirstTask = mRecentsView.getScrollForPage(mRecentsView.getFirstTaskIndex());
- mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask));
+ int scrollForPage0 = mRecentsView.getScrollForPage(0);
+ mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask
+ + (1 - mTransitionProgress.value) * scrollForPage0));
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index fb59946..2695054 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -50,10 +50,7 @@
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;
@@ -371,9 +368,12 @@
private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
if (targetState != mFromState) {
// Transition complete. log the action
- mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
+ mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
- mStartContainerType, mLauncher.getWorkspace().getCurrentPage());
+ mStartContainerType,
+ mFromState.containerType,
+ mToState.containerType,
+ mLauncher.getWorkspace().getCurrentPage());
}
clearState();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index b4f40c2..7bd4366 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,12 +16,11 @@
package com.android.launcher3.uioverrides;
-import android.content.pm.PackageManager;
+import static com.android.launcher3.LauncherState.NORMAL;
+
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;
@@ -29,6 +28,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsView;
import com.android.systemui.shared.recents.view.RecentsTransition;
@@ -68,6 +68,11 @@
OptionsPopupView.show(launcher, touchPoint.x, touchPoint.y);
}
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) {
+ OverviewInteractionState.setBackButtonVisible(launcher, launcher == null
+ || !launcher.isInState(NORMAL) || !launcher.hasWindowFocus());
+ }
+
public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
BitmapRenderer renderer) {
if (USE_HARDWARE_BITMAP && !forceSoftwareRenderer) {
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
index fae9b66..6e92d83 100644
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -16,17 +16,22 @@
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 static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
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 +39,21 @@
@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_SWITCH =
+ ACTION_VIRTUAL | (1 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_QUICK_SCRUB_START =
+ ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_QUICK_SCRUB_PROGRESS =
+ ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_QUICK_SCRUB_END =
+ ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
+ private static final int ACTION_RESET =
+ ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
+
private final EventArray mEmptyArray = new EventArray();
private final Object mExecutionLock = new Object();
@@ -46,45 +66,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;
- }
+ setInterimChoreographerLocked(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 +139,29 @@
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_SWITCH:
+ mConsumer.updateTouchTracking(INTERACTION_QUICK_SWITCH);
+ break;
+ 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;
+ default:
+ Log.e(TAG, "Invalid virtual event: " + event.getAction());
+ }
+ } else {
+ mConsumer.accept(event);
+ }
event.recycle();
}
array.clear();
@@ -135,6 +180,30 @@
}
}
+ private void queueVirtualAction(int action, float progress) {
+ queueNoPreProcess(MotionEvent.obtain(0, 0, action, progress, 0, 0));
+ }
+
+ public void onQuickSwitch() {
+ queueVirtualAction(ACTION_QUICK_SWITCH, 0);
+ }
+
+ public void onQuickScrubStart() {
+ queueVirtualAction(ACTION_QUICK_SCRUB_START, 0);
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ queueVirtualAction(ACTION_QUICK_SCRUB_PROGRESS, progress);
+ }
+
+ public void onQuickScrubEnd() {
+ queueVirtualAction(ACTION_QUICK_SCRUB_END, 0);
+ }
+
+ public void reset() {
+ queueVirtualAction(ACTION_RESET, 0);
+ }
+
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
index 40246e2..ff7d434 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -67,7 +67,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;
@@ -369,18 +368,8 @@
((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();
+ mQuickScrubController.onQuickSwitch();
}
}
}
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
new file mode 100644
index 0000000..431fb30
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
@@ -0,0 +1,84 @@
+/*
+ * 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.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.Build.VERSION_CODES;
+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.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 LauncherIcons mLauncherIcons;
+
+ public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+ super(context, iconCache, activityInfoCache);
+ }
+
+ @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);
+ mDefaultIcons.put(userId, info);
+ }
+
+ return new FastBitmapDrawable(info);
+ }
+ }
+
+ @Override
+ protected Drawable createBadgedDrawable(Drawable drawable, int userId) {
+ return new FastBitmapDrawable(getBitmapInfo(drawable, userId));
+ }
+
+ private synchronized BitmapInfo getBitmapInfo(Drawable drawable, int userId) {
+ if (mLauncherIcons == null) {
+ mLauncherIcons = LauncherIcons.obtain(mContext);
+ }
+
+ // 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);
+ }
+
+ @Override
+ protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId) {
+ return createBadgedDrawable(
+ activityInfo.loadUnbadgedIcon(mContext.getPackageManager()), userId);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
index d8f7aaf..488cd72 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -23,6 +23,8 @@
import static android.view.MotionEvent.INVALID_POINTER_ID;
import static com.android.quickstep.RemoteRunnable.executeSafely;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
+import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
@@ -35,9 +37,12 @@
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.metrics.LogMaker;
import android.os.Bundle;
import android.os.Looper;
+import android.os.SystemClock;
import android.util.Log;
+import android.view.Choreographer;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
@@ -52,6 +57,7 @@
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;
@@ -60,6 +66,40 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+class EventLogTags {
+ private EventLogTags() {
+ } // don't instantiate
+
+ /** 524292 sysui_multi_action (content|4) */
+ public static final int SYSUI_MULTI_ACTION = 524292;
+
+ public static void writeSysuiMultiAction(Object[] content) {
+ android.util.EventLog.writeEvent(SYSUI_MULTI_ACTION, content);
+ }
+}
+
+class MetricsLogger {
+ private static MetricsLogger sMetricsLogger;
+
+ private static MetricsLogger getLogger() {
+ if (sMetricsLogger == null) {
+ sMetricsLogger = new MetricsLogger();
+ }
+ return sMetricsLogger;
+ }
+
+ protected void saveLog(Object[] rep) {
+ EventLogTags.writeSysuiMultiAction(rep);
+ }
+
+ public void write(LogMaker content) {
+ if (content.getType() == 0/*MetricsEvent.TYPE_UNKNOWN*/) {
+ content.setType(4/*MetricsEvent.TYPE_ACTION*/);
+ }
+ saveLog(content.serialize());
+ }
+}
+
/**
* Touch consumer for handling events originating from an activity other than Launcher
*/
@@ -73,6 +113,7 @@
private final Intent mHomeIntent;
private final ISystemUiProxy mISystemUiProxy;
private final MainThreadExecutor mMainThreadExecutor;
+ private final Choreographer mBackgroundThreadChoreographer;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
@@ -83,12 +124,17 @@
private BaseSwipeInteractionHandler mInteractionHandler;
private int mDisplayRotation;
private Rect mStableInsets = new Rect();
+ private @HitTarget int mDownHitTarget = HIT_TARGET_NONE;
private VelocityTracker mVelocityTracker;
+ private MotionEventQueue mEventQueue;
+
+ private final MetricsLogger mMetricsLogger = new MetricsLogger();
public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
RecentsModel recentsModel, Intent homeIntent, ISystemUiProxy systemUiProxy,
- MainThreadExecutor mainThreadExecutor) {
+ MainThreadExecutor mainThreadExecutor, Choreographer backgroundThreadChoreographer,
+ @HitTarget int downHitTarget) {
super(base);
mRunningTask = runningTaskInfo;
mRecentsModel = recentsModel;
@@ -96,6 +142,8 @@
mVelocityTracker = VelocityTracker.obtain();
mISystemUiProxy = systemUiProxy;
mMainThreadExecutor = mainThreadExecutor;
+ mBackgroundThreadChoreographer = backgroundThreadChoreographer;
+ mDownHitTarget = downHitTarget;
}
@Override
@@ -109,12 +157,13 @@
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
- mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
+ mTouchSlop = ViewConfiguration.get(this).getScaledPagingTouchSlop();
mTouchThresholdCrossed = 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 (!isUsingScreenShot() && mDownHitTarget != HIT_TARGET_BACK) {
+ startTouchTrackingForWindowAnimation(ev.getEventTime());
}
Display display = getSystemService(WindowManager.class).getDefaultDisplay();
@@ -155,11 +204,15 @@
if (isUsingScreenShot()) {
startTouchTrackingForScreenshotAnimation();
+ } else if (mDownHitTarget == HIT_TARGET_BACK) {
+ // If we deferred starting the window animation on touch down, then
+ // start tracking now
+ startTouchTrackingForWindowAnimation(ev.getEventTime());
}
notifyGestureStarted();
}
- } else {
+ } else if (mInteractionHandler != null) {
// Move
mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
}
@@ -177,6 +230,9 @@
}
private void notifyGestureStarted() {
+ if (mInteractionHandler == null) {
+ return;
+ }
// Notify the handler that the gesture has actually started
mInteractionHandler.onGestureStarted();
@@ -195,7 +251,7 @@
}
private boolean isUsingScreenShot() {
- return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_animation", true);
+ return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_for_swipe_up", false);
}
/**
@@ -222,7 +278,7 @@
// Preload the plan
mRecentsModel.loadTasks(mRunningTask.id, null);
mInteractionHandler = handler;
- mInteractionHandler.setGestureEndCallback(this::onFinish);
+ mInteractionHandler.setGestureEndCallback(mEventQueue::reset);
}
private Bitmap getCurrentTaskSnapshot() {
@@ -253,7 +309,7 @@
}
}
- private void startTouchTrackingForWindowAnimation() {
+ private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
// Create the shared handler
final WindowTransformSwipeHandler handler =
new WindowTransformSwipeHandler(mRunningTask, this);
@@ -261,7 +317,7 @@
// 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(() -> {
@@ -270,14 +326,15 @@
switchToMainChoreographer();
}
});
- handler.initWhenReady(mMainThreadExecutor);
+ handler.initWhenReady();
+ TraceHelper.beginSection("RecentsController");
Runnable startActivity = () -> ActivityManagerWrapper.getInstance()
.startRecentsActivity(mHomeIntent,
new AssistDataReceiver() {
@Override
public void onHandleAssistData(Bundle bundle) {
- // Pass to AIAI
+ mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
}
},
new RecentsAnimationListener() {
@@ -286,16 +343,30 @@
RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
Rect minimizedHomeBounds) {
if (mInteractionHandler == handler) {
- handler.setRecentsAnimation(controller, apps, homeContentInsets,
+ TraceHelper.partitionSection("RecentsController", "Received");
+ handler.onRecentsAnimationStart(controller, apps, homeContentInsets,
minimizedHomeBounds);
} else {
+ TraceHelper.endSection("RecentsController", "Finishing no handler");
controller.finish(false /* toHome */);
}
+
+ // Mimic ActivityMetricsLogger.logAppTransitionMultiEvents() logging for
+ // "Recents" activity for app transition tests.
+ 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*/,
+ SystemClock.uptimeMillis() - touchTimeMs);
+ mMetricsLogger.write(builder);
}
public void onAnimationCanceled() {
+ TraceHelper.endSection("RecentsController",
+ "Cancelled: " + mInteractionHandler);
if (mInteractionHandler == handler) {
- handler.setRecentsAnimation(null, null, null, null);
+ handler.onRecentsAnimationCanceled();
}
}
}, null, null);
@@ -319,7 +390,7 @@
* the animation can still be running.
*/
private void finishTouchTracking() {
- if (mTouchThresholdCrossed) {
+ if (mTouchThresholdCrossed && mInteractionHandler != null) {
mVelocityTracker.computeCurrentVelocity(1000,
ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
@@ -331,11 +402,13 @@
// 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();
}
mVelocityTracker.recycle();
mVelocityTracker = null;
-
- onTouchTrackingComplete();
}
@Override
@@ -343,8 +416,8 @@
// Clean up the old interaction handler
if (mInteractionHandler != null) {
final BaseSwipeInteractionHandler handler = mInteractionHandler;
- mMainThreadExecutor.execute(handler::reset);
mInteractionHandler = null;
+ mMainThreadExecutor.execute(handler::reset);
}
}
@@ -352,16 +425,23 @@
public void updateTouchTracking(int interactionType) {
notifyGestureStarted();
- mMainThreadExecutor.execute(() -> {
+ if (isUsingScreenShot()) {
+ mMainThreadExecutor.execute(() -> {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.updateInteractionType(interactionType);
+ }
+ });
+ } else {
if (mInteractionHandler != null) {
mInteractionHandler.updateInteractionType(interactionType);
}
- });
+ }
}
@Override
- public boolean shouldUseBackgroundConsumer() {
- return !isUsingScreenShot();
+ public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ mEventQueue = queue;
+ return isUsingScreenShot() ? null : mBackgroundThreadChoreographer;
}
@Override
@@ -378,14 +458,10 @@
}
}
- private void onFinish() {
- mInteractionHandler = null;
+ public void switchToMainChoreographer() {
+ mEventQueue.setInterimChoreographer(null);
}
- public void onTouchTrackingComplete() { }
-
- public void switchToMainChoreographer() { }
-
@Override
public void preProcessMotionEvent(MotionEvent ev) {
if (mVelocityTracker != null) {
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
new file mode 100644
index 0000000..3c68281
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -0,0 +1,91 @@
+/*
+ * 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.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+
+/**
+ * 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 {
+
+ private static final String TAG = "OverviewFlags";
+ private static final Handler sUiHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ updateOverviewInteractionFlag((Context) msg.obj, msg.what, msg.arg1 == 1);
+ }
+ };
+ private static final Handler sBackgroundHandler = new Handler(
+ UiThreadHelper.getBackgroundLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ ISystemUiProxy systemUiProxy = (ISystemUiProxy) msg.obj;
+ int flags = msg.what;
+ try {
+ systemUiProxy.setInteractionState(flags);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to update overview interaction flags", e);
+ }
+ }
+ };
+
+ private static int sFlags;
+
+ public static void setBackButtonVisible(Context context, boolean visible) {
+ updateFlagOnUi(context, FLAG_HIDE_BACK_BUTTON, !visible);
+ }
+
+ private static void updateFlagOnUi(Context context, int flag, boolean enabled) {
+ sUiHandler.removeMessages(flag);
+ sUiHandler.sendMessage(sUiHandler.obtainMessage(flag, enabled ? 1 : 0, 0, context));
+ }
+
+ private static void updateOverviewInteractionFlag(Context context, int flag, boolean enabled) {
+ if (enabled) {
+ sFlags |= flag;
+ } else {
+ sFlags &= ~flag;
+ }
+
+ ISystemUiProxy systemUiProxy = RecentsModel.getInstance(context).getSystemUiProxy();
+ if (systemUiProxy == null) {
+ Log.w(TAG, "Unable to update overview interaction flags; not bound to service");
+ return;
+ }
+ // If we aren't already setting these flags, do so now on the background thread.
+ if (!sBackgroundHandler.hasMessages(sFlags)) {
+ sBackgroundHandler.sendMessage(sBackgroundHandler.obtainMessage(sFlags, systemUiProxy));
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index d1ce820..f28d51c 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -21,6 +21,11 @@
import com.android.launcher3.Alarm;
import com.android.launcher3.Launcher;
import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
* Responds to quick scrub callbacks to page through and launch recent tasks.
@@ -30,52 +35,67 @@
*/
public class QuickScrubController implements OnAlarmListener {
- private static final int NUM_QUICK_SCRUB_SECTIONS = 5;
+ public static final int QUICK_SWITCH_START_DURATION = 133;
+ public static final int QUICK_SWITCH_SNAP_DURATION = 120;
+
+ private static final boolean ENABLE_AUTO_ADVANCE = true;
+ private static final int NUM_QUICK_SCRUB_SECTIONS = 3;
+ private static final long INITIAL_AUTO_ADVANCE_DELAY = 1000;
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 Launcher mLauncher;
+ private boolean mInQuickScrub;
private int mQuickScrubSection;
private int mStartPage;
+ private boolean mHasAlarmRun;
- public QuickScrubController(Launcher launcher) {
+ public QuickScrubController(Launcher launcher, RecentsView recentsView) {
mLauncher = launcher;
- mAutoAdvanceAlarm = new Alarm();
- mAutoAdvanceAlarm.setOnAlarmListener(this);
+ mRecentsView = recentsView;
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm = new Alarm();
+ mAutoAdvanceAlarm.setOnAlarmListener(this);
+ }
}
public void onQuickScrubStart(boolean startingFromHome) {
- mRecentsView = mLauncher.getOverviewPanel();
+ mInQuickScrub = true;
mStartPage = startingFromHome ? 0 : mRecentsView.getFirstTaskIndex();
mQuickScrubSection = 0;
+ mHasAlarmRun = false;
+ mLauncher.getUserEventDispatcher().resetActionDurationMillis();
}
public void onQuickScrubEnd() {
- mAutoAdvanceAlarm.cancelAlarm();
- if (mRecentsView == null) {
- } else {
- int page = mRecentsView.getNextPage();
- Runnable launchTaskRunnable = () -> {
- if (page < mRecentsView.getFirstTaskIndex()) {
- mRecentsView.getPageAt(page).performClick();
- } else {
- ((TaskView) mRecentsView.getPageAt(page)).launchTask(true);
- }
- };
- 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();
- }
+ mInQuickScrub = false;
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm.cancelAlarm();
}
+ int page = mRecentsView.getNextPage();
+ Runnable launchTaskRunnable = () -> {
+ if (page < mRecentsView.getFirstTaskIndex()) {
+ mRecentsView.getPageAt(page).performClick();
+ } else {
+ ((TaskView) mRecentsView.getPageAt(page)).launchTask(true);
+ }
+ };
+ 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();
+ }
+ mLauncher.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP,
+ ControlType.QUICK_SCRUB_BUTTON, null, mStartPage == 0 ?
+ ContainerType.WORKSPACE : ContainerType.APP);
}
public void onQuickScrubProgress(float progress) {
@@ -83,20 +103,46 @@
if (quickScrubSection != mQuickScrubSection) {
int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
goToPageWithHaptic(pageToGoTo);
- if (quickScrubSection == NUM_QUICK_SCRUB_SECTIONS || quickScrubSection == 0) {
- mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
- } else {
- mAutoAdvanceAlarm.cancelAlarm();
+ if (ENABLE_AUTO_ADVANCE) {
+ if (quickScrubSection == NUM_QUICK_SCRUB_SECTIONS || quickScrubSection == 0) {
+ mAutoAdvanceAlarm.setAlarm(mHasAlarmRun
+ ? AUTO_ADVANCE_DELAY : INITIAL_AUTO_ADVANCE_DELAY);
+ } else {
+ mAutoAdvanceAlarm.cancelAlarm();
+ }
}
mQuickScrubSection = quickScrubSection;
}
}
+ public void onQuickSwitch() {
+ for (int i = mRecentsView.getFirstTaskIndex(); i < mRecentsView.getPageCount(); i++) {
+ TaskView taskView = (TaskView) mRecentsView.getPageAt(i);
+ if (taskView.getTask().key.id != mRecentsView.getRunningTaskId()) {
+ Runnable launchTaskRunnable = () -> taskView.launchTask(true);
+ if (mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION)) {
+ // Snap to the new page then launch it
+ mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
+ } else {
+ // No need to move page, just launch task directly
+ launchTaskRunnable.run();
+ }
+ break;
+ }
+ }
+ mLauncher.getUserEventDispatcher().logActionOnControl(Touch.FLING,
+ ControlType.QUICK_SCRUB_BUTTON, null, mStartPage == 0 ?
+ ContainerType.WORKSPACE : ContainerType.APP);
+ }
+
public void snapToPageForCurrentQuickScrubSection() {
- goToPageWithHaptic(mRecentsView.getFirstTaskIndex() + mQuickScrubSection);
+ if (mInQuickScrub) {
+ goToPageWithHaptic(mRecentsView.getFirstTaskIndex() + mQuickScrubSection);
+ }
}
private void goToPageWithHaptic(int pageToGoTo) {
+ pageToGoTo = Utilities.boundToRange(pageToGoTo, mStartPage, mRecentsView.getPageCount() - 1);
if (pageToGoTo != mRecentsView.getNextPage()) {
int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage())
* QUICKSCRUB_SNAP_DURATION_PER_PAGE;
@@ -115,6 +161,9 @@
} else if (mQuickScrubSection == 0 && currPage > mStartPage) {
goToPageWithHaptic(currPage - 1);
}
- mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ mHasAlarmRun = true;
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java b/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
index 9cc038f..1f9c728 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
@@ -53,12 +53,15 @@
private Rect mTaskInsets;
private Rect mThumbnail;
- private float mTaskScale;
+ 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) {
+ public RecentsAnimationInterpolator(Rect window, Rect insets, Rect task, Rect taskInsets,
+ float taskScale, float taskTranslationX) {
mWindow = window;
mInsets = insets;
mTask = task;
@@ -68,16 +71,18 @@
mThumbnail = new Rect(task);
Utilities.insetRect(mThumbnail, taskInsets);
- mTaskScale = (float) mInsetWindow.width() / mThumbnail.width();
+ mInitialTaskScale = taskScale;
+ mInitialTaskTranslationX = taskTranslationX;
+ mFinalTaskScale = (float) mInsetWindow.width() / mThumbnail.width();
mScaledTask = new Rect(task);
- Utilities.scaleRectAboutCenter(mScaledTask, mTaskScale);
+ Utilities.scaleRectAboutCenter(mScaledTask, mFinalTaskScale);
Rect finalScaledTaskInsets = new Rect(taskInsets);
- Utilities.scaleRect(finalScaledTaskInsets, mTaskScale);
+ Utilities.scaleRect(finalScaledTaskInsets, mFinalTaskScale);
mTargetTask = new Rect(mInsetWindow);
mTargetTask.offsetTo(window.top + insets.top - finalScaledTaskInsets.top,
window.left + insets.left - finalScaledTaskInsets.left);
- float initialWinScale = 1f / mTaskScale;
+ float initialWinScale = 1f / mFinalTaskScale;
Rect scaledWindow = new Rect(mInsetWindow);
Utilities.scaleRectAboutCenter(scaledWindow, initialWinScale);
Rect scaledInsets = new Rect(insets);
@@ -89,13 +94,14 @@
public TaskWindowBounds interpolate(float t) {
mTmpTaskWindowBounds.taskScale = Utilities.mapRange(t,
- 1, (float) mInsetWindow.width() / mThumbnail.width());
+ mInitialTaskScale, mFinalTaskScale);
mTmpTaskWindowBounds.taskX = Utilities.mapRange(t,
- 0, mTargetTask.left - mScaledTask.left);
+ mInitialTaskTranslationX, mTargetTask.left - mScaledTask.left);
mTmpTaskWindowBounds.taskY = Utilities.mapRange(t,
0, mTargetTask.top - mScaledTask.top);
- mTmpTaskWindowBounds.winScale = mTmpTaskWindowBounds.taskScale / mTaskScale;
+ float taskScale = Utilities.mapRange(t, 1, mFinalTaskScale);
+ mTmpTaskWindowBounds.winScale = taskScale / mFinalTaskScale;
mTmpTaskWindowBounds.winX = Utilities.mapRange(t,
mSrcWindow.left, 0);
mTmpTaskWindowBounds.winY = Utilities.mapRange(t,
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
index 7c98317..4e11220 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;
@@ -31,6 +32,7 @@
public synchronized void setController(
RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] targets) {
+ TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
this.controller = controller;
this.targets = targets;
@@ -39,12 +41,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 +66,8 @@
if (mInputConsumerEnabled) {
BackgroundExecutor.get().submit(() -> {
synchronized (this) {
+ TraceHelper.partitionSection("RecentsController",
+ "Enabling consumer on " + controller);
if (controller != null) {
controller.setInputConsumerEnabled(true);
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index d10c5a6..7fe7751 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -16,22 +16,33 @@
package com.android.quickstep;
import android.annotation.TargetApi;
+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 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;
@@ -60,6 +71,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,6 +82,7 @@
private int mLastLoadPlanId;
private int mTaskChangeId;
private ISystemUiProxy mSystemUiProxy;
+ private boolean mClearAssistCacheOnStackChange = true;
private RecentsModel(Context context) {
mContext = context;
@@ -75,7 +90,15 @@
Resources res = context.getResources();
mRecentsTaskLoader = new RecentsTaskLoader(mContext,
res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
- res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
+ res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0) {
+
+ @Override
+ protected IconLoader createNewIconLoader(Context context,
+ TaskKeyLruCache<Drawable> iconCache,
+ LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+ return new NormalizedIconLoader(context, iconCache, activityInfoCache);
+ }
+ };
mRecentsTaskLoader.startLoader(mContext);
mMainThreadExecutor = new MainThreadExecutor();
@@ -130,6 +153,13 @@
@Override
public void onTaskStackChanged() {
mTaskChangeId++;
+
+ Preconditions.assertUIThread();
+ if (mClearAssistCacheOnStackChange) {
+ mCachedAssistData.clear();
+ } else {
+ mClearAssistCacheOnStackChange = true;
+ }
}
public boolean isLoadPlanValid(int resultId) {
@@ -147,4 +177,40 @@
public ISystemUiProxy getSystemUiProxy() {
return mSystemUiProxy;
}
+
+ @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
index 343922c..ec0716c 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -16,6 +16,11 @@
package com.android.quickstep;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.quickstep.TaskView.CURVE_FACTOR;
+import static com.android.quickstep.TaskView.CURVE_INTERPOLATOR;
+
import android.animation.LayoutTransition;
import android.content.Context;
import android.graphics.Bitmap;
@@ -54,10 +59,6 @@
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.
*/
@@ -108,6 +109,8 @@
private Matrix mFadeMatrix;
private boolean mScrimOnLeft;
+ private boolean mFirstTaskIconScaledDown = false;
+
public RecentsView(Context context) {
this(context, null);
}
@@ -124,21 +127,22 @@
setClipToOutline(true);
mLauncher = Launcher.getLauncher(context);
- mQuickScrubController = new QuickScrubController(mLauncher);
+ mQuickScrubController = new QuickScrubController(mLauncher, this);
mModel = RecentsModel.getInstance(context);
mScrollState.isRtl = mIsRtl;
}
- public void updateThumbnail(int taskId, ThumbnailData thumbnailData) {
+ public TaskView 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);
taskView.setAlpha(1);
- return;
+ return taskView;
}
}
+ return null;
}
private void setupLayoutTransition() {
@@ -218,8 +222,8 @@
}
public boolean isTaskViewVisible(TaskView tv) {
- // For now, just check if it's the active task
- return indexOfChild(tv) == getNextPage();
+ // 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) {
@@ -266,6 +270,8 @@
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());
@@ -289,13 +295,15 @@
final Task task = tasks.get(i);
final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1 + mFirstTaskIndex);
taskView.bind(task);
- taskView.setScaleX(1f);
- taskView.setScaleY(1f);
- taskView.setTranslationX(0f);
- taskView.setTranslationY(0f);
- taskView.setAlpha(1f);
+ taskView.resetVisualProperties();
loader.loadTaskData(task);
}
+ updateCurveProperties();
+ applyIconScale(false /* animate */);
+
+ if (oldChildCount != getChildCount()) {
+ mQuickScrubController.snapToPageForCurrentQuickScrubSection();
+ }
}
private void updateTaskStackListenerState() {
@@ -392,7 +400,7 @@
/**
* Scales and adjusts translation of adjacent pages as if on a curved carousel.
*/
- private void updateCurveProperties() {
+ public void updateCurveProperties() {
if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
return;
}
@@ -431,6 +439,10 @@
return getChildCount() - mFirstTaskIndex;
}
+ public int getRunningTaskId() {
+ return mRunningTaskId;
+ }
+
/**
* Reloads the view if anything in recents changed.
*/
@@ -468,9 +480,7 @@
mRunningTaskId = runningTaskId;
setCurrentPage(mFirstTaskIndex);
if (mCurrentPage >= mFirstTaskIndex) {
- TaskView currentTask = (TaskView) getPageAt(mCurrentPage);
- currentTask.setIconScale(0);
- currentTask.setAlpha(0);
+ getPageAt(mCurrentPage).setAlpha(0);
}
}
@@ -478,6 +488,26 @@
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(mFirstTaskIndex);
+ if (firstTask != null) {
+ if (animate) {
+ firstTask.animateIconToScale(scale);
+ } else {
+ firstTask.setIconScale(scale);
+ }
+ }
+ }
+
@Override
public void draw(Canvas canvas) {
if (mScrim == null) {
diff --git a/quickstep/src/com/android/quickstep/TaskMenuView.java b/quickstep/src/com/android/quickstep/TaskMenuView.java
index 52b2400..6bbcb37 100644
--- a/quickstep/src/com/android/quickstep/TaskMenuView.java
+++ b/quickstep/src/com/android/quickstep/TaskMenuView.java
@@ -144,6 +144,7 @@
icon.setBounds(0, 0, iconSize, iconSize);
mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, taskView.getTask()));
+ mTaskIconAndName.setOnClickListener(v -> close(true));
for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, taskView);
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 d8ba186..75d8619 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -23,7 +23,6 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
import android.view.View;
@@ -98,11 +97,14 @@
@Override
public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
- if (launcher.getDeviceProfile().inMultiWindowMode()) {
+ if (launcher.getDeviceProfile().isMultiWindowMode) {
+ return null;
+ }
+ final Task task = taskView.getTask();
+ if (!task.isDockable) {
return null;
}
return (v -> {
- Task task = taskView.getTask();
final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(true);
final Consumer<Boolean> resultCallback = success -> {
if (success) {
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
index 4f93b1c..9b9c6f2 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
@@ -55,6 +55,7 @@
private final Matrix mMatrix = new Matrix();
+ private Task mTask;
private ThumbnailData mThumbnailData;
protected BitmapShader mBitmapShader;
@@ -83,6 +84,7 @@
* 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) {
@@ -189,7 +191,7 @@
mPaint.setShader(shader);
}
- mOverlay.setTaskInfo(mThumbnailData, mMatrix);
+ mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
invalidate();
}
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 8865a42..b407f75 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -19,34 +19,27 @@
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.Launcher;
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;
/**
@@ -68,23 +61,9 @@
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);
@@ -136,26 +115,7 @@
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);
+ opts = Launcher.getLauncher(getContext()).getActivityLaunchOptions(this, false);
} else {
opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
}
@@ -185,16 +145,21 @@
}
public void animateIconToScale(float scale) {
- ObjectAnimator.ofFloat(this, SCALE_ICON_PROPERTY, scale)
- .setDuration(SCALE_ICON_DURATION).start();
+ mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
}
protected void setIconScale(float iconScale) {
- mIconScale = iconScale;
- if (mIconView != null) {
- mIconView.setScaleX(mIconScale);
- mIconView.setScaleY(mIconScale);
- }
+ mIconView.animate().cancel();
+ mIconView.setScaleX(iconScale);
+ mIconView.setScaleY(iconScale);
+ }
+
+ public void resetVisualProperties() {
+ setScaleX(1f);
+ setScaleY(1f);
+ setTranslationX(0f);
+ setTranslationY(0f);
+ setAlpha(1f);
}
@Override
@@ -228,7 +193,6 @@
return SCROLL_TYPE_TASK;
}
-
private static final class TaskOutlineProvider extends ViewOutlineProvider {
private final int mMarginTop;
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
index 77480af..f35f6a6 100644
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -18,8 +18,11 @@
import android.annotation.TargetApi;
import android.os.Build;
import android.support.annotation.IntDef;
+import android.view.Choreographer;
import android.view.MotionEvent;
+import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.function.Consumer;
@@ -46,10 +49,6 @@
default void reset() { }
- default boolean shouldUseBackgroundConsumer() {
- return false;
- }
-
default void updateTouchTracking(@InteractionType int interactionType) { }
default void onQuickScrubEnd() { }
@@ -61,4 +60,8 @@
* posted on a handler thread.
*/
default void preProcessMotionEvent(MotionEvent ev) { }
+
+ default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ return null;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5e89644..c166292 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,9 +21,8 @@
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.OVERVIEW;
+import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION;
import android.annotation.TargetApi;
import android.app.ActivityManager.RunningTaskInfo;
@@ -36,7 +35,9 @@
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.View;
@@ -45,11 +46,13 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.model.ModelPreload;
import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.TraceHelper;
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.
@@ -57,6 +60,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";
@@ -69,8 +81,20 @@
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@Override
+ public void onPreMotionEvent(@HitTarget int downHitTarget) throws RemoteException {
+ TraceHelper.beginSection("SysUiBinder");
+ onBinderPreMotionEvent(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
@@ -79,33 +103,38 @@
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
RemoteRunnable.executeSafely(() -> mISystemUiProxy.setRecentsOnboardingText(
getResources().getString(R.string.recents_swipe_up_onboarding)));
+ Launcher launcher = (Launcher) LauncherAppState.getInstance(
+ TouchInteractionService.this).getModel().getCallback();
+ UiFactory.onLauncherStateOrFocusChanged(launcher);
}
@Override
public void onQuickSwitch() {
- mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SWITCH);
+ mEventQueue.onQuickSwitch();
+ TraceHelper.endSection("SysUiBinder", "onQuickSwitch");
}
@Override
public void onQuickScrubStart() {
- mCurrentConsumer.updateTouchTracking(INTERACTION_QUICK_SCRUB);
+ mEventQueue.onQuickScrubStart();
sQuickScrubEnabled = true;
- }
-
- @Override
- public void onQuickScrubEnd() {
- mCurrentConsumer.onQuickScrubEnd();
- sQuickScrubEnabled = false;
+ 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");
+ sQuickScrubEnabled = false;
}
};
private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
- private TouchConsumer mCurrentConsumer = mNoOpTouchConsumer;
private static boolean sConnected = false;
private static boolean sQuickScrubEnabled = false;
@@ -126,7 +155,10 @@
private MotionEventQueue mEventQueue;
private MainThreadExecutor mMainThreadExecutor;
private ISystemUiProxy mISystemUiProxy;
+
+ private Choreographer mMainThreadChoreographer;
private Choreographer mBackgroundThreadChoreographer;
+ private MotionEventQueue mNoOpEventQueue;
@Override
public void onCreate() {
@@ -144,10 +176,14 @@
// Clear the packageName as system can fail to dedupe it b/64108432
mHomeIntent.setComponent(mLauncher).setPackage(null);
- mEventQueue = new MotionEventQueue(Choreographer.getInstance(), mNoOpTouchConsumer);
+ mMainThreadChoreographer = Choreographer.getInstance();
+ mNoOpEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
+ mEventQueue = mNoOpEventQueue;
+
sConnected = true;
- new ModelPreload().start(this);
+ // Temporarily disable model preload
+ // new ModelPreload().start(this);
initBackgroundChoreographer();
}
@@ -164,89 +200,72 @@
return mMyBinder;
}
- private void onBinderMotionEvent(MotionEvent ev) {
- if (ev.getActionMasked() == ACTION_DOWN) {
- mRunningTask = mAM.getRunningTask();
+ private void onBinderPreMotionEvent(@HitTarget int downHitTarget) {
+ mRunningTask = mAM.getRunningTask();
- mCurrentConsumer.reset();
- if (mRunningTask == null) {
- mCurrentConsumer = mNoOpTouchConsumer;
- } else if (mRunningTask.topActivity.equals(mLauncher)) {
- mCurrentConsumer = getLauncherConsumer();
- } else {
- mCurrentConsumer = getOtherActivityConsumer();
- }
+ mEventQueue.reset();
- mEventQueue.setConsumer(mCurrentConsumer);
- mEventQueue.setInterimChoreographer(mCurrentConsumer.shouldUseBackgroundConsumer()
- ? mBackgroundThreadChoreographer : null);
+ if (mRunningTask == null) {
+ mEventQueue = mNoOpEventQueue;
+ } else if (mRunningTask.topActivity.equals(mLauncher)) {
+ mEventQueue = getLauncherEventQueue();
+ } else {
+ mEventQueue = new MotionEventQueue(mMainThreadChoreographer,
+ new OtherActivityTouchConsumer(this, mRunningTask, mRecentsModel,
+ mHomeIntent, mISystemUiProxy, mMainThreadExecutor,
+ mBackgroundThreadChoreographer, downHitTarget));
}
- mCurrentConsumer.preProcessMotionEvent(ev);
- mEventQueue.queue(ev);
}
- private TouchConsumer getOtherActivityConsumer() {
- TouchConsumer consumer = new OtherActivityTouchConsumer(this, mRunningTask, mRecentsModel,
- mHomeIntent, mISystemUiProxy, mMainThreadExecutor) {
-
- @Override
- public void switchToMainChoreographer() {
- if (mCurrentConsumer == this) {
- mEventQueue.setInterimChoreographer(null);
- }
- }
-
- @Override
- public void onTouchTrackingComplete() {
- if (mCurrentConsumer == this) {
- mCurrentConsumer = mNoOpTouchConsumer;
- mEventQueue.setConsumer(mCurrentConsumer);
- }
- }
- };
- return consumer;
- }
-
- private TouchConsumer getLauncherConsumer() {
-
+ private MotionEventQueue getLauncherEventQueue() {
Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
if (launcher == null) {
- return mNoOpTouchConsumer;
+ return mNoOpEventQueue;
}
View target = launcher.getDragLayer();
- if (!target.getWindowId().isFocused()) {
- return mNoOpTouchConsumer;
- }
- return new LauncherTouchConsumer(target);
+ return new MotionEventQueue(mMainThreadChoreographer,
+ 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: {
@@ -270,7 +289,7 @@
}
if (action == ACTION_UP || action == ACTION_CANCEL) {
- mEventQueue.setConsumer(mNoOpTouchConsumer);
+ mInvalidated = true;
}
}
@@ -282,6 +301,47 @@
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
}
+
+ @Override
+ public void updateTouchTracking(int interactionType) {
+ if (mInvalidated) {
+ return;
+ }
+ if (TouchConsumer.isInteractionQuick(interactionType)) {
+ Runnable action = () -> {
+ Runnable onComplete = null;
+ if (interactionType == INTERACTION_QUICK_SCRUB) {
+ mQuickScrubController.onQuickScrubStart(true);
+ } else if (interactionType == INTERACTION_QUICK_SWITCH) {
+ onComplete = mQuickScrubController::onQuickSwitch;
+ }
+ mLauncher.getStateManager().goToState(OVERVIEW, true, 0,
+ QUICK_SWITCH_START_DURATION, onComplete);
+ };
+
+ 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 7247af4..19942c3 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -18,6 +18,7 @@
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 static com.android.quickstep.QuickScrubController.QUICK_SWITCH_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;
@@ -28,14 +29,16 @@
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.pm.ActivityInfo;
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;
@@ -57,10 +60,16 @@
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.launcher3.util.ViewOnDrawExecutor;
import com.android.quickstep.TouchConsumer.InteractionType;
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.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.TransactionCompat;
@@ -75,38 +84,51 @@
// Launcher UI related states
private static final int STATE_LAUNCHER_PRESENT = 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_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_CONTROLLER_RECENTS = 1 << 4;
- private static final int STATE_SCALED_CONTROLLER_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 << 6;
- private static final int STATE_GESTURE_STARTED = 1 << 7;
+ 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_SWITCH = 1 << 11;
+ private static final int STATE_QUICK_SCRUB_START = 1 << 12;
+ private static final int STATE_QUICK_SCRUB_END = 1 << 13;
private static final int LAUNCHER_UI_STATES =
- STATE_LAUNCHER_PRESENT | 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_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_START_DURATION = 133;
- private static final int QUICK_SWITCH_SNAP_DURATION = 120;
private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
@@ -115,22 +137,21 @@
// 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 Rect mSourceRect = new Rect();
+ 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 Rect mSourceWindowClipInsets = new Rect();
+ private final RectF mSourceWindowClipInsets = new RectF();
+
// The bounds of launcher (not including insets) in device coordinates
private final Rect mHomeStackBounds = new Rect();
- // The bounds of the task view in launcher window coordinates
- private final Rect mTargetRect = new Rect();
- // Doesn't change after initialized, used as an anchor when changing mTargetRect
- private final Rect mInitialTargetRect = new Rect();
- // The interpolated rect from the source app rect to the target rect
- private final Rect mCurrentRect = 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();
private DeviceProfile mDp;
private int mTransitionDragLength;
@@ -159,10 +180,12 @@
private float mCurrentDisplacement;
private boolean mGestureStarted;
+ private int mLogAction = Touch.SWIPE;
private @InteractionType int mInteractionType = INTERACTION_NORMAL;
- private boolean mStartedQuickScrubFromHome;
- private boolean mDeferredQuickScrubEnd;
+
+ private InputConsumerController mInputConsumer =
+ InputConsumerController.getRecentsAnimationInputConsumer();
private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
private Matrix mTmpMatrix = new Matrix();
@@ -170,6 +193,7 @@
WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context) {
mContext = context;
mRunningTaskId = runningTaskInfo.id;
+ mInputConsumer.registerInputConsumer();
initStateCallbacks();
}
@@ -181,32 +205,46 @@
super.setState(stateFlag);
}
};
+
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_SCALED_CONTROLLER_APP | STATE_APP_CONTROLLER_RECEIVED,
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_SCALED_CONTROLLER_APP,
this::resumeLastTask);
- mStateCallback.addCallback(STATE_SCALED_CONTROLLER_RECENTS
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
| STATE_ACTIVITY_MULTIPLIER_COMPLETE
- | STATE_APP_CONTROLLER_RECEIVED,
+ | STATE_SCALED_CONTROLLER_RECENTS,
this::switchToScreenshot);
-
- mStateCallback.addCallback(STATE_SCALED_CONTROLLER_RECENTS
- | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
+ 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_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_RECENTS,
- 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_SWITCH,
+ this::onQuickInteractionStart);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_SCRUB_START,
+ this::onQuickInteractionStart);
+
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SWITCH_TO_SCREENSHOT_COMPLETE
+ | STATE_QUICK_SWITCH, this::switchToFinalAppAfterQuickSwitch);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SWITCH_TO_SCREENSHOT_COMPLETE
+ | STATE_QUICK_SCRUB_END, this::switchToFinalAppAfterQuickScrub);
}
private void setStateOnUiThread(int stateFlag) {
@@ -220,10 +258,14 @@
private void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
+ mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
+ mSourceStackBounds.width() - mSourceInsets.right,
+ mSourceStackBounds.height() - mSourceInsets.bottom);
- mSourceRect.set(0, 0, dp.widthPx - mSourceInsets.left - mSourceInsets.right,
- dp.heightPx - mSourceInsets.top - mSourceInsets.bottom);
- RecentsView.getPageRect(dp, mContext, mTargetRect);
+ Rect tempRect = new Rect();
+ RecentsView.getPageRect(dp, mContext, tempRect);
+
+ mTargetRect.set(tempRect);
mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
mHomeStackBounds.top - mSourceStackBounds.top);
mInitialTargetRect.set(mTargetRect);
@@ -232,21 +274,23 @@
// 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.
- Rect scaledTargetRect = new Rect(mTargetRect);
- Utilities.scaleRectAboutCenter(scaledTargetRect,
- (float) mSourceRect.width() / mTargetRect.width());
- scaledTargetRect.offsetTo(mSourceInsets.left, mSourceInsets.top);
- mSourceWindowClipInsets.set(scaledTargetRect.left, scaledTargetRect.top,
- mDp.widthPx - scaledTargetRect.right,
- mDp.heightPx - scaledTargetRect.bottom);
+ 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);
Rect targetInsets = dp.getInsets();
- mTransitionDragLength = dp.hotseatBarSizePx;
if (dp.isVerticalBarLayout()) {
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
- mTransitionDragLength += dp.hotseatBarSidePaddingPx + hotseatInset;
+ mTransitionDragLength = dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset;
} else {
- mTransitionDragLength += targetInsets.bottom;
+ mTransitionDragLength = dp.heightPx - tempRect.bottom;
}
}
@@ -277,35 +321,56 @@
mWasLauncherAlreadyVisible = alreadyOnHome;
mLauncher = launcher;
+ // For the duration of the gesture, lock the screen orientation to ensure that we do not
+ // rotate mid-quickscrub
+ mLauncher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+ mRecentsView = mLauncher.getOverviewPanel();
+ mQuickScrubController = mRecentsView.getQuickScrubController();
+ mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
+
+ mStateCallback.setState(STATE_LAUNCHER_PRESENT);
+ if (alreadyOnHome) {
+ onLauncherStart(launcher);
+ } else {
+ launcher.setOnStartCallback(this::onLauncherStart);
+ }
+ return true;
+ }
+
+ private void onLauncherStart(final Launcher launcher) {
+ if (mLauncher != launcher) {
+ return;
+ }
+ if ((mStateCallback.getState() & STATE_HANDLER_INVALIDATED) != 0) {
+ return;
+ }
+
+ mStateCallback.setState(STATE_LAUNCHER_STARTED);
LauncherState startState = mLauncher.getStateManager().getState();
if (startState.disableRestore) {
startState = mLauncher.getStateManager().getRestState();
}
mLauncher.getStateManager().setRestState(startState);
- AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
+ AbstractFloatingView.closeAllOpenViews(mLauncher, mWasLauncherAlreadyVisible);
- mRecentsView = mLauncher.getOverviewPanel();
- mLauncherLayoutListener = new LauncherLayoutListener(mLauncher);
- final int state;
- if (mWasLauncherAlreadyVisible) {
+ if (mWasLauncherAlreadyVisible && !mLauncher.getAppTransitionManager().isAnimating()) {
DeviceProfile dp = mLauncher.getDeviceProfile();
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
- mLauncherTransitionController = launcher.getStateManager()
+ mLauncherTransitionController = mLauncher.getStateManager()
.createAnimationToNewWorkspace(OVERVIEW, accuracy);
mLauncherTransitionController.dispatchOnStart();
mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
- state = STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN
- | STATE_LAUNCHER_PRESENT;
+ mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
} else {
TraceHelper.beginSection("WTS-init");
- launcher.getStateManager().goToState(OVERVIEW, false);
+ mLauncher.getStateManager().goToState(OVERVIEW, false);
TraceHelper.partitionSection("WTS-init", "State changed");
// TODO: Implement a better animation for fading in
- View rootView = launcher.getRootView();
+ View rootView = mLauncher.getRootView();
rootView.setAlpha(0);
rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
@@ -321,17 +386,14 @@
mStateCallback.setState(STATE_LAUNCHER_DRAWN);
}
});
- state = STATE_LAUNCHER_PRESENT;
// Optimization, hide the all apps view to prevent layout while initializing
mLauncher.getAppsView().setVisibility(View.GONE);
}
mRecentsView.showTask(mRunningTaskId);
+ mRecentsView.setFirstTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
mLauncherLayoutListener.open();
-
- mStateCallback.setState(state);
- return true;
}
public void setLauncherOnDrawCallback(Runnable callback) {
@@ -362,7 +424,6 @@
}
public void updateInteractionType(@InteractionType int interactionType) {
- Preconditions.assertUIThread();
if (mInteractionType != INTERACTION_NORMAL) {
throw new IllegalArgumentException(
"Can't change interaction type from " + mInteractionType);
@@ -373,20 +434,15 @@
}
mInteractionType = interactionType;
- if (mLauncher != null) {
- updateUiForQuickScrub();
- }
+ setStateOnUiThread(interactionType == INTERACTION_QUICK_SWITCH
+ ? STATE_QUICK_SWITCH : STATE_QUICK_SCRUB_START);
+
+ // Start the window animation without waiting for launcher.
+ animateToProgress(1f, QUICK_SWITCH_START_DURATION);
}
- private void updateUiForQuickScrub() {
- mStartedQuickScrubFromHome = mWasLauncherAlreadyVisible;
- mDeferredQuickScrubEnd = false;
- mQuickScrubController = mRecentsView.getQuickScrubController();
- mQuickScrubController.onQuickScrubStart(mStartedQuickScrubFromHome);
- animateToProgress(1f, QUICK_SWITCH_START_DURATION);
- if (mStartedQuickScrubFromHome) {
- mLauncherLayoutListener.setVisibility(View.INVISIBLE);
- }
+ private void onQuickInteractionStart() {
+ mQuickScrubController.onQuickScrubStart(false);
}
@WorkerThread
@@ -431,27 +487,24 @@
@WorkerThread
private void updateFinalShift() {
- if (mStartedQuickScrubFromHome) {
- return;
- }
-
float shift = mCurrentShift.value;
synchronized (mRecentsAnimationWrapper) {
if (mRecentsAnimationWrapper.controller != null) {
+ RectF currentRect;
synchronized (mTargetRect) {
- mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
+ currentRect = mRectFEvaluator.evaluate(shift, mSourceRect, mTargetRect);
}
- float scale = (float) mCurrentRect.width() / mSourceRect.width();
mClipRect.left = (int) (mSourceWindowClipInsets.left * shift);
mClipRect.top = (int) (mSourceWindowClipInsets.top * shift);
- mClipRect.right = (int) (mDp.widthPx - (mSourceWindowClipInsets.right * shift));
- mClipRect.bottom = (int) (mDp.heightPx - (mSourceWindowClipInsets.bottom * 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 - mSourceInsets.left * scale * shift,
- mCurrentRect.top - mSourceInsets.top * scale * shift);
+ mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
+
TransactionCompat transaction = new TransactionCompat();
for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
if (app.mode == MODE_CLOSING) {
@@ -466,6 +519,9 @@
if (mLauncherTransitionController != null) {
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.
@@ -476,8 +532,8 @@
if (offsetFromFirstTask != 0) {
synchronized (mTargetRect) {
mTargetRect.set(mInitialTargetRect);
- Utilities.scaleRectAboutCenter(mTargetRect, firstTask.getScaleX());
- int offsetX = (int) (offsetFromFirstTask + firstTask.getTranslationX());
+ Utilities.scaleRectFAboutCenter(mTargetRect, firstTask.getScaleX());
+ float offsetX = offsetFromFirstTask + firstTask.getTranslationX();
mTargetRect.offset(offsetX, 0);
}
}
@@ -491,7 +547,7 @@
}
}
- public void setRecentsAnimation(RecentsAnimationControllerCompat controller,
+ 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
@@ -532,13 +588,17 @@
setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
}
- public void onGestureStarted() {
- if (mLauncher != null) {
- notifyGestureStarted();
- }
+ public void onRecentsAnimationCanceled() {
+ mRecentsAnimationWrapper.setController(null, null);
+ clearReference();
+ setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
+ }
+ public void onGestureStarted() {
+ notifyGestureStarted();
setStateOnUiThread(STATE_GESTURE_STARTED);
mGestureStarted = true;
+ mRecentsAnimationWrapper.enableInputConsumer();
}
/**
@@ -546,7 +606,10 @@
* on both background and UI threads
*/
private void notifyGestureStarted() {
- mLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible);
+ final Launcher curLauncher = mLauncher;
+ if (curLauncher != null) {
+ curLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible);
+ }
}
@WorkerThread
@@ -559,6 +622,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);
@@ -570,11 +634,28 @@
// 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) {
ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
@@ -591,7 +672,8 @@
@UiThread
private void resumeLastTask() {
- mRecentsAnimationWrapper.finish(false /* toHome */);
+ mRecentsAnimationWrapper.finish(false /* toHome */, null);
+ doLogGesture(false /* toLauncher */);
}
public void reset() {
@@ -610,12 +692,23 @@
}
clearReference();
+ mInputConsumer.unregisterInputConsumer();
}
private void invalidateHandlerWithLauncher() {
mLauncherTransitionController = null;
mLauncherLayoutListener.setHandler(null);
mLauncherLayoutListener.close(false);
+
+ // Restore the requested orientation to the user preference after the gesture has ended
+ mLauncher.updateRequestedOrientation();
+ mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, false /* animate */);
+ }
+
+ private void resetStateForAnimationCancel() {
+ LauncherState startState = mLauncher.getStateManager().getRestState();
+ boolean animate = mWasLauncherAlreadyVisible || mGestureStarted;
+ mLauncher.getStateManager().goToState(startState, animate);
}
public void layoutListenerClosed() {
@@ -625,73 +718,70 @@
}
private void switchToScreenshot() {
+ boolean finishTransitionPosted = false;
+ final Runnable finishTransitionRunnable = () -> {
+ synchronized (mRecentsAnimationWrapper) {
+ mRecentsAnimationWrapper.finish(true /* toHome */,
+ () -> setStateOnUiThread(STATE_SWITCH_TO_SCREENSHOT_COMPLETE));
+ }
+ };
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 =
+ ThumbnailData thumbnail =
mRecentsAnimationWrapper.controller.screenshotTask(app.taskId);
- mRecentsView.updateThumbnail(app.taskId, thumbnail);
+ TaskView taskView = mRecentsView.updateThumbnail(app.taskId, thumbnail);
+ if (taskView != null) {
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ ViewOnDrawExecutor executor = new ViewOnDrawExecutor() {
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (!isCompleted()) {
+ runAllTasks();
+ }
+ }
+ };
+ executor.attachTo(mLauncher, taskView,
+ false /* waitForLoadAnimation */);
+ executor.execute(finishTransitionRunnable);
+ finishTransitionPosted = true;
+ }
}
}
transaction.apply();
}
}
- mRecentsAnimationWrapper.finish(true /* toHome */);
-
- 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) {
- Runnable launchTaskRunnable = () -> taskView.launchTask(true);
- if (mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION)) {
- // Snap to the new page then launch it
- mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
- } else {
- // No need to move page, just launch task directly
- launchTaskRunnable.run();
- }
- break;
- }
- }
- } else if (mInteractionType == INTERACTION_QUICK_SCRUB) {
- if (mQuickScrubController != null) {
- if (mDeferredQuickScrubEnd) {
- onQuickScrubEnd();
- } else {
- mQuickScrubController.snapToPageForCurrentQuickScrubSection();
- }
- }
+ if (!finishTransitionPosted) {
+ // If we haven't posted the transition end runnable, run it now
+ finishTransitionRunnable.run();
}
+ doLogGesture(true /* toLauncher */);
}
private void setupLauncherUiAfterSwipeUpAnimation() {
// Re apply state in case we did something funky during the transition.
mLauncher.getStateManager().reapplyState();
- // Animate ui the first icon.
- View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
- if (currentRecentsPage instanceof TaskView) {
- ((TaskView) currentRecentsPage).animateIconToScale(1f);
- }
+ // Animate the first icon.
+ mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
+
+ reset();
}
public void onQuickScrubEnd() {
- if ((mStateCallback.getState() & STATE_SCALED_CONTROLLER_RECENTS) == 0) {
- // If we are still animating into recents, then defer until that has run to end
- // quick scrub since we need to finish the window animation before launching the next
- // task
- mDeferredQuickScrubEnd = true;
- return;
- }
+ setStateOnUiThread(STATE_QUICK_SCRUB_END);
+ }
- if (mQuickScrubController != null) {
- mQuickScrubController.onQuickScrubEnd();
- } else {
- // TODO:
- }
+ private void switchToFinalAppAfterQuickSwitch() {
+ mQuickScrubController.onQuickSwitch();
+ }
+
+ 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
@@ -700,14 +790,16 @@
}
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);
}
- private synchronized void debugNewState(int stateFlag) {
+ private void debugNewState(int stateFlag) {
if (!DEBUG_STATES) {
return;
}
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/values-af/strings.xml b/res/values-af/strings.xml
index 9d0158b..1ff9a2d 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 56742d3..67c26e7 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 17075ce..1b1d7ca 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -33,8 +33,8 @@
<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>
@@ -60,6 +60,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>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 1e42fe8..00f71d0 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 6fc1968..0ee8b3a 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index f9e73ca..2f5eac7 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 86a79b3..94b7f13 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index d7f61eb..d9c6033 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index fbbab0e..84b2b78 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 793b1c7..1ca68eb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 0d73f82..e5fde42 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 27d615a..97d8a26 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 8fec587..96acfba 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index a78b820..9328c5f 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 11389d4..d7ab7d2 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 11389d4..d7ab7d2 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 11389d4..d7ab7d2 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 5a13087..11cfa2a 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 1f1e3b7..39da94c 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 162da73..68a0429 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 3f137e4..27a56dd 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index f4b000d..128af90 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 6146ef9..f5b0817 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 43bcc5c..4093535 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 8994ef4..520ad78 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index f7b433b..9a842b1 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index c292826..e030659 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5282a93..aa1e8dd 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 42eb733..ec3c6b2 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index db39449..697153a 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 611a038..02a561f 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index ff49b49..d115c73 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index c3988f3..ba9a47d 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 174949e..6c98d3e 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index db156e1..43c8520 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 527cb95..36f3c5e 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 7e1752c..b6d7877 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index ef92146..6c4deec 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index e52e0b2..d588bc1 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 4bb0d22..a8f9784 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index a50a107..0ebbbe7 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index e2fde48..05652fc 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 3099d1f..e446e7c 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 5b2e951..2e37e98 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index eec27b1..3b96ffa 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 53944cb..3a0fc2b 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index c247c4e..84f769b 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 1deef00..5e91a46 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index c11883d..94b75d7 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index f88fe96..6e19421 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index f44f28c..3cf9208 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index ee374f8..2bdd6ce 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 8d6819a..d15e8b2 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a9123bd..38c24a5 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 602bc34..6b6c929 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 0db8d62..c90ba1e 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -60,6 +60,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>
@@ -131,7 +137,7 @@
<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="work_mode_on_label" msgid="4781128097185272916">"Profil zarządzany przez Twoją organizację"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index ca4a1c6..c370a8e 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 77cc50f..0686ae7 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 7b2409c..651b264 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 5242322..9f43c56 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -33,7 +33,7 @@
<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>
@@ -60,6 +60,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>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 31d26e4..bbcc257 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 9567889..a3bc4ec 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index ca59140..337cb2d 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 7f4c3bc..e79f0d4 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 6d757fd..7d5e28d 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index b61ed69..c886780 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 05864fa..8925c89 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -60,6 +60,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) -->
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index aafb982..fc68b82 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 83d32f2..fb8a71b 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 5006d2e..6f2bc71 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index de90f30..5aad29a 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index d8184e4..a0ebe6f0 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index eb0d2be..ffced57 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 1070d89..cd65738 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 54a4ec0..200c2c0 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 1bd7317..a5b4892 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3610ef0..71e06e7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 0895651..7f792f8 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 76fbdb1..b95e838 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 53dbdd6..65d7d4c 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -60,6 +60,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>
diff --git a/res/values/config.xml b/res/values/config.xml
index 3dddac2..065ad36 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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2318e9e..381830c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -334,7 +334,7 @@
<!-- 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>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 12f022f..fc5ce8f 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -92,7 +92,8 @@
public final void close(boolean animate) {
animate &= !Utilities.isPowerSaverOn(getContext());
handleClose(animate);
- Launcher.getLauncher(getContext()).getUserEventDispatcher().resetElapsedContainerMillis();
+ Launcher.getLauncher(getContext()).getUserEventDispatcher()
+ .resetElapsedContainerMillis("container closed");
}
protected abstract void handleClose(boolean animate);
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 4a0f52d..12db3b6 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -48,8 +48,7 @@
public final UserEventDispatcher getUserEventDispatcher() {
if (mUserEventDispatcher == null) {
- mUserEventDispatcher = UserEventDispatcher.newInstance(this,
- mDeviceProfile.isLandscape, isInMultiWindowModeCompat());
+ mUserEventDispatcher = UserEventDispatcher.newInstance(this, mDeviceProfile);
}
return mUserEventDispatcher;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 950c7f7..ba55b36 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -40,7 +40,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;
@@ -121,10 +123,11 @@
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,7 +217,8 @@
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);
}
public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
@@ -226,7 +230,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
@@ -288,7 +292,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}.
@@ -503,14 +507,10 @@
}
}
- 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);
+ return !isMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
}
private static Context getContext(Context c, int orientation) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index bd19dfa..c4ec8c9 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -29,6 +29,8 @@
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Property;
import android.util.SparseArray;
@@ -304,4 +306,30 @@
}
invalidateSelf();
}
+
+ @Override
+ public ConstantState getConstantState() {
+ return new MyConstantState(mBitmap, mIconColor);
+ }
+
+ private static class MyConstantState extends ConstantState {
+ private final Bitmap mBitmap;
+ private 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/IconCache.java b/src/com/android/launcher3/IconCache.java
index 957a5e5..d0581a2 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -785,7 +785,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/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 246fa74..e460911 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -162,9 +162,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
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 4d820bc..55bfef6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -39,6 +39,7 @@
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;
@@ -239,6 +240,7 @@
@Thunk boolean mWorkspaceLoading = true;
+ private OnStartCallback mOnStartCallback;
private OnResumeCallback mOnResumeCallback;
private ViewOnDrawExecutor mPendingExecutor;
@@ -294,10 +296,6 @@
}
TraceHelper.beginSection("Launcher-onCreate");
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.preOnCreate();
- }
-
WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
wallpaperColorInfo.setOnThemeChangeListener(this);
overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
@@ -307,12 +305,11 @@
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());
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -375,11 +372,7 @@
// 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);
-
+ updateRequestedOrientation();
setContentView(mLauncherView);
getRootView().dispatchInsets();
@@ -402,6 +395,13 @@
TraceHelper.endSection("Launcher-onCreate");
}
+ public void updateRequestedOrientation() {
+ // 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);
+ }
+
@Override
public void onConfigurationChanged(Configuration newConfig) {
int diff = newConfig.diff(mOldConfig);
@@ -434,6 +434,7 @@
display.getSize(mwSize);
mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
}
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
}
@Override
@@ -784,7 +785,7 @@
if (!mAppLaunchSuccess) {
getUserEventDispatcher().logActionCommand(Action.Command.STOP,
- mStateManager.getState().containerType);
+ mStateManager.getState().containerType, -1);
}
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
@@ -795,6 +796,10 @@
super.onStart();
FirstFrameAnimatorHelper.setIsVisible(true);
+ if (mOnStartCallback != null) {
+ mOnStartCallback.onLauncherStart(this);
+ mOnStartCallback = null;
+ }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStart();
}
@@ -859,6 +864,12 @@
}
}
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ mStateManager.onWindowFocusChanged();
+ }
+
public interface LauncherOverlay {
/**
@@ -1275,7 +1286,8 @@
} 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
@@ -1651,7 +1663,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
@@ -1677,7 +1690,7 @@
if (v instanceof Workspace) {
if (isInState(OVERVIEW)) {
- getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+ getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Touch.TAP,
LauncherLogProto.Action.Direction.NONE,
LauncherLogProto.ContainerType.OVERVIEW, mWorkspace.getCurrentPage());
mStateManager.goToState(NORMAL);
@@ -1688,7 +1701,7 @@
if (v instanceof CellLayout) {
if (isInState(OVERVIEW)) {
int page = mWorkspace.indexOfChild(v);
- getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Type.TOUCH,
+ getUserEventDispatcher().logActionOnContainer(LauncherLogProto.Action.Touch.TAP,
LauncherLogProto.Action.Direction.NONE,
LauncherLogProto.ContainerType.OVERVIEW, page);
mWorkspace.snapToPageFromOverView(page);
@@ -1852,6 +1865,18 @@
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);
+ }
+ }
startActivitySafely(v, intent, item);
}
@@ -1900,7 +1925,7 @@
intent.setSourceBounds(getViewBounds(v));
// If there is no target package, use the default intent chooser animation
launchOptions = hasTargetPackage
- ? getActivityLaunchOptions(v, isInMultiWindowModeCompat())
+ ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
: null;
} else {
launchOptions = null;
@@ -1955,8 +1980,13 @@
}
}
+ public Bundle getActivityLaunchOptionsAsBundle(View v, boolean useDefaultLaunchOptions) {
+ ActivityOptions activityOptions = getActivityLaunchOptions(v, useDefaultLaunchOptions);
+ return activityOptions == null ? null : activityOptions.toBundle();
+ }
+
@TargetApi(Build.VERSION_CODES.M)
- public Bundle getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
+ public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
return useDefaultLaunchOptions
? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v)
: mAppTransitionManager.getActivityLaunchOptions(this, v);
@@ -1975,18 +2005,12 @@
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())
+ ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
: null;
UserHandle user = item == null ? null : item.user;
@@ -1997,6 +2021,11 @@
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);
@@ -2159,6 +2188,10 @@
mOnResumeCallback = callback;
}
+ public void setOnStartCallback(OnStartCallback callback) {
+ mOnStartCallback = callback;
+ }
+
/**
* Implementation of the method from LauncherModel.Callbacks.
*/
@@ -2864,4 +2897,12 @@
void onLauncherResume();
}
+
+ /**
+ * Callback for listening for onStart
+ */
+ public interface OnStartCallback {
+
+ void onLauncherStart(Launcher launcher);
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 43d5e62..19fa3d4 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,23 +48,27 @@
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);
}
/** Cancels the current Launcher transition animation */
public void finishLauncherAnimation() {
}
+
+ public boolean isAnimating() {
+ // We don't know when the activity options are being used.
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index ed7bf3d..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();
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 929606e..6646b78 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -444,11 +444,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 +489,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..138ea0f 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -54,7 +54,6 @@
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;
@@ -116,11 +115,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;
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 137e4b1..2f8687d 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -158,6 +158,11 @@
private void goToState(LauncherState state, boolean animated, long delay,
Runnable onCompleteRunnable) {
+ goToState(state, animated, delay, -1, onCompleteRunnable);
+ }
+
+ public void goToState(LauncherState state, boolean animated, long delay, long overrideDuration,
+ Runnable onCompleteRunnable) {
if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null) {
// Run any queued runnable
if (onCompleteRunnable != null) {
@@ -170,6 +175,7 @@
mConfig.reset();
if (!animated) {
+ preOnStateTransitionStart();
onStateTransitionStart(state);
for (StateHandler handler : getStateHandlers()) {
handler.setState(state);
@@ -189,6 +195,9 @@
// Since state NORMAL can be reached from multiple states, just assume that the
// transition plays in reverse and use the same duration as previous state.
mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration;
+ if (overrideDuration > -1) {
+ mConfig.duration = overrideDuration;
+ }
AnimatorSet animation = createAnimationToNewWorkspaceInternal(
state, new AnimatorSetBuilder(), onCompleteRunnable);
@@ -223,6 +232,8 @@
protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
+ preOnStateTransitionStart();
+
for (StateHandler handler : getStateHandlers()) {
builder.startTag(handler);
handler.setStateWithAnimation(state, builder, mConfig);
@@ -261,6 +272,15 @@
return mConfig.mCurrentAnimation;
}
+ private void preOnStateTransitionStart() {
+ // If we are still animating to launcher from an app,
+ // finish it and let this state animation take over.
+ LauncherAppTransitionManager transitionManager = mLauncher.getAppTransitionManager();
+ if (transitionManager != null) {
+ transitionManager.finishLauncherAnimation();
+ }
+ }
+
private void onStateTransitionStart(LauncherState state) {
mState.onStateDisabled(mLauncher);
mState = state;
@@ -271,13 +291,6 @@
// Only disable clipping if needed, otherwise leave it as previous value.
mLauncher.getWorkspace().setClipChildren(false);
}
-
- // If we are still animating to launcher from an app,
- // finish it and let this state animation take over.
- LauncherAppTransitionManager transitionManager = mLauncher.getAppTransitionManager();
- if (transitionManager != null) {
- transitionManager.finishLauncherAnimation();
- }
}
private void onStateTransitionEnd(LauncherState state) {
@@ -289,12 +302,17 @@
state.onStateTransitionEnd(mLauncher);
mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
mLauncher.finishAutoCancelActionMode();
if (state == NORMAL) {
setRestState(null);
}
+
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ }
+
+ public void onWindowFocusChanged() {
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
}
public LauncherState getLastState() {
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 bb137b0..d6e5d18 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -59,14 +59,11 @@
* 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;
-
public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
@@ -154,7 +151,7 @@
// Page Indicator
@Thunk int mPageIndicatorViewId;
- protected PageIndicator mPageIndicator;
+ protected T mPageIndicator;
// Reordering
// We use the min scale to determine how much to expand the actually PagedView measured
@@ -224,7 +221,6 @@
mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
- setOnHierarchyChangeListener(this);
setWillNotDraw(false);
}
@@ -235,9 +231,9 @@
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());
}
}
@@ -282,7 +278,7 @@
}
}
- public PageIndicator getPageIndicator() {
+ public T getPageIndicator() {
return mPageIndicator;
}
@@ -384,7 +380,7 @@
private void updatePageIndicator() {
// Update the page indicator (when we aren't reordering)
if (mPageIndicator != null) {
- mPageIndicator.setContentDescription(getPageIndicatorDescription());
+ mPageIndicator.setPageDescription(getPageIndicatorDescription());
if (!isReordering(false)) {
mPageIndicator.setActiveMarker(getNextPage());
}
@@ -748,63 +744,24 @@
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) {
+ dispatchPageCountChanged();
+ }
+
+ @Override
+ public void onViewRemoved(View 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) {
@@ -1352,9 +1309,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
@@ -1932,6 +1887,14 @@
getNextPage() + 1, getChildCount());
}
+ protected float getDownMotionX() {
+ return mDownMotionX;
+ }
+
+ protected float getDownMotionY() {
+ return mDownMotionY;
+ }
+
@Override
public boolean onHoverEvent(android.view.MotionEvent event) {
return true;
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index d559b44..31dab97 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;
@@ -232,6 +233,19 @@
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();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2d24028..1414946 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -52,6 +52,7 @@
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;
@@ -75,6 +76,7 @@
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
+import com.android.launcher3.pageindicators.WorkspacePageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.uioverrides.UiFactory;
@@ -100,10 +102,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,6 +236,7 @@
boolean mStartedSendingScrollEvents;
float mLastOverlayScroll = 0;
boolean mOverlayShown = false;
+ private Runnable mOnOverlayHiddenCallback;
private boolean mForceDrawAdjacentPages = false;
private boolean mPageRearrangeEnabled = false;
@@ -273,9 +275,7 @@
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
- setOnHierarchyChangeListener(this);
setHapticFeedbackEnabled(false);
-
initWorkspace();
// Disable multitouch across the workspace/all apps/customize tray
@@ -463,7 +463,7 @@
}
@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.");
}
@@ -471,7 +471,7 @@
cl.setOnInterceptTouchListener(this);
cl.setClickable(true);
cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- super.onChildViewAdded(parent, child);
+ super.onViewAdded(child);
}
boolean isTouchActive() {
@@ -1165,6 +1165,7 @@
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
}
mOverlayShown = false;
+ tryRunOverlayCallback();
}
float offset = 0f;
float slip = 0f;
@@ -1188,6 +1189,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);
@@ -1625,7 +1679,7 @@
if (popupContainer != null) {
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
}
}
@@ -2662,10 +2716,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;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index d4277db..a7eae39 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -21,8 +21,6 @@
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.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
@@ -50,7 +48,6 @@
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;
@@ -59,12 +56,8 @@
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.BottomUserEducationView;
-import java.util.List;
-import java.util.Set;
-
/**
* The all apps view container.
*/
@@ -80,9 +73,8 @@
private SearchUiManager mSearchUiManager;
private View mSearchContainer;
- private InterceptingViewPager mViewPager;
+ private AllAppsPagedView mViewPager;
private FloatingHeaderView mHeader;
- private TabsPagerAdapter mTabsPagerAdapter;
private SpannableStringBuilder mSearchQueryBuilder = null;
@@ -184,7 +176,7 @@
}
public AllAppsRecyclerView getActiveRecyclerView() {
- if (!mUsingTabs || mViewPager.getCurrentItem() == 0) {
+ if (!mUsingTabs || mViewPager.getNextPage() == 0) {
return mAH[AdapterHolder.MAIN].recyclerView;
} else {
return mAH[AdapterHolder.WORK].recyclerView;
@@ -329,9 +321,8 @@
if (mUsingTabs) {
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
- setupWorkProfileTabs();
+ onTabChanged(mViewPager.getNextPage());
} else {
- mTabsPagerAdapter = null;
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
mAH[AdapterHolder.WORK].recyclerView = null;
}
@@ -355,50 +346,35 @@
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();
+ applyTouchDelegate();
+ 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));
+ if (pos == AdapterHolder.WORK) {
+ BottomUserEducationView.showIfNeeded(mLauncher);
+ }
}
public AlphabeticalAppsList getApps() {
@@ -519,37 +495,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/AllAppsPagedView.java b/src/com/android/launcher3/allapps/AllAppsPagedView.java
new file mode 100644
index 0000000..3b4450b
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsPagedView.java
@@ -0,0 +1,80 @@
+/*
+ * 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;
+import com.android.launcher3.R;
+
+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() {
+ return getResources().getString(
+ getNextPage() == 0 ? R.string.all_apps_personal_tab : R.string.all_apps_work_tab);
+ }
+
+ @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/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/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 0dcebe3..6078776 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,9 +45,19 @@
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);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 8abafb0..993663e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -522,7 +522,7 @@
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
}
};
anim.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index f4462aa..a468cb5 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -44,14 +44,14 @@
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.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 +89,6 @@
private Folder mFolder;
private PagedFolderKeyEventListener mKeyListener;
- private PageIndicator mPageIndicator;
-
public FolderPagedView(Context context, AttributeSet attrs) {
super(context, attrs);
InvariantDeviceProfile profile = LauncherAppState.getIDP(context);
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 00ee009..c608a23 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -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,11 +114,20 @@
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(int itemType) {
+ Target t = newTarget(Target.Type.ITEM);
+ t.itemType = itemType;
+ return t;
}
public static Target newItemTarget(View v) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 243dbea..627115d 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -27,11 +27,13 @@
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;
@@ -64,8 +66,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,8 +75,8 @@
}
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;
return ued;
}
@@ -158,9 +159,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();
@@ -176,19 +191,29 @@
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));
@@ -196,22 +221,39 @@
// 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 +274,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) {
@@ -257,7 +324,7 @@
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
@@ -290,9 +357,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 +394,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/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
index 24e5b9c..5acc790 100644
--- a/src/com/android/launcher3/model/LoaderResults.java
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -162,7 +162,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
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/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 114b2b8..cbdabf3 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,8 @@
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<>();
private SettingsObserver mNotificationBadgingObserver;
@@ -258,6 +260,48 @@
}
}
+ @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. */
public List<StatusBarNotification> getNotificationsForKeys(List<NotificationKeyData> keys) {
StatusBarNotification[] notifications = NotificationListener.this
@@ -295,20 +339,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 +355,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..4fc7d8a 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -41,7 +41,8 @@
*
* 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, OnClickListener, PageIndicator {
private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
@@ -185,14 +186,18 @@
}
@Override
- public void setActiveMarker(int activePage) {
+ public void setActiveMarker(int activePage) { }
+
+ @Override
+ public void setMarkersCount(int numMarkers) {
+ if (Float.compare(numMarkers, mNumPagesFloat) != 0) {
+ animateToNumPages(numMarkers);
+ }
}
@Override
- protected void onPageCountChanged() {
- if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
- animateToNumPages(mNumPages);
- }
+ public void setPageDescription(CharSequence description) {
+ setContentDescription(description);
}
public void setShouldAutoHide(boolean shouldAutoHide) {
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index d2bcd18..32fd063 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -82,7 +82,7 @@
@Override
public void onClick(View view) {
Rect sourceBounds = launcher.getViewBounds(view);
- Bundle opts = launcher.getActivityLaunchOptions(view, false);
+ Bundle opts = launcher.getActivityLaunchOptionsAsBundle(view, false);
InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts);
launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
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..d3c0fef 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);
- }
-
- 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 final void initWhenReady() {
+ sScheduler.schedule(this);
}
public void clearReference() {
- if (sPendingHandler.get() == this) {
- sPendingHandler.clear();
- }
+ sScheduler.clearReference(this);
}
public static boolean handleCreate(Launcher launcher, Intent intent) {
@@ -101,14 +82,53 @@
}
}
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 void clearReference(InternalStateHandler handler) {
+ if (mPendingHandler.get() == handler) {
+ mPendingHandler.clear();
+ }
+ }
+ }
}
\ No newline at end of file
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/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index bde9c0a..ac381cc 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -15,12 +15,16 @@
*/
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;
import android.util.Log;
import android.util.MutableLong;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
/**
@@ -31,7 +35,8 @@
*/
public class TraceHelper {
- private static final boolean ENABLED = FeatureFlags.IS_DOGFOOD_BUILD;
+ private static final boolean FORCE_LOG = Utilities.IS_DEBUG_DEVICE;
+ private static final boolean ENABLED = FORCE_LOG || FeatureFlags.IS_DOGFOOD_BUILD;
private static final boolean SYSTEM_TRACE = false;
private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
@@ -40,7 +45,7 @@
if (ENABLED) {
MutableLong time = sUpTimes.get(sectionName);
if (time == null) {
- time = new MutableLong(Log.isLoggable(sectionName, Log.VERBOSE) ? 0 : -1);
+ time = new MutableLong((FORCE_LOG || 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
index 735fb2f..a647378 100644
--- a/src/com/android/launcher3/util/VerticalSwipeController.java
+++ b/src/com/android/launcher3/util/VerticalSwipeController.java
@@ -62,7 +62,7 @@
private boolean mNoIntercept;
private AnimatorPlaybackController mCurrentAnimation;
- private LauncherState mToState;
+ protected LauncherState mToState;
private float mStartProgress;
// Ratio of transition process [0, 1] to drag displacement (px)
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_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
index 2d39505..4168e11 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
@@ -159,7 +159,7 @@
.setPackage(getContext().getPackageName());
intent.setSourceBounds(mLauncher.getViewBounds(v));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent, mLauncher.getActivityLaunchOptions(v, false));
+ getContext().startActivity(intent, mLauncher.getActivityLaunchOptionsAsBundle(v, false));
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 744125e..64a29ea 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,6 +16,7 @@
package com.android.launcher3.uioverrides;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import android.graphics.Bitmap;
@@ -61,4 +62,6 @@
}
public static void resetOverview(Launcher launcher) { }
+
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
}