Merge branch 'ub-launcher3-master' into master
Bug: 73327733
Test: manual test
Change-Id: Id2211340155ebea35a2adc04b6f41237911e9856
diff --git a/Android.mk b/Android.mk
index 8e7f1ac..5d3eb8a 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.
@@ -121,8 +125,6 @@
$(LOCAL_PATH)/AndroidManifest.xml \
$(LOCAL_PATH)/AndroidManifest-common.xml
-LOCAL_MANIFEST_FILE := go/AndroidManifest.xml
-
LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
include $(BUILD_PACKAGE)
diff --git a/build.gradle b/build.gradle
index 61c05e5..0030b8b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -13,12 +13,12 @@
apply plugin: 'com.google.protobuf'
android {
- compileSdkVersion 27
- buildToolsVersion '27.0.0'
+ compileSdkVersion 28
+ buildToolsVersion '28.0.0'
defaultConfig {
minSdkVersion 21
- targetSdkVersion 27
+ targetSdkVersion 28
versionCode 1
versionName "1.0"
@@ -102,7 +102,7 @@
jcenter()
}
-final String SUPPORT_LIBS_VERSION = '27.0.0-SNAPSHOT'
+final String SUPPORT_LIBS_VERSION = '28.0.0-SNAPSHOT'
dependencies {
compile "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
diff --git a/go/AndroidManifest.xml b/go/AndroidManifest.xml
deleted file mode 100644
index fbaf981..0000000
--- a/go/AndroidManifest.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.launcher3" >
-
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
-
- <application
- android:backupAgent="com.android.launcher3.LauncherBackupAgent"
- android:fullBackupOnly="true"
- android:fullBackupContent="@xml/backupscheme"
- android:hardwareAccelerated="true"
- android:icon="@drawable/ic_launcher_home"
- android:label="@string/derived_app_name"
- android:theme="@style/LauncherTheme"
- android:largeHeap="@bool/config_largeHeap"
- android:restoreAnyVersion="true"
- android:supportsRtl="true" >
-
- <!-- Activity for handling PinItemRequest. Only supports shortcuts -->
- <activity android:name="com.android.launcher3.dragndrop.AddItemActivity"
- android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
- android:excludeFromRecents="true"
- android:autoRemoveFromRecents="true"
- android:label="@string/action_add_to_workspace"
- tools:node="replace" >
- <intent-filter>
- <action android:name="android.content.pm.action.CONFIRM_PIN_SHORTCUT" />
- </intent-filter>
- </activity>
-
- </application>
-
-</manifest>
diff --git a/go/res/drawable/ic_widget.xml b/go/res/drawable/ic_widget.xml
deleted file mode 100644
index 5336876..0000000
--- a/go/res/drawable/ic_widget.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M3.9,18.35c2.5-2.49,5.78-3.64,10.14-3.64v3.05c0,0.47,0.57,0.71,0.9,0.37l5.74-5.74c0.41-0.41,0.41-1.08,0-1.49l-5.74-5.74
- c-0.33-0.33-0.9-0.1-0.9,0.37v2.95c-6.32,0.9-9.56,4.9-11.02,9.34C2.86,18.34,3.51,18.74,3.9,18.35z"/>
-</vector>
diff --git a/go/res/layout/widget_cell_content.xml b/go/res/layout/widget_cell_content.xml
deleted file mode 100644
index 49506d9..0000000
--- a/go/res/layout/widget_cell_content.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/widget_preview_label_vertical_padding"
- android:paddingBottom="@dimen/widget_preview_label_vertical_padding"
- android:paddingLeft="@dimen/widget_preview_label_horizontal_padding"
- android:paddingRight="@dimen/widget_preview_label_horizontal_padding"
- android:orientation="horizontal">
-
- <!-- The name of the widget. -->
- <TextView
- android:id="@+id/widget_name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:ellipsize="end"
- android:fadingEdge="horizontal"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center"
- android:singleLine="true"
- android:maxLines="1"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp" />
-
- <!-- The original dimensions of the widget (can't be the same text as above due to different
- style. -->
- <TextView
- android:id="@+id/widget_dims"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginStart="5dp"
- android:layout_marginLeft="5dp"
- android:visibility="gone"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="14sp"
- android:fontFamily="sans-serif-condensed"
- android:alpha="0.8" />
- </LinearLayout>
-
- <!-- The image of the widget. This view does not support padding. Any placement adjustment
- should be done using margins. -->
- <com.android.launcher3.widget.WidgetImageView
- android:id="@+id/widget_preview"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-</merge>
\ No newline at end of file
diff --git a/go/res/values-af/strings.xml b/go/res/values-af/strings.xml
deleted file mode 100644
index 10ac473..0000000
--- a/go/res/values-af/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Raak en hou om \'n kortpad op te tel."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dubbeltik en hou om \'n kortpad op te tel of gebruik gepasmaakte handelinge."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Kortpaaie"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-kortpaaie"</string>
-</resources>
diff --git a/go/res/values-am/strings.xml b/go/res/values-am/strings.xml
deleted file mode 100644
index b3b253f..0000000
--- a/go/res/values-am/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"አንድ አቋራጭ ለመውሰድ ነክተው ይያዙ"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"አንድ አቋራጭ ለመውሰድ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"አቋራጮች"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> አቋራጮች"</string>
-</resources>
diff --git a/go/res/values-ar/strings.xml b/go/res/values-ar/strings.xml
deleted file mode 100644
index 2b3b807..0000000
--- a/go/res/values-ar/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"المس مع الاستمرار لاختيار اختصار."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"يمكنك النقر نقرًا مزدوجًا مع الاستمرار لاختيار اختصار أو استخدام الإجراءات المخصصة."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"الاختصارات"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"اختصارات <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-az/strings.xml b/go/res/values-az/strings.xml
deleted file mode 100644
index c4b8cb7..0000000
--- a/go/res/values-az/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Qısayolu seçmək üçün toxunub saxlayın."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Qısayolu seçmək üçün iki dəfə basıb saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Qısayollar"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> qısayolları"</string>
-</resources>
diff --git a/go/res/values-b+sr+Latn/strings.xml b/go/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 0da5699..0000000
--- a/go/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i zadržite da biste izabrali prečicu."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i zadržite da biste izabrali prečicu ili koristite prilagođene radnje."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice za <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-be/strings.xml b/go/res/values-be/strings.xml
deleted file mode 100644
index 4189e35..0000000
--- a/go/res/values-be/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Дакраніцеся і ўтрымлiвайце ярлык, каб дадаць яго."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць ярлык або выкарыстоўваць спецыяльныя дзеянні."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Ярлыкі"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Ярлыкі <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-bg/strings.xml b/go/res/values-bg/strings.xml
deleted file mode 100644
index 1b85992..0000000
--- a/go/res/values-bg/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Докоснете и задръжте за избор на пряк път."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Докоснете двукратно и задръжте за избор на пряк път или използвайте персонализирани действия."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Преки пътища"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Преки пътища за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-bn/strings.xml b/go/res/values-bn/strings.xml
deleted file mode 100644
index c56c925..0000000
--- a/go/res/values-bn/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"কোনও শর্টকাট বেছে নিতে টাচ করে ধরে রাখুন।"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"কোনও শর্টকাট বেছে নিতে ডাবল ট্যাপ করে ধরে রাখুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন।"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"শর্টকাট"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> এর শর্টকাট"</string>
-</resources>
diff --git a/go/res/values-bs/strings.xml b/go/res/values-bs/strings.xml
deleted file mode 100644
index 7042468..0000000
--- a/go/res/values-bs/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i držite da uzmete prečicu."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i držite da uzmete prečicu ili koristite prilagođene akcije."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Prečice"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečice aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ca/strings.xml b/go/res/values-ca/strings.xml
deleted file mode 100644
index 3b5c3f7..0000000
--- a/go/res/values-ca/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén premuda una drecera per seleccionar-la."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Fes doble toc i mantén premut per seleccionar una drecera o per utilitzar accions personalitzades."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Dreceres"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Dreceres de l\'aplicació <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-cs/strings.xml b/go/res/values-cs/strings.xml
deleted file mode 100644
index e4018f2..0000000
--- a/go/res/values-cs/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Zkratku vyberete podržením."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvojitým klepnutím a podržením vyberte zkratku, případně použijte vlastní akce."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Zkratky"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Zkratky aplikace <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-da/strings.xml b/go/res/values-da/strings.xml
deleted file mode 100644
index 821d36a..0000000
--- a/go/res/values-da/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Hold en genvej nede for at samle den op."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tryk to gange, og hold en genvej nede for at samle den op og bruge tilpassede handlinger."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Genveje"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-genveje"</string>
-</resources>
diff --git a/go/res/values-de/strings.xml b/go/res/values-de/strings.xml
deleted file mode 100644
index 43a1b3a..0000000
--- a/go/res/values-de/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Doppeltippen und halten, um eine Verknüpfung auszuwählen."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Doppeltippen und halten, um eine Verknüpfung auszuwählen oder benutzerdefinierte Aktionen zu nutzen."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Verknüpfungen"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-Verknüpfungen"</string>
-</resources>
diff --git a/go/res/values-el/strings.xml b/go/res/values-el/strings.xml
deleted file mode 100644
index ae59907..0000000
--- a/go/res/values-el/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Αγγίξτε παρατεταμένα για να σηκώσετε μια συντόμευση."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Πατήσετε δύο φορές παρατεταμένα για να σηκώσετε μια συντόμευση ή για να χρησιμοποιήσετε προσαρμοσμένες ενέργειες."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Συντομεύσεις"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Συντομεύσεις <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-en-rAU/strings.xml b/go/res/values-en-rAU/strings.xml
deleted file mode 100644
index 2ee2c26..0000000
--- a/go/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Shortcuts"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shortcuts"</string>
-</resources>
diff --git a/go/res/values-en-rGB/strings.xml b/go/res/values-en-rGB/strings.xml
deleted file mode 100644
index 2ee2c26..0000000
--- a/go/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Shortcuts"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shortcuts"</string>
-</resources>
diff --git a/go/res/values-en-rIN/strings.xml b/go/res/values-en-rIN/strings.xml
deleted file mode 100644
index 2ee2c26..0000000
--- a/go/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Touch & hold to pick up a shortcut."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Double-tap & hold to pick up a shortcut or use custom actions."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Shortcuts"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shortcuts"</string>
-</resources>
diff --git a/go/res/values-es-rUS/strings.xml b/go/res/values-es-rUS/strings.xml
deleted file mode 100644
index 5212c03..0000000
--- a/go/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén presionado para elegir un acceso directo."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Presiona dos veces y mantén presionado para elegir un acceso directo o usar acciones personalizadas."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Accesos directos"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Accesos directos de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-es/strings.xml b/go/res/values-es/strings.xml
deleted file mode 100644
index 3ae4588..0000000
--- a/go/res/values-es/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén pulsado el acceso directo que quieras."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toca dos veces y mantén pulsado el acceso directo o utiliza acciones personalizadas."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Accesos directos"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Accesos directos de <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-et/strings.xml b/go/res/values-et/strings.xml
deleted file mode 100644
index 2513e65..0000000
--- a/go/res/values-et/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Otsetee valimiseks puudutage seda pikalt."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Topeltpuudutage ja hoidke otsetee valimiseks või kohandatud toimingute kasutamiseks."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Otseteed"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> otseteed"</string>
-</resources>
diff --git a/go/res/values-eu/strings.xml b/go/res/values-eu/strings.xml
deleted file mode 100644
index 9949ef0..0000000
--- a/go/res/values-eu/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Eduki sakatuta lasterbide bat aukeratzeko."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Sakatu birritan eta eduki sakatuta lasterbide bat aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Lasterbideak"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioaren lasterbidea"</string>
-</resources>
diff --git a/go/res/values-fa/strings.xml b/go/res/values-fa/strings.xml
deleted file mode 100644
index 8bc5256..0000000
--- a/go/res/values-fa/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"برای انتخاب یک میانبر، لمس کنید و نگهدارید."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"برای انتخاب یک میانبر، دو ضربه سریع بزنید و نگهدارید یا از اقدامهای سفارشی استفاده کنید."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"میانبرها"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"میانبرهای <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-fi/strings.xml b/go/res/values-fi/strings.xml
deleted file mode 100644
index da9b0e1..0000000
--- a/go/res/values-fi/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Valitse pikakuvake painamalla sitä pitkään."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Valitse pikakuvake tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla pitkään."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Pikakuvakkeet"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Kohteen <xliff:g id="NAME">%1$s</xliff:g> pikakuvakkeet"</string>
-</resources>
diff --git a/go/res/values-fr-rCA/strings.xml b/go/res/values-fr-rCA/strings.xml
deleted file mode 100644
index c7fd6d6..0000000
--- a/go/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Maintenez un doigt sur le raccourci pour l\'ajouter"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Touchez 2x un raccourci et maintenez doigt dessus pour l’aj. ou utiliser des actions personnalisées."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Raccourcis"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Raccourcis : <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-fr/strings.xml b/go/res/values-fr/strings.xml
deleted file mode 100644
index 238fe73..0000000
--- a/go/res/values-fr/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Appui prolongé pour sélectionner raccourci."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Appuyez 2 fois et maintenez pression pour sélectionner raccourci ou utilisez actions personnalisées."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Raccourcis"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Raccourcis <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-gl/strings.xml b/go/res/values-gl/strings.xml
deleted file mode 100644
index 31621d5..0000000
--- a/go/res/values-gl/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Mantén premido un atallo para seleccionalo."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toca dúas veces e mantén premido para seleccionar un atallo ou utiliza accións personalizadas."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Atallos"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atallos da aplicación <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-gu/strings.xml b/go/res/values-gu/strings.xml
deleted file mode 100644
index bdb549f..0000000
--- a/go/res/values-gu/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"એક શૉર્ટકટ ચૂંટવા ટૅપ કરી રાખો."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"એક શૉર્ટકટ ચૂંટવા અથવા કોઈ કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટૅપ કરી રાખો."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"શૉર્ટકટ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> શૉર્ટકટ"</string>
-</resources>
diff --git a/go/res/values-hi/strings.xml b/go/res/values-hi/strings.xml
deleted file mode 100644
index 2c1650a..0000000
--- a/go/res/values-hi/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"शॉर्टकट चुनने के लिए छूकर रखें."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"शॉर्टकट चुनने के लिए दो बार छूएं और कुछ देर दबाएं रखें या अपने मुताबिक कार्रवाइयों का इस्तेमाल करें."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"शॉर्टकट"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> शॉर्टकट"</string>
-</resources>
diff --git a/go/res/values-hr/strings.xml b/go/res/values-hr/strings.xml
deleted file mode 100644
index fccdeb5..0000000
--- a/go/res/values-hr/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dodirnite i zadržite kako biste podigli prečac."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvaput dodirnite i zadržite pritisak kako biste podigli prečac ili pokušajte prilagođenim radnjama."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Prečaci"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Prečaci za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-hu/strings.xml b/go/res/values-hu/strings.xml
deleted file mode 100644
index 369c227..0000000
--- a/go/res/values-hu/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Felvételhez tartsa nyomva a parancsikont."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Parancsikon felvételéhez koppintson rá duplán és tartsa nyomva, vagy használjon egyéni műveleteket."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Parancsikonok"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-parancsikonok"</string>
-</resources>
diff --git a/go/res/values-hy/strings.xml b/go/res/values-hy/strings.xml
deleted file mode 100644
index 4747f6d..0000000
--- a/go/res/values-hy/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար։"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Կրկնակի հպեք և պահեք՝ դյուրանցում ընտրելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Դյուրանցումներ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> դյուրանցումներ"</string>
-</resources>
diff --git a/go/res/values-in/strings.xml b/go/res/values-in/strings.xml
deleted file mode 100644
index c8b9da3..0000000
--- a/go/res/values-in/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tap lama untuk memilih pintasan."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tap dua kali & tahan untuk memilih pintasan atau menggunakan tindakan khusus."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Pintasan"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Pintasan <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-is/strings.xml b/go/res/values-is/strings.xml
deleted file mode 100644
index b8bb923..0000000
--- a/go/res/values-is/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Haltu fingri á flýtileið til að grípa hana."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ýttu tvisvar og haltu fingri á flýtileið til að grípa hana eða notaðu sérsniðnar aðgerðir."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Flýtileiðir"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> flýtileiðir"</string>
-</resources>
diff --git a/go/res/values-it/strings.xml b/go/res/values-it/strings.xml
deleted file mode 100644
index bc5d998..0000000
--- a/go/res/values-it/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tocca e tieni premuto per scegliere la scorciatoia"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tocca due volte e tieni premuto per scegliere una scorciatoia o per usare azioni personalizzate."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Scorciatoie"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Scorciatoie di <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-iw/strings.xml b/go/res/values-iw/strings.xml
deleted file mode 100644
index f541d4d..0000000
--- a/go/res/values-iw/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"כדי להוסיף קיצור דרך, מקישים עליו פעמיים ומחזיקים."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"כדי להוסיף קיצור דרך או להשתמש בפעולות מותאמות אישית, מקישים על קיצור הדרך פעמיים ומחזיקים."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"קיצורי דרך"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"קיצורי דרך לאפליקציה <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ja/strings.xml b/go/res/values-ja/strings.xml
deleted file mode 100644
index 8f02d7f..0000000
--- a/go/res/values-ja/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ショートカットを追加するには押し続けます。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ダブルタップ後に押し続けてショートカットを選択するか、カスタム操作を使用してください。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ショートカット"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"「<xliff:g id="NAME">%1$s</xliff:g>」のショートカット"</string>
-</resources>
diff --git a/go/res/values-ka/strings.xml b/go/res/values-ka/strings.xml
deleted file mode 100644
index 1b46534..0000000
--- a/go/res/values-ka/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"შეეხეთ და დააყოვნეთ მალსახმობის ასარჩევად."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ორმაგად შეეხეთ და გეჭიროთ მალსახმობის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"მალსახმობები"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-ის მალსახმობები"</string>
-</resources>
diff --git a/go/res/values-kk/strings.xml b/go/res/values-kk/strings.xml
deleted file mode 100644
index e909818..0000000
--- a/go/res/values-kk/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Таңбашаны таңдау үшін оны түртіп, ұстап тұрыңыз."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Таңбашаны таңдау немесе арнаулы әрекеттерді пайдалану үшін екі рет түртіп, ұстап тұрыңыз."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Таңбашалар"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> таңбаша"</string>
-</resources>
diff --git a/go/res/values-km/strings.xml b/go/res/values-km/strings.xml
deleted file mode 100644
index 40082a4..0000000
--- a/go/res/values-km/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ប៉ះ ហើយចុចឲ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ។"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ប៉ះពីរដង ហើយចុចឱ្យជាប់ដើម្បីរើសផ្លូវកាត់មួយ ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ផ្លូវកាត់"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ផ្លូវកាត់សម្រាប់ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-kn/strings.xml b/go/res/values-kn/strings.xml
deleted file mode 100644
index 9c121fd..0000000
--- a/go/res/values-kn/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ ಆರಿಸಲು ಹೋಲ್ಡ್ ಮಾಡಿ."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
-</resources>
diff --git a/go/res/values-ko/strings.xml b/go/res/values-ko/strings.xml
deleted file mode 100644
index 60f925e..0000000
--- a/go/res/values-ko/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"바로가기를 선택하려면 길게 터치하세요."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"바로가기를 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"바로가기"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 바로가기"</string>
-</resources>
diff --git a/go/res/values-ky/strings.xml b/go/res/values-ky/strings.xml
deleted file mode 100644
index 4c7e973..0000000
--- a/go/res/values-ky/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Кыска жолду тандоо үчүн басып туруңуз."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Кыска жолду тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Кыска жолдор"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> кыска жол"</string>
-</resources>
diff --git a/go/res/values-lo/strings.xml b/go/res/values-lo/strings.xml
deleted file mode 100644
index 7864884..0000000
--- a/go/res/values-lo/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ແຕະຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ແຕະສອງເທື່ອຄ້າງໄວ້ເພື່ອຮັບປຸ່ມລັດ ຫຼື ໃຊ້ຄຳສັ່ງແບບກຳນົດເອງ."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ປຸ່ມລັດ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ປຸ່ມລັດ <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-lt/strings.xml b/go/res/values-lt/strings.xml
deleted file mode 100644
index 8f49032..0000000
--- a/go/res/values-lt/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Dukart pal. ir palaik., kad pasir. spart. klav."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dukart palieskite ir palaikykite, kad pasirinkt. spartųjį klavišą ar naudotumėte tinkintus veiksmus."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Spartieji klavišai"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"„<xliff:g id="NAME">%1$s</xliff:g>“ spartieji klavišai"</string>
-</resources>
diff --git a/go/res/values-lv/strings.xml b/go/res/values-lv/strings.xml
deleted file mode 100644
index 04315eb..0000000
--- a/go/res/values-lv/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Lai izvēlētos saīsni, pieskarieties un turiet to."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Lai atlasītu saīsni, veiciet dubultskārienu uz tās un turiet to vai arī veiciet pielāgotas darbības."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Saīsnes"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Lietotnes <xliff:g id="NAME">%1$s</xliff:g> saīsnes"</string>
-</resources>
diff --git a/go/res/values-mk/strings.xml b/go/res/values-mk/strings.xml
deleted file mode 100644
index 52d66b5..0000000
--- a/go/res/values-mk/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Допрете двапати и задржете за да изберете кратенка."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Допрете двапати и задржете за да изберете кратенка или да користите приспособени дејства."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Кратенки"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Кратенки за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ml/strings.xml b/go/res/values-ml/strings.xml
deleted file mode 100644
index b3c12e1..0000000
--- a/go/res/values-ml/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ഒരു കുറുക്കുവഴി ചേർക്കുന്നതിന് അത് സ്പർശിച്ച് പിടിക്കുക."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"കുറുക്കുവഴികൾ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> കുറുക്കുവഴികൾ"</string>
-</resources>
diff --git a/go/res/values-mn/strings.xml b/go/res/values-mn/strings.xml
deleted file mode 100644
index c89dfd1..0000000
--- a/go/res/values-mn/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Товчлол авах бол удаан дарна уу."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Товчлол авах болон тохируулсан үйлдлийг ашиглахын тулд хоёр товшоод хүлээнэ үү."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Товчлол"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-н товчлол"</string>
-</resources>
diff --git a/go/res/values-mr/strings.xml b/go/res/values-mr/strings.xml
deleted file mode 100644
index 2c767b4..0000000
--- a/go/res/values-mr/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"शॉर्टकट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"शॉर्टकट निवडण्यासाठी किंवा कस्टम क्रिया वापरण्यासाठी दोनदा टॅप करा आणि धरून ठेवा."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"शॉर्टकट"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> शॉर्टकट"</string>
-</resources>
diff --git a/go/res/values-ms/strings.xml b/go/res/values-ms/strings.xml
deleted file mode 100644
index 42add9a..0000000
--- a/go/res/values-ms/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Sentuh & tahan untuk mengambil pintasan."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ketik dua kali & tahan untuk mengambil pintasan atau menggunakan tindakan tersuai."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Pintasan"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Pintasan <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-my/strings.xml b/go/res/values-my/strings.xml
deleted file mode 100644
index 5784df6..0000000
--- a/go/res/values-my/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"လက်ကွက်ဖြတ်လမ်းတစ်ခုကို ရွေးရန် ထိပြီး ဖိထားပါ"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"လက်ကွက်ဖြတ်လမ်းကို ရွေးရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန်နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ဖြတ်လမ်းလင့်ခ်များ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ဖြတ်လမ်းလင့်ခ်များ"</string>
-</resources>
diff --git a/go/res/values-ne/strings.xml b/go/res/values-ne/strings.xml
deleted file mode 100644
index 0be0375..0000000
--- a/go/res/values-ne/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"कुनै सटकर्ट छनौट गर्न छोइराख्नुहोस्।"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"कुनै सर्टकट छनौट गर्न दुईपटक ट्याप गरेर होल्ड गर्नुहोस् वा रोजेका कारबाहीहरू प्रयोग गर्नुहोस्।"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"सर्टकटहरू"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> सर्टकटहरू"</string>
-</resources>
diff --git a/go/res/values-nl/strings.xml b/go/res/values-nl/strings.xml
deleted file mode 100644
index 5bcd016..0000000
--- a/go/res/values-nl/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tik en houd vast om snelkoppeling toe te voegen."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dubbeltik en houd vast om een snelkoppeling toe te voegen of aangepaste acties te gebruiken."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Snelkoppelingen"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snelkoppelingen"</string>
-</resources>
diff --git a/go/res/values-pa/strings.xml b/go/res/values-pa/strings.xml
deleted file mode 100644
index c7e4abf..0000000
--- a/go/res/values-pa/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ।"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"ਕੋਈ ਸ਼ਾਰਟਕੱਟ ਚੁਣਨ ਲਈ ਦੋ ਵਾਰ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾ ਕੇ ਰੱਖੋ ਜਾਂ ਵਿਉਂਂਤੀ ਕਾਰਵਾਈਆਂ ਵਰਤੋ।"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ਸ਼ਾਰਟਕੱਟ"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ਸ਼ਾਰਟਕੱਟ"</string>
-</resources>
diff --git a/go/res/values-pl/strings.xml b/go/res/values-pl/strings.xml
deleted file mode 100644
index 45a1dc2..0000000
--- a/go/res/values-pl/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Kliknij i przytrzymaj, by wybrać skrót."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Kliknij dwukrotnie i przytrzymaj, by wybrać skrót lub użyć działań niestandardowych."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Skróty"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> – skróty"</string>
-</resources>
diff --git a/go/res/values-pt-rPT/strings.xml b/go/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 7a75a05..0000000
--- a/go/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Toque sem soltar para escolher um atalho."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toque duas vezes sem soltar para escolher um atalho ou utilize ações personalizadas."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Atalhos"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atalhos da aplicação <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-pt/strings.xml b/go/res/values-pt/strings.xml
deleted file mode 100644
index 53bbfc4..0000000
--- a/go/res/values-pt/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Toque e segure para selecionar um atalho."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Toque duas vezes na tela e segure para selecionar um atalho ou usar ações personalizadas."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Atalhos"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Atalhos do app <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ro/strings.xml b/go/res/values-ro/strings.xml
deleted file mode 100644
index 75d1796..0000000
--- a/go/res/values-ro/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Atingeți și țineți apăsat pentru a selecta o comandă rapidă."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Atingeți de două ori și țineți apăsat pentru comandă rapidă sau folosiți acțiuni personalizate."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Comenzi rapide"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Comenzi rapide pentru <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ru/strings.xml b/go/res/values-ru/strings.xml
deleted file mode 100644
index 9c5c8cd..0000000
--- a/go/res/values-ru/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Чтобы выбрать ярлык, нажмите на него и удерживайте."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Чтобы выбрать ярлык или использовать специальные действия, нажмите на него дважды и не отпускайте."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Ярлыки"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>: ярлыки"</string>
-</resources>
diff --git a/go/res/values-si/strings.xml b/go/res/values-si/strings.xml
deleted file mode 100644
index 4b25c90..0000000
--- a/go/res/values-si/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"කෙටි මගක් තෝරා ගැනීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"විජට් එකක් තෝරා ගැනීමට හෝ අභිරුචි භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"කෙටි මං"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"කෙටි මං <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sk/strings.xml b/go/res/values-sk/strings.xml
deleted file mode 100644
index fc02933..0000000
--- a/go/res/values-sk/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Skratku pridáte pridržaním."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Skratku pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Skratky"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Skratky aplikácie <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sl/strings.xml b/go/res/values-sl/strings.xml
deleted file mode 100644
index 6ecedfb..0000000
--- a/go/res/values-sl/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Pridržite bližnjico, da jo izberete."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dvakrat se dotaknite bližnjice in jo pridržite, da jo izberete, ali pa uporabite dejanja po meri."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Bližnjice"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Bližnjice za <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sq/strings.xml b/go/res/values-sq/strings.xml
deleted file mode 100644
index bb74db6..0000000
--- a/go/res/values-sq/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Prek dhe mbaj prekur për të zgjedhur një shkurtore."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Prek dy herë dhe mbaj prekur për të zgjedhur një shkurtore ose për të përdorur veprimet e personalizuara."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Shkurtoret"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> shkurtore"</string>
-</resources>
diff --git a/go/res/values-sr/strings.xml b/go/res/values-sr/strings.xml
deleted file mode 100644
index 0b9aea2..0000000
--- a/go/res/values-sr/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Додирните и задржите да бисте изабрали пречицу."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Двапут додирните и задржите да бисте изабрали пречицу или користите прилагођене радње."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Пречице"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Пречице за <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sv/strings.xml b/go/res/values-sv/strings.xml
deleted file mode 100644
index c3f434c..0000000
--- a/go/res/values-sv/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Tryck länge om du vill ta upp en genväg."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Tryck snabbt två gånger och håll kvar om du vill ta upp en genväg eller använda anpassade åtgärder."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Genvägar"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Genvägar för <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-sw/strings.xml b/go/res/values-sw/strings.xml
deleted file mode 100644
index 13c12e4..0000000
--- a/go/res/values-sw/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Gusa na ushikilie ili uchague njia ya mkato."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Gusa mara mbili na ushikilie ile uchague njia ya mkato au utumie vitendo maalum."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Njia za mkato"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Njia za mkato za <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ta/strings.xml b/go/res/values-ta/strings.xml
deleted file mode 100644
index 50059b6..0000000
--- a/go/res/values-ta/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"குறுக்குவழியைச் சேர்க்க, தொட்டு பிடித்திருக்கவும்."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"குறுக்குவழியை சேர்க்க, இருமுறை தட்டிப் பிடித்திருக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"குறுக்குவழிகள்"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> குறுக்குவழிகள்"</string>
-</resources>
diff --git a/go/res/values-te/strings.xml b/go/res/values-te/strings.xml
deleted file mode 100644
index 0bdf743..0000000
--- a/go/res/values-te/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"సత్వరమార్గాన్ని ఎంచుకోవడానికి తాకి & నొక్కి ఉంచండి."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"సత్వరమార్గాన్ని ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి &ఉంచండి."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"సత్వరమార్గాలు"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> సత్వరమార్గాలు"</string>
-</resources>
diff --git a/go/res/values-th/strings.xml b/go/res/values-th/strings.xml
deleted file mode 100644
index e73d89f..0000000
--- a/go/res/values-th/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"แตะค้างไว้เพื่อเลือกทางลัด"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"แตะสองครั้งค้างไว้เพื่อเลือกทางลัดหรือใช้การกระทำที่กำหนดเอง"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"ทางลัด"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"ทางลัด <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-tl/strings.xml b/go/res/values-tl/strings.xml
deleted file mode 100644
index 8f44ec5..0000000
--- a/go/res/values-tl/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Pindutin nang matagal upang kumuha ng shortcut."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"I-double tap nang matagal upang kumuha ng shortcut o gumamit ng mga custom na pagkilos."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Mga Shortcut"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Mga shortcut sa <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-tr/strings.xml b/go/res/values-tr/strings.xml
deleted file mode 100644
index f0f3cce..0000000
--- a/go/res/values-tr/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Kısayol seçmek için dokunun ve basılı tutun."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Bir kısayolu seçmek veya özel işlemleri kullanmak için iki kez dokunun ve basılı tutun."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Kısayollar"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> kısayolları"</string>
-</resources>
diff --git a/go/res/values-uk/strings.xml b/go/res/values-uk/strings.xml
deleted file mode 100644
index 8d1f583..0000000
--- a/go/res/values-uk/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Натисніть і утримуйте, щоб вибрати ярлик."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Двічі натисніть і утримуйте, щоб вибрати ярлик, або виконайте іншу дію."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Ярлики"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Ярлики додатка <xliff:g id="NAME">%1$s</xliff:g>"</string>
-</resources>
diff --git a/go/res/values-ur/strings.xml b/go/res/values-ur/strings.xml
deleted file mode 100644
index 46bd823..0000000
--- a/go/res/values-ur/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"کوئی شارٹ کٹ منتخب کرنے کیلئے ٹچ کریں اور دبائے رکھیں۔"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"کوئی شارٹ کٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور دبائے رکھیں۔"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"شارٹ کٹس"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> شارٹ کٹس"</string>
-</resources>
diff --git a/go/res/values-uz/strings.xml b/go/res/values-uz/strings.xml
deleted file mode 100644
index 318bc15..0000000
--- a/go/res/values-uz/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Yorliqni tanlab olish uchun bosib turing."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Ikki marta bosib va bosib turgan holatda yorliqni tanlang yoki maxsus amaldan foydalaning."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Yorliqlar"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi yorliqlari"</string>
-</resources>
diff --git a/go/res/values-zh-rCN/strings.xml b/go/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 57351d3..0000000
--- a/go/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"触摸并按住快捷方式即可选择快捷方式。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"点按两次并按住快捷方式即可选择快捷方式,您也可以使用自定义操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"快捷方式"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>快捷方式"</string>
-</resources>
diff --git a/go/res/values-zh-rTW/strings.xml b/go/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 07ae2ed..0000000
--- a/go/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"輕觸兩下並按住捷徑即可選取,你也可以使用自訂動作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"「<xliff:g id="NAME">%1$s</xliff:g>」捷徑"</string>
-</resources>
diff --git a/go/res/values-zu/strings.xml b/go/res/values-zu/strings.xml
deleted file mode 100644
index e5051df..0000000
--- a/go/res/values-zu/strings.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Thinta futhi ubambe ukuze ukhethe isinqamuleli."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Thepha kabulu futhi ubambe ukuze ukhethe isinqamuleli noma usebenzise izenzo zangokwezifiso."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Izinqamuleli"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> izinqamuleli"</string>
-</resources>
diff --git a/go/res/values/strings.xml b/go/res/values/strings.xml
deleted file mode 100644
index 8ef2e62..0000000
--- a/go/res/values/strings.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2017 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
- <!-- Message to tell the user to press and hold on a shortcut to add it [CHAR_LIMIT=50] -->
- <string name="long_press_widget_to_add">Touch & hold to pick up a shortcut.</string>
- <!-- Accessibility spoken hint message in widget picker, which allows user to add a shortcut. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
- <string name="long_accessible_way_to_add">Double-tap & hold to pick up a shortcut or use custom actions.</string>
- <!-- Text for shortcut add button -->
- <string name="widget_button_text">Shortcuts</string>
-
- <!-- Strings for widgets & more in the popup container/bottom sheet -->
- <!-- Title for a bottom sheet that shows shortcuts for a particular app -->
- <string name="widgets_bottom_sheet_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> shortcuts</string>
-
-</resources>
diff --git a/go/res/xml/device_profiles.xml b/go/res/xml/device_profiles.xml
index 487c026..16d7e13 100644
--- a/go/res/xml/device_profiles.xml
+++ b/go/res/xml/device_profiles.xml
@@ -15,7 +15,7 @@
limitations under the License.
-->
-<profiles xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+<profiles xmlns:launcher="http://schemas.android.com/apk/res-auto" >
<profile
launcher:name="Go Device"
diff --git a/go/src_flags/com/android/launcher3/config/FeatureFlags.java b/go/src_flags/com/android/launcher3/config/FeatureFlags.java
index b11bb7c..f324fcd 100644
--- a/go/src_flags/com/android/launcher3/config/FeatureFlags.java
+++ b/go/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -24,6 +24,5 @@
private FeatureFlags() {}
// Features to control Launcher3Go behavior
- public static final boolean GO_DISABLE_WIDGETS = true;
public static final boolean LAUNCHER3_SPRING_ICONS = false;
}
diff --git a/proguard.flags b/proguard.flags
index a315cdc..b8cade5 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -97,6 +97,11 @@
# support jar.
-keep class android.support.v7.widget.RecyclerView { *; }
+# LauncherAppTransitionManager
+-keep class com.android.launcher3.LauncherAppTransitionManagerImpl {
+ public <init>(...);
+}
+
-keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** {
*;
}
@@ -104,3 +109,8 @@
-keep interface com.android.launcher3.model.nano.LauncherDumpProto.** {
*;
}
+
+# BUG(70852369): Surpress additional warnings after changing from Proguard to R8
+-dontwarn android.app.**
+-dontwarn android.view.**
+-dontwarn android.os.**
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/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 08c740c..02b4379 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -23,6 +23,7 @@
<uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+ <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
android:fullBackupOnly="true"
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index fa0e0f3..d5859a7 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png
new file mode 100644
index 0000000..d4f995d
--- /dev/null
+++ b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_left.png
Binary files differ
diff --git a/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png
new file mode 100644
index 0000000..9013c1a
--- /dev/null
+++ b/quickstep/res/drawable-hdpi/recents_horizontal_scrim_right.png
Binary files differ
diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png
new file mode 100644
index 0000000..6e924ec
--- /dev/null
+++ b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_left.png
Binary files differ
diff --git a/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png
new file mode 100644
index 0000000..33d0edd
--- /dev/null
+++ b/quickstep/res/drawable-mdpi/recents_horizontal_scrim_right.png
Binary files differ
diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png
new file mode 100644
index 0000000..20c85e6
--- /dev/null
+++ b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_left.png
Binary files differ
diff --git a/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png
new file mode 100644
index 0000000..58a1ca0
--- /dev/null
+++ b/quickstep/res/drawable-xhdpi/recents_horizontal_scrim_right.png
Binary files differ
diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png
new file mode 100644
index 0000000..9d3dc31
--- /dev/null
+++ b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_left.png
Binary files differ
diff --git a/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png
new file mode 100644
index 0000000..7fb248b
--- /dev/null
+++ b/quickstep/res/drawable-xxhdpi/recents_horizontal_scrim_right.png
Binary files differ
diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png
new file mode 100644
index 0000000..49ec7aa
--- /dev/null
+++ b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_left.png
Binary files differ
diff --git a/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png
new file mode 100644
index 0000000..a8922e2
--- /dev/null
+++ b/quickstep/res/drawable-xxxhdpi/recents_horizontal_scrim_right.png
Binary files differ
diff --git a/quickstep/res/drawable/bg_workspace_card_button.xml b/quickstep/res/drawable/bg_workspace_card_button.xml
new file mode 100644
index 0000000..3ba47bb
--- /dev/null
+++ b/quickstep/res/drawable/bg_workspace_card_button.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <color android:color="#ffffffff" />
+ </item>
+
+ <item>
+ <color android:color="#77FFFFFF" />
+ </item>
+</ripple>
diff --git a/quickstep/res/drawable/ic_pin.xml b/quickstep/res/drawable/ic_pin.xml
new file mode 100644
index 0000000..8c799e3
--- /dev/null
+++ b/quickstep/res/drawable/ic_pin.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+
+ <path
+ android:fillColor="#FFffffff"
+ android:pathData="M16,12L16,4l1,0L17,2L7,2l0,2l1,0l0,8l-2,2l0,2l5.2,0l0,6l1.6,0l0,-6L18,16l0,-2L16,12z"/>
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_split_screen.xml b/quickstep/res/drawable/ic_split_screen.xml
new file mode 100644
index 0000000..77bd333
--- /dev/null
+++ b/quickstep/res/drawable/ic_split_screen.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="88.0dp"
+ android:height="88.0dp"
+ android:viewportWidth="88.0"
+ android:viewportHeight="88.0" >
+
+ <path
+ android:pathData="M 32,11
+ C 32,11 68,11 68,11
+ 76.74,11.06 76.98,12.76 77,21
+ 77.01,25.97 78.50,38.23 73.85,40.98
+ 71.80,42.19 68.35,42 66,42
+ 66,42 22,42 22,42
+ 18.82,41.99 14.87,42.38 12.60,39.69
+ 10.71,37.44 11.01,33.77 11,31
+ 10.99,25.54 9.53,16.08 13.31,12.02
+ 18.07,10.21 26.66,11 32,11 Z
+ M 32,46
+ C 32,46 68,46 68,46
+ 76.74,46.06 76.98,47.76 77,56
+ 77.01,60.97 78.50,73.23 73.85,75.98
+ 71.80,77.19 68.35,77 66,77
+ 66,77 22,77 22,77
+ 18.82,76.99 14.87,77.38 12.60,74.69
+ 10.71,72.44 11.01,68.77 11,66
+ 10.99,60.54 9.53,51.08 13.31,47.02
+ 18.07,45.21 26.66,46 32,46 Z"
+ android:fillColor="@android:color/white" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/layout/longpress_options_menu.xml b/quickstep/res/layout/longpress_options_menu.xml
new file mode 100644
index 0000000..9cf0fcf
--- /dev/null
+++ b/quickstep/res/layout/longpress_options_menu.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.uioverrides.OptionsPopupView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="96dp"
+ android:background="?attr/popupColorPrimary"
+ android:elevation="@dimen/deep_shortcuts_elevation"
+ android:orientation="horizontal"
+ launcher:layout_ignoreInsets="true">
+
+ <FrameLayout
+ android:id="@+id/wallpaper_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawablePadding="4dp"
+ android:drawableTint="?android:attr/textColorPrimary"
+ android:drawableTop="@drawable/ic_wallpaper"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/wallpaper_button_text"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/widget_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawablePadding="4dp"
+ android:drawableTint="?android:attr/textColorPrimary"
+ android:drawableTop="@drawable/ic_widget"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/widget_button_text"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"/>
+
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/settings_button"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:drawablePadding="4dp"
+ android:drawableTint="?android:attr/textColorPrimary"
+ android:drawableTop="@drawable/ic_setting"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:text="@string/settings_button_text"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="12sp"/>
+
+ </FrameLayout>
+
+</com.android.launcher3.uioverrides.OptionsPopupView>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index 22f014a..9f4f8a1 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -22,4 +22,10 @@
android:clipChildren="false"
android:clipToPadding="false"
android:alpha="0.0"
- android:visibility="invisible" />
\ No newline at end of file
+ android:visibility="invisible" >
+
+ <com.android.launcher3.uioverrides.WorkspaceCard
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</com.android.quickstep.RecentsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task.xml b/quickstep/res/layout/task.xml
index 839d934..91b6aa3 100644
--- a/quickstep/res/layout/task.xml
+++ b/quickstep/res/layout/task.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?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");
@@ -14,23 +13,20 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.quickstep.TaskView
- xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.quickstep.TaskView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:elevation="4dp">
<com.android.quickstep.TaskThumbnailView
android:id="@+id/snapshot"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_marginTop="@dimen/task_thumbnail_top_margin"
- android:scaleType="matrix"
- android:background="@drawable/task_thumbnail_background"
- android:elevation="4dp" />
+ android:layout_marginTop="@dimen/task_thumbnail_top_margin" />
+
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/task_thumbnail_icon_size"
android:layout_height="@dimen/task_thumbnail_icon_size"
- android:layout_gravity="top|center_horizontal"
- android:elevation="5dp"/>
+ android:layout_gravity="top|center_horizontal" />
</com.android.quickstep.TaskView>
\ No newline at end of file
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
new file mode 100644
index 0000000..6e3fb4f
--- /dev/null
+++ b/quickstep/res/layout/task_menu.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.quickstep.TaskMenuView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/bg_popup_item_width"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:elevation="@dimen/deep_shortcuts_elevation"
+ android:orientation="vertical"
+ android:background="?attr/popupColorPrimary"
+ android:divider="@drawable/all_apps_divider"
+ android:showDividers="middle"
+ android:animateLayoutChanges="true">
+ <TextView
+ android:id="@+id/task_icon_and_name"
+ android:layout_width="match_parent"
+ android:layout_height="112dp"
+ android:textSize="14sp"
+ android:paddingTop="18dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_horizontal"/>
+</com.android.quickstep.TaskMenuView>
\ No newline at end of file
diff --git a/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-af/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-af/strings.xml
index dea7749..b4c735b 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-am/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-am/strings.xml
index dea7749..62d54db 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ar/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ar/strings.xml
index dea7749..de210b7 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-az/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-az/strings.xml
index dea7749..91c5179 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-b+sr+Latn/strings.xml
index dea7749..a4eb3e8 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-be/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-be/strings.xml
index dea7749..f798802 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-bg/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-bg/strings.xml
index dea7749..de88634 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-nb/strings.xml b/quickstep/res/values-bn/strings.xml
similarity index 61%
copy from go/res/values-nb/strings.xml
copy to quickstep/res/values-bn/strings.xml
index 2a5ffb6..028a475 100644
--- a/go/res/values-nb/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Trykk og hold for å velge en snarvei."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Snarveier"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snarveier"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-bs/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-bs/strings.xml
index dea7749..16b72ea 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ca/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ca/strings.xml
index dea7749..6d65fd7 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-cs/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-cs/strings.xml
index dea7749..b6894c0 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-da/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-da/strings.xml
index dea7749..ff2d25d 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-de/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-de/strings.xml
index dea7749..e5242c7 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-el/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-el/strings.xml
index dea7749..9d7023d 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-en-rAU/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-en-rAU/strings.xml
index dea7749..694e73a 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-en-rGB/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-en-rGB/strings.xml
index dea7749..694e73a 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-en-rIN/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-en-rIN/strings.xml
index dea7749..694e73a 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-es-rUS/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-es-rUS/strings.xml
index dea7749..37a3168 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-es/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-es/strings.xml
index dea7749..60c87d3 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-et/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-et/strings.xml
index dea7749..33d1cba 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-eu/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-eu/strings.xml
index dea7749..d0e86c2 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-fa/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-fa/strings.xml
index dea7749..198f33d 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-fi/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-fi/strings.xml
index dea7749..f244586 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-fr-rCA/strings.xml
index dea7749..d9b2a51 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-fr/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-fr/strings.xml
index dea7749..8c1a5d2 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-gl/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-gl/strings.xml
index dea7749..d6f26ac 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-gu/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-gu/strings.xml
index dea7749..8c779d6 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-hr/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-hr/strings.xml
index dea7749..eaa4416 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-hu/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-hu/strings.xml
index dea7749..b72c760 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-hy/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-hy/strings.xml
index dea7749..632ea3e 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-in/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-in/strings.xml
index dea7749..d731bce 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-is/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-is/strings.xml
index dea7749..d4d54d3 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-it/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-it/strings.xml
index dea7749..6223ddb 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-iw/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-iw/strings.xml
index dea7749..1da258a 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ja/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ja/strings.xml
index dea7749..bee863f 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ka/strings.xml
similarity index 62%
rename from go/res/values-zh-rHK/strings.xml
rename to quickstep/res/values-ka/strings.xml
index dea7749..4488cf8 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-kk/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-kk/strings.xml
index dea7749..8c82ef1 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-km/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-km/strings.xml
index dea7749..64136c9 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-nb/strings.xml b/quickstep/res/values-kn/strings.xml
similarity index 61%
copy from go/res/values-nb/strings.xml
copy to quickstep/res/values-kn/strings.xml
index 2a5ffb6..7f12dee 100644
--- a/go/res/values-nb/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Trykk og hold for å velge en snarvei."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Snarveier"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snarveier"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ko/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ko/strings.xml
index dea7749..39fdf33 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ky/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ky/strings.xml
index dea7749..2602b51 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-lo/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-lo/strings.xml
index dea7749..7ba29d2 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-lt/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-lt/strings.xml
index dea7749..2252381 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-lv/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-lv/strings.xml
index dea7749..d5f5e9c 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-mk/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-mk/strings.xml
index dea7749..6458aa0 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-nb/strings.xml b/quickstep/res/values-ml/strings.xml
similarity index 61%
copy from go/res/values-nb/strings.xml
copy to quickstep/res/values-ml/strings.xml
index 2a5ffb6..4d2745a 100644
--- a/go/res/values-nb/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Trykk og hold for å velge en snarvei."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Snarveier"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snarveier"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-mn/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-mn/strings.xml
index dea7749..4aec257 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-mr/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-mr/strings.xml
index dea7749..3f85d77 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ms/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ms/strings.xml
index dea7749..80e942c 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-vi/strings.xml b/quickstep/res/values-my/strings.xml
similarity index 60%
rename from go/res/values-vi/strings.xml
rename to quickstep/res/values-my/strings.xml
index 1197619..b6b0300 100644
--- a/go/res/values-vi/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Chạm và giữ để chọn lối tắt."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng hành động tùy chỉnh."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Lối tắt"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Lối tắt <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-nb/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-nb/strings.xml
index dea7749..4dc144c 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-nb/strings.xml b/quickstep/res/values-ne/strings.xml
similarity index 61%
rename from go/res/values-nb/strings.xml
rename to quickstep/res/values-ne/strings.xml
index 2a5ffb6..a171b73 100644
--- a/go/res/values-nb/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Trykk og hold for å velge en snarvei."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Dobbelttrykk og hold for å velge en snarvei eller bruke tilpassede handlinger."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Snarveier"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g>-snarveier"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-nl/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-nl/strings.xml
index dea7749..4acddc5 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-pa/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-pa/strings.xml
index dea7749..6bc1876 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-pl/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-pl/strings.xml
index dea7749..174a96a 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-pt-rPT/strings.xml
index dea7749..c3df4f2 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-pt/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-pt/strings.xml
index dea7749..9730b98 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ro/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ro/strings.xml
index dea7749..46671e8 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ru/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ru/strings.xml
index dea7749..c2edf75 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-si/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-si/strings.xml
index dea7749..7702543 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-sk/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-sk/strings.xml
index dea7749..84e7793 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-sl/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-sl/strings.xml
index dea7749..c01fcd4 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-sq/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-sq/strings.xml
index dea7749..ae6e6bc 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-sr/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-sr/strings.xml
index dea7749..84debfd 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-sv/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-sv/strings.xml
index dea7749..495bca7 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-sw/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-sw/strings.xml
index dea7749..63594f2 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-vi/strings.xml b/quickstep/res/values-ta/strings.xml
similarity index 60%
copy from go/res/values-vi/strings.xml
copy to quickstep/res/values-ta/strings.xml
index 1197619..2d740ca 100644
--- a/go/res/values-vi/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"Chạm và giữ để chọn lối tắt."</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"Nhấn đúp và giữ để chọn lối tắt hoặc sử dụng hành động tùy chỉnh."</string>
- <string name="widget_button_text" msgid="4221900832360456858">"Lối tắt"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"Lối tắt <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-te/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-te/strings.xml
index dea7749..36e5e3e 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-th/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-th/strings.xml
index dea7749..882b096 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-tl/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-tl/strings.xml
index dea7749..dbd03b1 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-tr/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-tr/strings.xml
index dea7749..57cb349 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-uk/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-uk/strings.xml
index dea7749..8c2c6f6 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-ur/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-ur/strings.xml
index dea7749..ab5b38f 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-uz/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-uz/strings.xml
index dea7749..280bd88 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-vi/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-vi/strings.xml
index dea7749..2aca3b8 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-zh-rCN/strings.xml
index dea7749..165f141 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-zh-rHK/strings.xml
index dea7749..6ba0244 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-zh-rTW/strings.xml
index dea7749..9834038 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/go/res/values-zh-rHK/strings.xml b/quickstep/res/values-zu/strings.xml
similarity index 62%
copy from go/res/values-zh-rHK/strings.xml
copy to quickstep/res/values-zu/strings.xml
index dea7749..a4ba48c 100644
--- a/go/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -19,8 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="long_press_widget_to_add" msgid="4001616142797446267">"按住捷徑即可選取捷徑。"</string>
- <string name="long_accessible_way_to_add" msgid="2725225828389948328">"扲兩下然後扲住就可以揀選捷徑,或者用自訂嘅操作。"</string>
- <string name="widget_button_text" msgid="4221900832360456858">"捷徑"</string>
- <string name="widgets_bottom_sheet_title" msgid="3949835990909395998">"<xliff:g id="NAME">%1$s</xliff:g> 捷徑"</string>
+ <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/drawable/task_thumbnail_background.xml b/quickstep/res/values/config.xml
similarity index 65%
copy from quickstep/res/drawable/task_thumbnail_background.xml
copy to quickstep/res/values/config.xml
index 603380e..94211c6 100644
--- a/quickstep/res/drawable/task_thumbnail_background.xml
+++ b/quickstep/res/values/config.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
+ http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="#FF000000" />
- <corners android:radius="2dp" />
-</shape>
+<resources>
+ <string name="task_overlay_factory_class" translatable="false"></string>
+
+</resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 4f85957..0956048 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -16,10 +16,28 @@
<resources>
+ <dimen name="options_menu_icon_size">24dp</dimen>
+
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
+ <dimen name="task_menu_background_radius">12dp</dimen>
+ <dimen name="task_corner_radius">2dp</dimen>
+ <dimen name="task_fade_length">20dp</dimen>
+ <dimen name="recents_page_spacing">10dp</dimen>
+
<dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
<dimen name="quickstep_fling_min_velocity">250dp</dimen>
+ <dimen name="workspace_overview_offset_x">-24dp</dimen>
+
+ <!-- TODO: This can be calculated using other resource values -->
+ <dimen name="all_apps_search_box_full_height">90dp</dimen>
+
+ <!-- Launcher app transition -->
+ <dimen name="content_trans_y">25dp</dimen>
+ <dimen name="workspace_trans_y">80dp</dimen>
+ <dimen name="recents_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/res/drawable/task_thumbnail_background.xml b/quickstep/res/values/override.xml
similarity index 69%
copy from quickstep/res/drawable/task_thumbnail_background.xml
copy to quickstep/res/values/override.xml
index 603380e..ba99d81 100644
--- a/quickstep/res/drawable/task_thumbnail_background.xml
+++ b/quickstep/res/values/override.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="#FF000000" />
- <corners android:radius="2dp" />
-</shape>
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_transition_manager_class" translatable="false">com.android.launcher3.LauncherAppTransitionManagerImpl</string>
+</resources>
+
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index ef61226..ec8eb52 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -20,4 +20,13 @@
<!-- Application name -->
<string name="derived_app_name" translatable="false">Quickstep</string>
+
+ <!-- Options for recent tasks -->
+ <!-- Title for an option to enter split screen mode for a given app -->
+ <string name="recent_task_option_split_screen">Split screen</string>
+ <!-- Title for an option to keep an app pinned to the screen until it is unpinned -->
+ <string name="recent_task_option_pin">Pin</string>
+
+ <!-- Text that shows above the navigation bar after launching a few apps -->
+ <string name="recents_swipe_up_onboarding">Swipe up from the bottom to switch apps</string>
</resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
new file mode 100644
index 0000000..872e6ca
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -0,0 +1,41 @@
+/*
+ * 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.animation.AnimatorSet;
+
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+
+import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
+
+ AnimatorSet mAnimator;
+ private Launcher mLauncher;
+
+ LauncherAnimationRunner(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ postAtFrontOfQueueAsynchronously(mLauncher.getWindow().getDecorView().getHandler(), () -> {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
new file mode 100644
index 0000000..0fe29e3
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -0,0 +1,865 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import static com.android.launcher3.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;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+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;
+import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.TaskView;
+import com.android.systemui.shared.system.ActivityCompat;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
+import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+/**
+ * Manages the opening and closing app transitions from Launcher.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+@SuppressWarnings("unused")
+public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManager
+ implements OnDeviceProfileChangeListener {
+
+ private static final String TAG = "LauncherTransition";
+ private static final int REFRESH_RATE_MS = 16;
+
+ private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
+ "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
+
+ private static final int RECENTS_LAUNCH_DURATION = 336;
+ private static final int LAUNCHER_RESUME_START_DELAY = 150;
+ 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_OFF_SCREEN = 1.3059858f;
+ private static final float ALL_APPS_PROGRESS_OVERSHOOT = 0.99581414f;
+
+ private final DragLayer mDragLayer;
+ private final Launcher mLauncher;
+ private DeviceProfile mDeviceProfile;
+
+ 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;
+
+ private LauncherTransitionAnimator mCurrentAnimator;
+
+ public LauncherAppTransitionManagerImpl(Context context) {
+ mLauncher = Launcher.getLauncher(context);
+ mDragLayer = mLauncher.getDragLayer();
+ mDeviceProfile = mLauncher.getDeviceProfile();
+
+ mIsRtl = Utilities.isRtl(mLauncher.getResources());
+
+ 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();
+ }
+
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mDeviceProfile = dp;
+ }
+
+ private void setCurrentAnimator(LauncherTransitionAnimator animator) {
+ if (isAnimating()) {
+ mCurrentAnimator.cancel();
+ }
+ mCurrentAnimator = animator;
+ }
+
+ @Override
+ public void finishLauncherAnimation() {
+ if (isAnimating()) {
+ mCurrentAnimator.finishLauncherAnimation();
+ }
+ mCurrentAnimator = null;
+ }
+
+ @Override
+ public boolean isAnimating() {
+ return mCurrentAnimator != null && mCurrentAnimator.isRunning();
+ }
+
+ /**
+ * @return ActivityOptions with remote animations that controls how the window of the opening
+ * targets are displayed.
+ */
+ @Override
+ public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
+ if (hasControlRemoteAppTransitionPermission()) {
+ try {
+ RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) {
+ @Override
+ public void onAnimationStart(RemoteAnimationTargetCompat[] targets,
+ Runnable finishedCallback) {
+ // Post at front of queue ignoring sync barriers to make sure it gets
+ // processed before the next frame.
+ postAtFrontOfQueueAsynchronously(v.getHandler(), () -> {
+ final boolean removeTrackingView;
+ LauncherTransitionAnimator animator =
+ composeRecentsLaunchAnimator(v, targets);
+ if (animator != null) {
+ // We are animating the task view directly, do not remove it after
+ removeTrackingView = false;
+ } else {
+ animator = composeAppLaunchAnimator(v, targets);
+ // A new floating view is created for the animation, remove it after
+ removeTrackingView = true;
+ }
+
+ setCurrentAnimator(animator);
+ mAnimator = animator.getAnimatorSet();
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // Reset launcher to normal state
+ v.setVisibility(View.VISIBLE);
+ if (removeTrackingView) {
+ ((ViewGroup) mDragLayer.getParent()).removeView(
+ mFloatingView);
+ }
+
+ mDragLayer.setAlpha(1f);
+ mDragLayer.setTranslationY(0f);
+
+ View appsView = mLauncher.getAppsView();
+ appsView.setAlpha(1f);
+ appsView.setTranslationY(0f);
+
+ finishedCallback.run();
+ }
+ });
+ mAnimator.start();
+ // Because t=0 has the app icon in its original spot, we can skip the
+ // first frame and have the same movement one frame earlier.
+ mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
+ });
+ }
+ };
+
+ return ActivityOptionsCompat.makeRemoteAnimation(
+ 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.
+ }
+ }
+ return getDefaultActivityLaunchOptions(launcher, v);
+ }
+
+ /**
+ * Composes the animations for a launch from the recents list if possible.
+ */
+ private LauncherTransitionAnimator composeRecentsLaunchAnimator(View v,
+ RemoteAnimationTargetCompat[] targets) {
+ // Ensure recents is actually visible
+ if (!mLauncher.isInState(LauncherState.OVERVIEW)) {
+ return null;
+ }
+
+ // Resolve the opening task id
+ int openingTaskId = -1;
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+ openingTaskId = target.taskId;
+ break;
+ }
+ }
+
+ // If there is no opening task id, fall back to the normal app icon launch animation
+ if (openingTaskId == -1) {
+ return null;
+ }
+
+ // If the opening task id is not currently visible in overview, then fall back to normal app
+ // icon launch animation
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ TaskView taskView = recentsView.getTaskView(openingTaskId);
+ if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
+ return null;
+ }
+
+ // Found a visible recents task that matches the opening app, lets launch the app from there
+ 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;
+ }
+
+ /**
+ * @return Animator that controls the window of the opening targets for the recents launch
+ * animation.
+ */
+ private ValueAnimator getRecentsWindowAnimator(TaskView v,
+ RemoteAnimationTargetCompat[] targets) {
+ Rect taskViewBounds = new Rect();
+ mDragLayer.getDescendantRectRelativeToSelf(v, taskViewBounds);
+
+ // TODO: Use the actual target insets instead of the current thumbnail insets in case the
+ // device state has changed
+ RecentsAnimationInterpolator recentsInterpolator = new RecentsAnimationInterpolator(
+ new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx),
+ v.getThumbnail().getInsets(),
+ taskViewBounds,
+ new Rect(0, v.getThumbnail().getTop(), 0, 0),
+ v.getScaleX(),
+ v.getTranslationX());
+
+ Rect crop = new Rect();
+ Matrix matrix = new Matrix();
+
+ ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+ appAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+ appAnimator.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
+ appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ boolean isFirstFrame = true;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final Surface surface = getSurface(v);
+ final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
+ if (frameNumber == -1) {
+ // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
+ Log.w(TAG, "Failed to animate, surface got destroyed.");
+ return;
+ }
+ final float percent = animation.getAnimatedFraction();
+ TaskWindowBounds tw = recentsInterpolator.interpolate(percent);
+
+ v.setScaleX(tw.taskScale);
+ v.setScaleY(tw.taskScale);
+ v.setTranslationX(tw.taskX);
+ v.setTranslationY(tw.taskY);
+ // Defer fading out the view until after the app window gets faded in
+ v.setAlpha(getValue(1f, 0f, 75, 75,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR));
+
+ matrix.setScale(tw.winScale, tw.winScale);
+ matrix.postTranslate(tw.winX, tw.winY);
+ crop.set(tw.winCrop);
+
+ // Fade in the app window.
+ float alphaDelay = 0;
+ float alphaDuration = 75;
+ float alpha = getValue(0f, 1f, alphaDelay, alphaDuration,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR);
+
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == RemoteAnimationTargetCompat.MODE_OPENING) {
+ t.setAlpha(target.leash, alpha);
+
+ // TODO: This isn't correct at the beginning of the animation, but better
+ // than nothing.
+ matrix.postTranslate(target.position.x, target.position.y);
+ t.setMatrix(target.leash, matrix);
+ t.setWindowCrop(target.leash, crop);
+ t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface));
+ }
+ if (isFirstFrame) {
+ t.show(target.leash);
+ }
+ }
+ t.apply();
+
+ matrix.reset();
+ 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;
+ }
+
+ /**
+ * Composes the animations for a launch from an app icon.
+ */
+ private LauncherTransitionAnimator composeAppLaunchAnimator(View v,
+ RemoteAnimationTargetCompat[] targets) {
+ 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, RemoteAnimationTargetCompat[] targets) {
+ AnimatorSet launcherAnimators = new AnimatorSet();
+ launcherAnimators.play(getIconAnimator(v));
+ if (launcherIsATargetWithMode(targets, MODE_CLOSING)) {
+ launcherAnimators.play(getLauncherContentAnimator(false /* show */));
+ }
+ return launcherAnimators;
+ }
+
+ /**
+ * Content is everything on screen except the background and the floating view (if any).
+ *
+ * @param show If true: Animate the content so that it moves upwards and fades in.
+ * Else: Animate the content so that it moves downwards and fades out.
+ */
+ private AnimatorSet getLauncherContentAnimator(boolean show) {
+ AnimatorSet launcherAnimator = new AnimatorSet();
+
+ float[] alphas = show
+ ? new float[] {0, 1}
+ : new float[] {1, 0};
+ float[] trans = show
+ ? new float[] {mContentTransY, 0,}
+ : new float[] {0, mContentTransY};
+
+ if (mLauncher.isInState(LauncherState.ALL_APPS) && !mDeviceProfile.isVerticalBarLayout()) {
+ // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView.
+ View appsView = mLauncher.getAppsView();
+ appsView.setAlpha(alphas[0]);
+ appsView.setTranslationY(trans[0]);
+
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
+ alpha.setDuration(217);
+ alpha.setInterpolator(Interpolators.LINEAR);
+ ObjectAnimator transY = ObjectAnimator.ofFloat(appsView, View.TRANSLATION_Y, trans);
+ transY.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ transY.setDuration(350);
+
+ launcherAnimator.play(alpha);
+ launcherAnimator.play(transY);
+ } else {
+ mDragLayer.setAlpha(alphas[0]);
+ mDragLayer.setTranslationY(trans[0]);
+
+ ObjectAnimator dragLayerAlpha = ObjectAnimator.ofFloat(mDragLayer, View.ALPHA, alphas);
+ dragLayerAlpha.setDuration(217);
+ dragLayerAlpha.setInterpolator(Interpolators.LINEAR);
+ ObjectAnimator dragLayerTransY = ObjectAnimator.ofFloat(mDragLayer, View.TRANSLATION_Y,
+ trans);
+ dragLayerTransY.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ dragLayerTransY.setDuration(350);
+
+ launcherAnimator.play(dragLayerAlpha);
+ launcherAnimator.play(dragLayerTransY);
+ }
+ return launcherAnimator;
+ }
+
+ /**
+ * @return Animator that controls the icon used to launch the target.
+ */
+ private AnimatorSet getIconAnimator(View v) {
+ boolean isBubbleTextView = v instanceof BubbleTextView;
+ mFloatingView = new View(mLauncher);
+ if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
+ // Create a copy of the app icon
+ 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
+ Rect rect = new Rect();
+ mDragLayer.getDescendantRectRelativeToSelf(v, rect);
+ int viewLocationStart = mIsRtl
+ ? mDeviceProfile.widthPx - rect.right
+ : rect.left;
+ int viewLocationTop = rect.top;
+
+ if (isBubbleTextView) {
+ ((BubbleTextView) v).getIconBounds(rect);
+ }
+ LayoutParams lp = new LayoutParams(rect.width(), rect.height());
+ lp.ignoreInsets = true;
+ lp.setMarginStart(viewLocationStart + rect.left);
+ lp.topMargin = viewLocationTop + rect.top;
+ mFloatingView.setLayoutParams(lp);
+
+ // Swap the two views in place.
+ ((ViewGroup) mDragLayer.getParent()).addView(mFloatingView);
+ v.setVisibility(View.INVISIBLE);
+
+ AnimatorSet appIconAnimatorSet = new AnimatorSet();
+ // Animate the app icon to the center
+ float centerX = mDeviceProfile.widthPx / 2;
+ float centerY = mDeviceProfile.heightPx / 2;
+
+ float xPosition = mIsRtl
+ ? mDeviceProfile.widthPx - lp.getMarginStart() - rect.width()
+ : lp.getMarginStart();
+ float dX = centerX - xPosition - (lp.width / 2);
+ float dY = centerY - lp.topMargin - (lp.height / 2);
+
+ ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX);
+ ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY);
+
+ // Adjust the duration to change the "curve" of the app icon to the center.
+ boolean isBelowCenterY = lp.topMargin < centerY;
+ x.setDuration(isBelowCenterY ? 500 : 233);
+ y.setDuration(isBelowCenterY ? 233 : 500);
+ x.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ y.setInterpolator(Interpolators.AGGRESSIVE_EASE);
+ appIconAnimatorSet.play(x);
+ appIconAnimatorSet.play(y);
+
+ // Scale the app icon to take up the entire screen. This simplifies the math when
+ // animating the app window position / scale.
+ float maxScaleX = mDeviceProfile.widthPx / (float) rect.width();
+ float maxScaleY = mDeviceProfile.heightPx / (float) rect.height();
+ float scale = Math.max(maxScaleX, maxScaleY);
+ ObjectAnimator sX = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_X, 1f, scale);
+ ObjectAnimator sY = ObjectAnimator.ofFloat(mFloatingView, View.SCALE_Y, 1f, scale);
+ sX.setDuration(500);
+ sY.setDuration(500);
+ 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(32);
+ alpha.setDuration(50);
+ alpha.setInterpolator(Interpolators.LINEAR);
+ appIconAnimatorSet.play(alpha);
+
+ return appIconAnimatorSet;
+ }
+
+ /**
+ * @return Animator that controls the window of the opening targets.
+ */
+ private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
+ Rect bounds = new Rect();
+ if (v instanceof BubbleTextView) {
+ ((BubbleTextView) v).getIconBounds(bounds);
+ } else {
+ mDragLayer.getDescendantRectRelativeToSelf(v, bounds);
+ }
+ int[] floatingViewBounds = new int[2];
+
+ Rect crop = new Rect();
+ Matrix matrix = new Matrix();
+
+ ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+ appAnimator.setDuration(500);
+ appAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ boolean isFirstFrame = true;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final Surface surface = getSurface(mFloatingView);
+ final long frameNumber = surface != null ? getNextFrameNumber(surface) : -1;
+ if (frameNumber == -1) {
+ // Booo, not cool! Our surface got destroyed, so no reason to animate anything.
+ Log.w(TAG, "Failed to animate, surface got destroyed.");
+ return;
+ }
+ final float percent = animation.getAnimatedFraction();
+ final float easePercent = Interpolators.AGGRESSIVE_EASE.getInterpolation(percent);
+
+ // Calculate app icon size.
+ float iconWidth = bounds.width() * mFloatingView.getScaleX();
+ float iconHeight = bounds.height() * mFloatingView.getScaleY();
+
+ // Scale the app window to match the icon size.
+ float scaleX = iconWidth / mDeviceProfile.widthPx;
+ float scaleY = iconHeight / mDeviceProfile.heightPx;
+ float scale = Math.min(1f, Math.min(scaleX, scaleY));
+ matrix.setScale(scale, scale);
+
+ // Position the scaled window on top of the icon
+ int deviceWidth = mDeviceProfile.widthPx;
+ int deviceHeight = mDeviceProfile.heightPx;
+ float scaledWindowWidth = deviceWidth * scale;
+ float scaledWindowHeight = deviceHeight * scale;
+
+ float offsetX = (scaledWindowWidth - iconWidth) / 2;
+ float offsetY = (scaledWindowHeight - iconHeight) / 2;
+ mFloatingView.getLocationInWindow(floatingViewBounds);
+ float transX0 = floatingViewBounds[0] - offsetX;
+ float transY0 = floatingViewBounds[1] - offsetY;
+ matrix.postTranslate(transX0, transY0);
+
+ // Fade in the app window.
+ float alphaDelay = 0;
+ float alphaDuration = 60;
+ float alpha = getValue(0f, 1f, alphaDelay, alphaDuration,
+ appAnimator.getDuration() * percent, Interpolators.LINEAR);
+
+ // Animate the window crop so that it starts off as a square, and then reveals
+ // horizontally.
+ float cropHeight = deviceHeight * easePercent + deviceWidth * (1 - easePercent);
+ float initialTop = (deviceHeight - deviceWidth) / 2f;
+ crop.left = 0;
+ crop.top = (int) (initialTop * (1 - easePercent));
+ crop.right = deviceWidth;
+ crop.bottom = (int) (crop.top + cropHeight);
+
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == MODE_OPENING) {
+ t.setAlpha(target.leash, alpha);
+
+ // TODO: This isn't correct at the beginning of the animation, but better
+ // than nothing.
+ matrix.postTranslate(target.position.x, target.position.y);
+ t.setMatrix(target.leash, matrix);
+ t.setWindowCrop(target.leash, crop);
+ t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface));
+ }
+ if (isFirstFrame) {
+ t.show(target.leash);
+ }
+ }
+ t.apply();
+
+ matrix.reset();
+ isFirstFrame = false;
+ }
+ });
+ return appAnimator;
+ }
+
+ /**
+ * Registers remote animations used when closing apps to home screen.
+ */
+ private void registerRemoteAnimations() {
+ if (hasControlRemoteAppTransitionPermission()) {
+ try {
+ RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat();
+ definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN,
+ new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(),
+ CLOSING_TRANSITION_DURATION_MS, 0 /* statusBarTransitionDelay */));
+
+// TODO: App controlled transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER
+
+ new ActivityCompat(mLauncher).registerRemoteAnimations(definition);
+ } catch (NoClassDefFoundError e) {
+ // Gracefully fall back if the user's platform doesn't have the latest changes
+ }
+ }
+ }
+
+ private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
+ int launcherTaskId = mLauncher.getTaskId();
+ for (RemoteAnimationTargetCompat target : targets) {
+ if (target.mode == mode && target.taskId == launcherTaskId) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @return Runner that plays when user goes to Launcher
+ * ie. pressing home, swiping up from nav bar.
+ */
+ private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
+ return new LauncherAnimationRunner(mLauncher) {
+ @Override
+ public void onAnimationStart(RemoteAnimationTargetCompat[] targets,
+ Runnable finishedCallback) {
+ Handler handler = mLauncher.getWindow().getDecorView().getHandler();
+ postAtFrontOfQueueAsynchronously(handler, () -> {
+ 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;
+ }
+
+ LauncherTransitionAnimator animator = new LauncherTransitionAnimator(
+ getLauncherResumeAnimation(), getClosingWindowAnimators(targets));
+ setCurrentAnimator(animator);
+ mAnimator = animator.getAnimatorSet();
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishedCallback.run();
+ }
+ });
+ mAnimator.start();
+
+ // Because t=0 has the app icon in its original spot, we can skip the
+ // first frame and have the same movement one frame earlier.
+ mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
+ });
+ }
+ };
+ }
+
+ /**
+ * Animator that controls the transformations of the windows the targets that are closing.
+ */
+ private Animator getClosingWindowAnimators(RemoteAnimationTargetCompat[] targets) {
+ Matrix matrix = new Matrix();
+ float height = mLauncher.getDeviceProfile().heightPx;
+ float width = mLauncher.getDeviceProfile().widthPx;
+ float endX = (Utilities.isRtl(mLauncher.getResources()) ? -width : width) * 1.16f;
+
+ ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
+ closingAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
+
+ closingAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ boolean isFirstFrame = true;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final float percent = animation.getAnimatedFraction();
+ float currentPlayTime = percent * closingAnimator.getDuration();
+
+ float scale = getValue(1f, 0.8f, 0, 267, currentPlayTime,
+ Interpolators.AGGRESSIVE_EASE);
+
+ float dX = getValue(0, endX, 0, 350, currentPlayTime,
+ Interpolators.AGGRESSIVE_EASE_IN_OUT);
+
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat app : targets) {
+ if (app.mode == RemoteAnimationTargetCompat.MODE_CLOSING) {
+ t.setAlpha(app.leash, getValue(1f, 0f, 0, 350, currentPlayTime,
+ Interpolators.APP_CLOSE_ALPHA));
+ matrix.setScale(scale, scale,
+ app.sourceContainerBounds.centerX(),
+ app.sourceContainerBounds.centerY());
+ matrix.postTranslate(dX, 0);
+ matrix.postTranslate(app.position.x, app.position.y);
+ t.setMatrix(app.leash, matrix);
+ }
+ if (isFirstFrame) {
+ int layer = app.mode == RemoteAnimationTargetCompat.MODE_CLOSING
+ ? Integer.MAX_VALUE
+ : app.prefixOrderIndex;
+ t.setLayer(app.leash, layer);
+ t.show(app.leash);
+ }
+ }
+ t.apply();
+
+ matrix.reset();
+ isFirstFrame = false;
+ }
+ });
+ return closingAnimator;
+ }
+
+ /**
+ * @return Animator that modifies Launcher as a result from {@link #getWallpaperOpenRunner}.
+ */
+ private AnimatorSet getLauncherResumeAnimation() {
+ if (mLauncher.isInState(LauncherState.ALL_APPS)
+ || mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ AnimatorSet contentAnimator = getLauncherContentAnimator(true /* show */);
+ contentAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
+ return contentAnimator;
+ } else {
+ AnimatorSet workspaceAnimator = new AnimatorSet();
+ mLauncher.getWorkspace().setTranslationY(mWorkspaceTransY);
+ mLauncher.getWorkspace().setAlpha(0f);
+ workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(),
+ View.TRANSLATION_Y, mWorkspaceTransY, 0));
+ workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(), View.ALPHA,
+ 0, 1f));
+ workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
+ workspaceAnimator.setDuration(333);
+ workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+ // 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_OFF_SCREEN;
+ // And will end slightly pulled up, so that there is something to overshoot back to 1f.
+ final float slideEnd = ALL_APPS_PROGRESS_OVERSHOOT;
+
+ allAppsController.setProgress(startY);
+
+ Animator allAppsSlideIn =
+ ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, startY, slideEnd);
+ allAppsSlideIn.setStartDelay(LAUNCHER_RESUME_START_DELAY);
+ allAppsSlideIn.setDuration(317);
+ allAppsSlideIn.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+ Animator allAppsOvershoot =
+ ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, slideEnd, 1f);
+ allAppsOvershoot.setDuration(153);
+ allAppsOvershoot.setInterpolator(Interpolators.OVERSHOOT_0);
+
+ AnimatorSet resumeLauncherAnimation = new AnimatorSet();
+ resumeLauncherAnimation.play(workspaceAnimator);
+ resumeLauncherAnimation.playSequentially(allAppsSlideIn, allAppsOvershoot);
+ return resumeLauncherAnimation;
+ }
+ }
+
+ private boolean hasControlRemoteAppTransitionPermission() {
+ return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Helper method that allows us to get interpolated values for embedded
+ * animations with a delay and/or different duration.
+ */
+ private static float getValue(float start, float end, float delay, float duration,
+ float currentPlayTime, Interpolator i) {
+ float time = Math.max(0, currentPlayTime - delay);
+ float newPercent = Math.min(1f, time / duration);
+ newPercent = i.getInterpolation(newPercent);
+ return end * newPercent + start * (1 - newPercent);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java b/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java
new file mode 100644
index 0000000..aec2869
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/LauncherTransitionAnimator.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+
+/**
+ * Creates an AnimatorSet consisting on one Animator for Launcher transition, and one Animator for
+ * the Window transitions.
+ *
+ * Allows for ending the Launcher animator without ending the Window animator.
+ */
+public class LauncherTransitionAnimator {
+
+ private AnimatorSet mAnimatorSet;
+ private Animator mLauncherAnimator;
+ private Animator mWindowAnimator;
+
+ LauncherTransitionAnimator(Animator launcherAnimator, Animator windowAnimator) {
+ if (launcherAnimator != null) {
+ mLauncherAnimator = launcherAnimator;
+ }
+ mWindowAnimator = windowAnimator;
+
+ mAnimatorSet = new AnimatorSet();
+ if (launcherAnimator != null) {
+ mAnimatorSet.play(launcherAnimator);
+ }
+ mAnimatorSet.play(windowAnimator);
+ }
+
+ public AnimatorSet getAnimatorSet() {
+ return mAnimatorSet;
+ }
+
+ public void cancel() {
+ mAnimatorSet.cancel();
+ }
+
+ public boolean isRunning() {
+ return mAnimatorSet.isRunning();
+ }
+
+ public void finishLauncherAnimation() {
+ if (mLauncherAnimator != null) {
+ mLauncherAnimator.end();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
similarity index 62%
copy from src/com/android/launcher3/states/AllAppsState.java
copy to quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index ed3023a..426fe35 100644
--- a/src/com/android/launcher3/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.states;
+package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
+import static com.android.launcher3.allapps.DiscoveryBounce.APPS_VIEW_SHOWN;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import android.view.View;
@@ -30,12 +32,17 @@
*/
public class AllAppsState extends LauncherState {
- public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
-
private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+ private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
+ @Override
+ public float getPageAlpha(int pageIndex) {
+ return 0;
+ }
+ };
+
public AllAppsState(int id) {
- super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS);
+ super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
}
@Override
@@ -54,7 +61,33 @@
}
@Override
+ public float getVerticalProgress(Launcher launcher) {
+ return 0f;
+ }
+
+ @Override
public View getFinalFocus(Launcher launcher) {
return launcher.getAppsView();
}
+
+ @Override
+ public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+ // TODO: interpolate
+ return LauncherState.OVERVIEW.getWorkspaceScaleAndTranslation(launcher);
+ }
+
+ @Override
+ public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+ return PAGE_ALPHA_PROVIDER;
+ }
+
+ @Override
+ public float getHoseatAlpha(Launcher launcher) {
+ return 0;
+ }
+
+ @Override
+ public LauncherState getHistoryForState(LauncherState previousState) {
+ return previousState == OVERVIEW ? OVERVIEW : NORMAL;
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java b/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java
new file mode 100644
index 0000000..6df1aba
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/DragPauseDetector.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.OnAlarmListener;
+
+/**
+ * Utility class to detect a pause during a drag.
+ */
+public class DragPauseDetector implements OnAlarmListener {
+
+ private static final float MAX_VELOCITY_TO_PAUSE = 0.2f;
+ private static final long PAUSE_DURATION = 100;
+
+ private final Alarm mAlarm;
+ private final Runnable mOnPauseCallback;
+
+ private boolean mTriggered = false;
+ private int mDisabledFlags = 0;
+
+ public DragPauseDetector(Runnable onPauseCallback) {
+ mOnPauseCallback = onPauseCallback;
+
+ mAlarm = new Alarm();
+ mAlarm.setOnAlarmListener(this);
+ mAlarm.setAlarm(PAUSE_DURATION);
+ }
+
+ public void onDrag(float velocity) {
+ if (mTriggered || !isEnabled()) {
+ return;
+ }
+
+ if (Math.abs(velocity) > MAX_VELOCITY_TO_PAUSE) {
+ // Cancel any previous alarm and set a new alarm
+ mAlarm.setAlarm(PAUSE_DURATION);
+ }
+ }
+
+ @Override
+ public void onAlarm(Alarm alarm) {
+ if (!mTriggered && isEnabled()) {
+ mTriggered = true;
+ mOnPauseCallback.run();
+ }
+ }
+
+ public boolean isTriggered () {
+ return mTriggered;
+ }
+
+ public boolean isEnabled() {
+ return mDisabledFlags == 0;
+ }
+
+ public void addDisabledFlags(int flags) {
+ boolean wasEnabled = isEnabled();
+ mDisabledFlags |= flags;
+ resetAlarm(wasEnabled);
+ }
+
+ public void clearDisabledFlags(int flags) {
+ boolean wasEnabled = isEnabled();
+ mDisabledFlags &= ~flags;
+ resetAlarm(wasEnabled);
+ }
+
+ private void resetAlarm(boolean wasEnabled) {
+ boolean isEnabled = isEnabled();
+ if (wasEnabled == isEnabled) {
+ // Nothing has changed
+ } if (isEnabled && !mTriggered) {
+ mAlarm.setAlarm(PAUSE_DURATION);
+ } else if (!isEnabled) {
+ mAlarm.cancelAlarm();
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
new file mode 100644
index 0000000..541c6bb
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/EdgeSwipeController.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.touch.SwipeDetector.DIRECTION_NEGATIVE;
+import static com.android.launcher3.touch.SwipeDetector.DIRECTION_POSITIVE;
+import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
+import static com.android.launcher3.touch.SwipeDetector.VERTICAL;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+
+import android.graphics.Rect;
+import android.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.
+ */
+public class EdgeSwipeController extends VerticalSwipeController implements
+ OnDeviceProfileChangeListener {
+
+ 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);
+ l.addOnDeviceProfileChangeListener(this);
+ }
+
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mDetector.updateDirection(dp.isVerticalBarLayout() ? HORIZONTAL : VERTICAL);
+ }
+
+ @Override
+ protected boolean shouldInterceptTouch(MotionEvent ev) {
+ return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+ }
+
+ @Override
+ protected int getSwipeDirection(MotionEvent ev) {
+ return isTransitionFlipped() ? DIRECTION_NEGATIVE : DIRECTION_POSITIVE;
+ }
+
+ public EdgeSwipeController(Launcher l, LauncherState baseState) {
+ super(l, baseState);
+ }
+
+ @Override
+ protected boolean isTransitionFlipped() {
+ return mLauncher.getDeviceProfile().isSeascape();
+ }
+
+ @Override
+ protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
+ 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
+ protected void initSprings() {
+ mSpringHandlers = new SpringAnimationHandler[0];
+ }
+
+ @Override
+ protected float getShiftRange() {
+ return getShiftRange(mLauncher);
+ }
+
+ public static float getShiftRange(Launcher launcher) {
+ RecentsView.getPageRect(launcher.getDeviceProfile(), launcher, sTempRect);
+ DragLayer dl = launcher.getDragLayer();
+ Rect insets = dl.getInsets();
+ DeviceProfile dp = launcher.getDeviceProfile();
+
+ if (dp.isVerticalBarLayout()) {
+ if (dp.isSeascape()) {
+ return insets.left + sTempRect.left;
+ } else {
+ return dl.getWidth() - sTempRect.right + insets.right;
+ }
+ } else {
+ return dl.getHeight() - sTempRect.bottom + insets.bottom;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/IgnoreTouchesInQuickScrub.java b/quickstep/src/com/android/launcher3/uioverrides/IgnoreTouchesInQuickScrub.java
new file mode 100644
index 0000000..2d5eb5a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/IgnoreTouchesInQuickScrub.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.TouchInteractionService;
+
+/**
+ * Consumes touches when quick scrub is enabled.
+ */
+public class IgnoreTouchesInQuickScrub implements TouchController {
+
+ public IgnoreTouchesInQuickScrub() {
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return true;
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ return TouchInteractionService.isQuickScrubEnabled();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OptionsPopupView.java b/quickstep/src/com/android/launcher3/uioverrides/OptionsPopupView.java
new file mode 100644
index 0000000..c089d06
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/OptionsPopupView.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Outline;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.Toast;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.RevealOutlineAnimation;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.GradientView;
+import com.android.launcher3.widget.WidgetsFullSheet;
+
+/**
+ * Popup shown on long pressing an empty space in launcher
+ */
+public class OptionsPopupView extends AbstractFloatingView implements OnClickListener {
+
+ private final float mOutlineRadius;
+ private final Launcher mLauncher;
+ private final PointF mTouchPoint = new PointF();
+
+ private final GradientView mGradientView;
+
+ protected Animator mOpenCloseAnimator;
+
+ public OptionsPopupView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public OptionsPopupView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mOutlineRadius);
+ }
+ });
+
+ mLauncher = Launcher.getLauncher(context);
+
+ mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate(
+ R.layout.widgets_bottom_sheet_scrim, mLauncher.getDragLayer(), false);
+ mGradientView.setProgress(1, false);
+ mGradientView.setAlpha(0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ findViewById(R.id.wallpaper_button).setOnClickListener(this);
+ findViewById(R.id.widget_button).setOnClickListener(this);
+ findViewById(R.id.settings_button).setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (view.getId() == R.id.wallpaper_button) {
+ mLauncher.onClickWallpaperPicker(null);
+ close(true);
+ } else if (view.getId() == R.id.widget_button) {
+ if (mLauncher.getPackageManager().isSafeMode()) {
+ Toast.makeText(mLauncher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
+ } else {
+ WidgetsFullSheet.show(mLauncher, true /* animated */);
+ close(true);
+ }
+ } else if (view.getId() == R.id.settings_button) {
+ mLauncher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+ .setPackage(mLauncher.getPackageName())
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ close(true);
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() != MotionEvent.ACTION_DOWN) {
+ return false;
+ }
+ if (mLauncher.getDragLayer().isEventOverView(this, ev)) {
+ return false;
+ }
+ close(true);
+ return true;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ protected void animateClose() {
+ if (!mIsOpen) {
+ return;
+ }
+ mIsOpen = false;
+
+ final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+ closeAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
+
+ // Rectangular reveal (reversed).
+ final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, true);
+ closeAnim.play(revealAnim);
+
+ Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+ fadeOut.setInterpolator(Interpolators.DEACCEL);
+ closeAnim.play(fadeOut);
+
+ Animator gradientAlpha = ObjectAnimator.ofFloat(mGradientView, ALPHA, 0);
+ gradientAlpha.setInterpolator(Interpolators.DEACCEL);
+ closeAnim.play(gradientAlpha);
+
+ closeAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ closeComplete();
+ }
+ });
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ }
+ mOpenCloseAnimator = closeAnim;
+ closeAnim.start();
+ }
+
+ /**
+ * Closes the popup without animation.
+ */
+ private void closeComplete() {
+ if (mOpenCloseAnimator != null) {
+ mOpenCloseAnimator.cancel();
+ mOpenCloseAnimator = null;
+ }
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(this);
+ mLauncher.getDragLayer().removeView(mGradientView);
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // TODO:
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_OPTIONS_POPUP) != 0;
+ }
+
+ private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+ DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ Rect startRect = new Rect();
+ startRect.offset((int) (mTouchPoint.x - lp.x), (int) (mTouchPoint.y - lp.y));
+
+ Rect endRect = new Rect(0, 0, lp.width, lp.height);
+ if (getOutlineProvider() instanceof RevealOutlineAnimation) {
+ ((RevealOutlineAnimation) getOutlineProvider()).getOutline(endRect);
+ }
+
+ return new RoundedRectRevealOutlineProvider
+ (mOutlineRadius, mOutlineRadius, startRect, endRect);
+ }
+
+ private void animateOpen() {
+ mIsOpen = true;
+ final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+ openAnim.setDuration(getResources().getInteger(R.integer.config_popupOpenCloseDuration));
+
+ final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, false);
+ openAnim.play(revealAnim);
+
+ Animator gradientAlpha = ObjectAnimator.ofFloat(mGradientView, ALPHA, 1);
+ gradientAlpha.setInterpolator(Interpolators.ACCEL);
+ openAnim.play(gradientAlpha);
+
+ mOpenCloseAnimator = openAnim;
+
+ openAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mOpenCloseAnimator = null;
+ }
+ });
+ openAnim.start();
+ }
+
+ public static void show(Launcher launcher, float x, float y) {
+ DragLayer dl = launcher.getDragLayer();
+ OptionsPopupView view = (OptionsPopupView) launcher.getLayoutInflater()
+ .inflate(R.layout.longpress_options_menu, dl, false);
+ DragLayer.LayoutParams lp = (DragLayer.LayoutParams) view.getLayoutParams();
+
+ int maxWidth = dl.getWidth();
+ int maxHeight = dl.getHeight();
+ if (x <= 0 || y <= 0 || x >= maxWidth || y >= maxHeight) {
+ x = maxWidth / 2;
+ y = maxHeight / 2;
+ }
+ view.mTouchPoint.set(x, y);
+
+ int height = lp.height;
+
+ // Find a good width;
+ int childCount = view.getChildCount();
+ int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ int widthSpec = MeasureSpec.makeMeasureSpec(maxWidth / childCount, MeasureSpec.AT_MOST);
+ int maxChildWidth = 0;
+
+ for (int i = 0; i < childCount; i ++) {
+ View child = ((ViewGroup) view.getChildAt(i)).getChildAt(0);
+ child.measure(widthSpec, heightSpec);
+ maxChildWidth = Math.max(maxChildWidth, child.getMeasuredWidth());
+ }
+ Rect insets = dl.getInsets();
+ int margin = (int) (2 * view.getElevation());
+
+ int width = Math.min(maxWidth - insets.left - insets.right - 2 * margin,
+ maxChildWidth * childCount);
+ lp.width = width;
+
+ // Position is towards the finger
+ lp.customPosition = true;
+ lp.x = Utilities.boundToRange((int) (x - width / 2), insets.left + margin,
+ maxWidth - insets.right - width - margin);
+ lp.y = Utilities.boundToRange((int) (y - height / 2), insets.top + margin,
+ maxHeight - insets.bottom - height - margin);
+
+ launcher.getDragLayer().addView(view.mGradientView);
+ launcher.getDragLayer().addView(view);
+ view.animateOpen();
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 1176034..9ba2308 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,11 +16,18 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import android.content.Context;
+import android.graphics.Rect;
import android.view.View;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.RecentsView;
@@ -29,16 +36,28 @@
*/
public class OverviewState extends LauncherState {
- private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE;
+ private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
+ | FLAG_DISABLE_RESTORE;
public OverviewState(int id) {
- super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS);
+ super(id, ContainerType.TASKSWITCHER, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
}
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
- // TODO: Find a better transition
- return new float[] {0f, 0};
+ Rect pageRect = new Rect();
+ RecentsView.getScaledDownPageRect(launcher.getDeviceProfile(), launcher, pageRect);
+ RecentsView rv = launcher.getOverviewPanel();
+
+ if (launcher.getWorkspace().getNormalChildWidth() <= 0 || pageRect.isEmpty()) {
+ return super.getWorkspaceScaleAndTranslation(launcher);
+ }
+
+ float overlap = 0;
+ if (rv.getCurrentPage() >= rv.getFirstTaskIndex()) {
+ overlap = launcher.getResources().getDimension(R.dimen.workspace_overview_offset_x);
+ }
+ return getScaleAndTranslationForPageRect(launcher, overlap, pageRect);
}
@Override
@@ -54,7 +73,61 @@
}
@Override
+ public float getVerticalProgress(Launcher launcher) {
+ return getVerticalProgress(launcher.getDeviceProfile(), launcher);
+ }
+
+ @Override
public View getFinalFocus(Launcher launcher) {
return launcher.getOverviewPanel();
}
+
+ public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+ final int centerPage = launcher.getWorkspace().getNextPage();
+ return new PageAlphaProvider(ACCEL_2) {
+ @Override
+ public float getPageAlpha(int pageIndex) {
+ return pageIndex != centerPage ? 0 : 1f;
+ }
+ };
+ }
+
+ public static float[] getScaleAndTranslationForPageRect(Launcher launcher, float offsetX,
+ Rect pageRect) {
+ Workspace ws = launcher.getWorkspace();
+ float childWidth = ws.getNormalChildWidth();
+ float childHeight = ws.getNormalChildHeight();
+
+ float scale = pageRect.height() / childHeight;
+ Rect insets = launcher.getDragLayer().getInsets();
+
+ float halfHeight = ws.getExpectedHeight() / 2;
+ float childTop = halfHeight - scale * (halfHeight - ws.getPaddingTop() - insets.top);
+ float translationY = pageRect.top - childTop;
+
+ // Align the workspace horizontally centered with the task rect
+ float halfWidth = ws.getExpectedWidth() / 2;
+ float childCenter = halfWidth -
+ scale * (halfWidth - ws.getPaddingLeft() - insets.left - childWidth / 2);
+ float translationX = pageRect.centerX() - childCenter;
+
+ if (Utilities.isRtl(launcher.getResources())) {
+ translationX -= offsetX / scale;
+ } else {
+ translationX += offsetX / scale;
+ }
+
+ return new float[] {scale, translationX, translationY};
+ }
+
+ public static float getVerticalProgress(DeviceProfile grid, Context context) {
+ if (!grid.isVerticalBarLayout()) {
+ return 1f;
+ }
+
+ float total = grid.heightPx;
+ float searchHeight = total - grid.availableHeightPx +
+ context.getResources().getDimension(R.dimen.all_apps_search_box_full_height);
+ return 1 - (searchHeight / total);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
new file mode 100644
index 0000000..468a561
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.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;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+
+/**
+ * Touch controller for swipe interaction in Overview state
+ */
+public class OverviewSwipeController extends AnimatorListenerAdapter
+ implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "OverviewSwipeController";
+
+ private static final float ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS = 0.1f;
+ private static final int SINGLE_FRAME_MS = 16;
+
+ // Progress after which the transition is assumed to be a success in case user does not fling
+ private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+
+ private final Launcher mLauncher;
+ private final SwipeDetector mDetector;
+ private final RecentsView mRecentsView;
+ private final int[] mTempCords = new int[2];
+
+ private AnimatorPlaybackController mCurrentAnimation;
+ private boolean mCurrentAnimationIsGoingUp;
+
+ private boolean mNoIntercept;
+ private boolean mSwipeDownEnabled;
+
+ private float mDisplacementShift;
+ private float mProgressMultiplier;
+ private float mEndDisplacement;
+ private int mStartingTarget;
+
+ private TaskView mTaskBeingDragged;
+
+ public OverviewSwipeController(Launcher launcher) {
+ mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ mDetector = new SwipeDetector(launcher, this, SwipeDetector.VERTICAL);
+ }
+
+ private boolean canInterceptTouch() {
+ if (mCurrentAnimation != null) {
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+ return mLauncher.isInState(OVERVIEW);
+ }
+
+ private boolean isEventOverHotseat(MotionEvent ev) {
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ return ev.getY() >
+ mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher);
+ } else {
+ return mLauncher.getDragLayer().isEventOverHotseat(ev);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
+ Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
+ mDetector.finishedScrolling();
+ mCurrentAnimation = null;
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch();
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ final int directionsToDetectScroll;
+ boolean ignoreSlopWhenSettling = false;
+ if (mCurrentAnimation != null) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ ignoreSlopWhenSettling = true;
+ } else {
+ mTaskBeingDragged = null;
+ mSwipeDownEnabled = true;
+
+ int currentPage = mRecentsView.getCurrentPage();
+ if (currentPage == 0) {
+ // User is on home tile
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ } else {
+ View view = mRecentsView.getChildAt(currentPage);
+ if (mLauncher.getDragLayer().isEventOverView(view, ev) &&
+ view instanceof TaskView) {
+ // The tile can be dragged down to open the task.
+ mTaskBeingDragged = (TaskView) view;
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ 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;
+ }
+ }
+ }
+
+ mDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, ignoreSlopWhenSettling);
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ return mDetector.onTouchEvent(ev);
+ }
+
+ private void reInitAnimationController(boolean goingUp) {
+ if (!goingUp && !mSwipeDownEnabled) {
+ goingUp = true;
+ }
+ if (mCurrentAnimation != null && mCurrentAnimationIsGoingUp == goingUp) {
+ // No need to init
+ return;
+ }
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setPlayFraction(0);
+ }
+ mCurrentAnimationIsGoingUp = goingUp;
+ float range = mLauncher.getAllAppsController().getShiftRange();
+ long maxDuration = (long) (2 * range);
+ DragLayer dl = mLauncher.getDragLayer();
+
+ if (mTaskBeingDragged == null) {
+ // User is either going to all apps or home
+ mCurrentAnimation = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(goingUp ? ALL_APPS : NORMAL, maxDuration);
+ if (goingUp) {
+ mEndDisplacement = -range;
+ } else {
+ mEndDisplacement = EdgeSwipeController.getShiftRange(mLauncher);
+ }
+ } else {
+ if (goingUp) {
+ AnimatorSet anim = new AnimatorSet();
+ ObjectAnimator translate = ObjectAnimator.ofFloat(
+ mTaskBeingDragged, View.TRANSLATION_Y, -mTaskBeingDragged.getBottom());
+ translate.setInterpolator(LINEAR);
+ translate.setDuration(maxDuration);
+ anim.play(translate);
+
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(mTaskBeingDragged, View.ALPHA, 0);
+ alpha.setInterpolator(DEACCEL_1_5);
+ alpha.setDuration(maxDuration);
+ anim.play(alpha);
+ mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration);
+ mEndDisplacement = -mTaskBeingDragged.getBottom();
+ } else {
+ AnimatorSet anim = new AnimatorSet();
+ // TODO: Setup a zoom animation
+ mCurrentAnimation = AnimatorPlaybackController.wrap(anim, maxDuration);
+
+ mTempCords[1] = mTaskBeingDragged.getHeight();
+ dl.getDescendantCoordRelativeToSelf(mTaskBeingDragged, mTempCords);
+ mEndDisplacement = dl.getHeight() - mTempCords[1];
+ }
+ }
+
+ mCurrentAnimation.getTarget().addListener(this);
+ mCurrentAnimation.dispatchOnStart();
+ mProgressMultiplier = 1 / mEndDisplacement;
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ if (mCurrentAnimation == null) {
+ reInitAnimationController(mDetector.wasInitialTouchPositive());
+ mDisplacementShift = 0;
+ } else {
+ mDisplacementShift = mCurrentAnimation.getProgressFraction() / mProgressMultiplier;
+ mCurrentAnimation.pause();
+ }
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float totalDisplacement = displacement + mDisplacementShift;
+ boolean isGoingUp =
+ totalDisplacement == 0 ? mCurrentAnimationIsGoingUp : totalDisplacement < 0;
+ if (isGoingUp != mCurrentAnimationIsGoingUp) {
+ reInitAnimationController(isGoingUp);
+ }
+ mCurrentAnimation.setPlayFraction(totalDisplacement * mProgressMultiplier);
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ final boolean goingToEnd;
+ final int logAction;
+ if (fling) {
+ logAction = Touch.FLING;
+ boolean goingUp = velocity < 0;
+ if (!goingUp && !mSwipeDownEnabled) {
+ goingToEnd = false;
+ } else if (goingUp != mCurrentAnimationIsGoingUp) {
+ // In case the fling is in opposite direction, make sure if is close enough
+ // from the start position
+ if (mCurrentAnimation.getProgressFraction()
+ >= ALLOWED_FLING_DIRECTION_CHANGE_PROGRESS) {
+ // Not allowed
+ goingToEnd = false;
+ } else {
+ reInitAnimationController(goingUp);
+ goingToEnd = true;
+ }
+ } else {
+ goingToEnd = true;
+ }
+ } else {
+ logAction = Touch.SWIPE;
+ goingToEnd = mCurrentAnimation.getProgressFraction() > SUCCESS_TRANSITION_PROGRESS;
+ }
+
+ float progress = mCurrentAnimation.getProgressFraction();
+ long animationDuration = SwipeDetector.calculateDuration(
+ velocity, goingToEnd ? (1 - progress) : progress);
+
+ float nextFrameProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS / Math.abs(mEndDisplacement), 0f, 1f);
+
+
+ mCurrentAnimation.setEndAction(() -> onCurrentAnimationEnd(goingToEnd, logAction));
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ anim.setFloatValues(nextFrameProgress, goingToEnd ? 1f : 0f);
+ anim.setDuration(animationDuration);
+ anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ anim.start();
+ }
+
+ private void onCurrentAnimationEnd(boolean wasSuccess, int logAction) {
+ if (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
new file mode 100644
index 0000000..4fb3886
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeUpController.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.VerticalSwipeController;
+
+/**
+ * Extension of {@link VerticalSwipeController} which allows swipe up from OVERVIEW to ALL_APPS
+ * Note that the swipe down is handled by {@link TwoStepSwipeController}.
+ */
+public class OverviewSwipeUpController extends VerticalSwipeController {
+
+ public OverviewSwipeUpController(Launcher l) {
+ super(l, OVERVIEW);
+ }
+
+ @Override
+ protected boolean shouldInterceptTouch(MotionEvent ev) {
+ if (!mLauncher.isInState(OVERVIEW)) {
+ return false;
+ }
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ return ev.getY() >
+ mLauncher.getDragLayer().getHeight() * OVERVIEW.getVerticalProgress(mLauncher);
+ } else {
+ return mLauncher.getDragLayer().isEventOverHotseat(ev);
+ }
+ }
+
+ @Override
+ protected int getSwipeDirection(MotionEvent ev) {
+ return SwipeDetector.DIRECTION_POSITIVE;
+ }
+
+ @Override
+ protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
+ if (stateChanged) {
+ // Transition complete. log the action
+ mLauncher.getUserEventDispatcher().logStateChangeAction(
+ wasFling ? Touch.FLING : Touch.SWIPE,
+ Direction.UP,
+ 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 da1eff9..7d371e9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,39 +15,148 @@
*/
package com.android.launcher3.uioverrides;
-import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
-import android.animation.AnimatorSet;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.view.View;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.WorkspaceStateTransitionAnimation.AnimatedPropertySetter;
-import com.android.launcher3.WorkspaceStateTransitionAnimation.PropertySetter;
-import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.TaskView;
public class RecentsViewStateController implements StateHandler {
private final Launcher mLauncher;
+ private final RecentsView mRecentsView;
+ private final WorkspaceCard mWorkspaceCard;
+
+ private final AnimatedFloat mTransitionProgress = new AnimatedFloat(this::onTransitionProgress);
+ // The fraction representing the visibility of the RecentsView. This allows delaying the
+ // overall transition while the RecentsView is being shown or hidden.
+ private final AnimatedFloat mVisibilityMultiplier = new AnimatedFloat(this::onVisibilityProgress);
+
+ private boolean mIsRecentsScrollingToFirstTask;
public RecentsViewStateController(Launcher launcher) {
mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ mRecentsView.setStateController(this);
+
+ mWorkspaceCard = (WorkspaceCard) mRecentsView.getChildAt(0);
+ mWorkspaceCard.setup(launcher);
}
@Override
public void setState(LauncherState state) {
- setState(state, NO_ANIM_PROPERTY_SETTER);
+ 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
- public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
- AnimatorSet anim, AnimationConfig config) {
- setState(toState, new AnimatedPropertySetter(config.duration, layerViews, anim));
+ public void setStateWithAnimation(final LauncherState toState,
+ AnimatorSetBuilder builder, AnimationConfig config) {
+ boolean settingEnabled = Utilities.getPrefs(mLauncher)
+ .getBoolean("pref_scroll_to_first_task", false);
+ mIsRecentsScrollingToFirstTask = mLauncher.isInState(NORMAL) && toState == OVERVIEW
+ && settingEnabled;
+ // TODO: Instead of animating the workspace translationX, move the contents
+ mWorkspaceCard.setWorkspaceScrollingEnabled(mIsRecentsScrollingToFirstTask);
+
+ // Scroll to the workspace card before changing to the NORMAL state.
+ int currPage = mRecentsView.getCurrentPage();
+ if (toState == NORMAL && currPage != 0 && !config.userControlled) {
+ int maxSnapDuration = PagedView.SLOW_PAGE_SNAP_ANIMATION_DURATION;
+ int durationPerPage = maxSnapDuration / 10;
+ int snapDuration = Math.min(maxSnapDuration, durationPerPage * currPage);
+ mRecentsView.snapToPage(0, snapDuration);
+ builder.setStartDelay(snapDuration);
+ }
+
+ ObjectAnimator progressAnim =
+ mTransitionProgress.animateToValue(toState == OVERVIEW ? 1 : 0);
+ progressAnim.setDuration(config.duration);
+ progressAnim.setInterpolator(Interpolators.LINEAR);
+ progressAnim.addListener(new AnimationSuccessListener() {
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mWorkspaceCard.setWorkspaceScrollingEnabled(toState == OVERVIEW);
+ mRecentsView.setCurrentPage(mRecentsView.getPageNearestToCenterOfScreen());
+ }
+ });
+ builder.play(progressAnim);
+
+ ObjectAnimator visibilityAnim = animateVisibility(toState == OVERVIEW);
+ visibilityAnim.setDuration(config.duration);
+ visibilityAnim.setInterpolator(Interpolators.LINEAR);
+ builder.play(visibilityAnim);
}
- private void setState(LauncherState state, PropertySetter setter) {
- setter.setViewAlpha(null, mLauncher.getOverviewPanel(),
- state == LauncherState.OVERVIEW ? 1 : 0);
+ public void setVisibility(boolean isVisible) {
+ mVisibilityMultiplier.cancelAnimation();
+ mRecentsView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
+ mVisibilityMultiplier.updateValue(isVisible ? 1 : 0);
+ }
+
+ public ObjectAnimator animateVisibility(boolean isVisible) {
+ ObjectAnimator anim = mVisibilityMultiplier.animateToValue(isVisible ? 1 : 0);
+ if (isVisible) {
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mRecentsView.setVisibility(View.VISIBLE);
+ }
+ });
+ } else {
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ mRecentsView.setVisibility(View.GONE);
+ }
+ });
+ }
+ return anim;
+ }
+
+ public void setTransitionProgress(float progress) {
+ mTransitionProgress.cancelAnimation();
+ mTransitionProgress.updateValue(progress);
+ }
+
+ private void onTransitionProgress() {
+ applyProgress();
+ if (mIsRecentsScrollingToFirstTask) {
+ int scrollForFirstTask = mRecentsView.getScrollForPage(mRecentsView.getFirstTaskIndex());
+ int scrollForPage0 = mRecentsView.getScrollForPage(0);
+ mRecentsView.setScrollX((int) (mTransitionProgress.value * scrollForFirstTask
+ + (1 - mTransitionProgress.value) * scrollForPage0));
+ }
+ }
+
+ private void onVisibilityProgress() {
+ applyProgress();
+ }
+
+ private void applyProgress() {
+ mRecentsView.setAlpha(mTransitionProgress.value * mVisibilityMultiplier.value);
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java b/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java
new file mode 100644
index 0000000..651a753
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaggedAnimatorSetBuilder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import android.animation.Animator;
+import android.util.SparseArray;
+
+import com.android.launcher3.anim.AnimatorSetBuilder;
+
+import java.util.Collections;
+import java.util.List;
+
+public class TaggedAnimatorSetBuilder extends AnimatorSetBuilder {
+
+ /**
+ * Map of the index in {@link #mAnims} to tag. All the animations in {@link #mAnims} starting
+ * from this index correspond to the tag (until a new tag is specified for an index)
+ */
+ private final SparseArray<Object> mTags = new SparseArray<>();
+
+ @Override
+ public void startTag(Object obj) {
+ mTags.put(mAnims.size(), obj);
+ }
+
+ public List<Animator> getAnimationsForTag(Object tag) {
+ int startIndex = mTags.indexOfValue(tag);
+ if (startIndex < 0) {
+ return Collections.emptyList();
+ }
+ int startPos = mTags.keyAt(startIndex);
+
+ int endIndex = startIndex + 1;
+ int endPos = endIndex >= mTags.size() ? mAnims.size() : mTags.keyAt(endIndex);
+
+ return mAnims.subList(startPos, endPos);
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
new file mode 100644
index 0000000..2695054
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.support.animation.SpringAnimation;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.FloatRange;
+import com.android.launcher3.util.TouchController;
+import com.android.quickstep.TouchInteractionService;
+
+import java.util.ArrayList;
+
+/**
+ * Handles vertical touch gesture on the DragLayer
+ */
+public class TwoStepSwipeController extends AnimatorListenerAdapter
+ implements TouchController, SwipeDetector.Listener {
+
+ private static final String TAG = "TwoStepSwipeController";
+
+ private static final float RECATCH_REJECTION_FRACTION = .0875f;
+ private static final int SINGLE_FRAME_MS = 16;
+ private static final long QUICK_SNAP_TO_OVERVIEW_DURATION = 250;
+
+ // Progress after which the transition is assumed to be a success in case user does not fling
+ private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
+
+ /**
+ * Index of the vertical swipe handles in {@link LauncherStateManager#getStateHandlers()}.
+ */
+ private static final int SWIPE_HANDLER_INDEX = 0;
+
+ /**
+ * Index of various UI handlers in {@link LauncherStateManager#getStateHandlers()} not related
+ * to vertical swipe.
+ */
+ private static final int OTHER_HANDLERS_START_INDEX = SWIPE_HANDLER_INDEX + 1;
+
+ // Swipe progress range (when starting from NORMAL state) where OVERVIEW state is allowed
+ private static final float MIN_PROGRESS_TO_OVERVIEW = 0.1f;
+ private static final float MAX_PROGRESS_TO_OVERVIEW = 0.4f;
+
+ private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0;
+ private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1;
+ private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
+ private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
+ private static final int FLAG_DISABLED_TWO_TARGETS = 1 << 5;
+ private static final int FLAG_DISABLED_BACK_TARGET = 1 << 6;
+
+ private final Launcher mLauncher;
+ private final SwipeDetector mDetector;
+
+ private boolean mNoIntercept;
+ private int mStartContainerType;
+
+ private DragPauseDetector mDragPauseDetector;
+ private FloatRange mOverviewProgressRange;
+ private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder;
+ private AnimatorSet mQuickOverviewAnimation;
+ private boolean mAnimatingToOverview;
+ private CroppedAnimationController mCroppedAnimationController;
+
+ private AnimatorPlaybackController mCurrentAnimation;
+ private LauncherState mFromState;
+ private LauncherState mToState;
+
+ private float mStartProgress;
+ // Ratio of transition process [0, 1] to drag displacement (px)
+ private float mProgressMultiplier;
+
+ private SpringAnimationHandler[] mSpringHandlers;
+
+ public TwoStepSwipeController(Launcher l) {
+ mLauncher = l;
+ mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
+ }
+
+ private boolean canInterceptTouch(MotionEvent ev) {
+ if (mCurrentAnimation != null) {
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (mLauncher.isInState(NORMAL)) {
+ if ((ev.getEdgeFlags() & EDGE_NAV_BAR) != 0 &&
+ !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ // On normal swipes ignore edge swipes
+ return false;
+ }
+ } else if (mLauncher.isInState(ALL_APPS)) {
+ if (!mLauncher.getAppsView().shouldContainerScroll(ev)) {
+ return false;
+ }
+ } else {
+ // Don't listen for the swipe gesture if we are already in some other state.
+ return false;
+ }
+ if (mAnimatingToOverview) {
+ return false;
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ if (mCurrentAnimation != null && animation == mCurrentAnimation.getOriginalTarget()) {
+ Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
+ clearState();
+ }
+ }
+
+ private void initSprings() {
+ AllAppsContainerView appsView = mLauncher.getAppsView();
+
+ SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
+ if (handler == null) {
+ mSpringHandlers = new SpringAnimationHandler[0];
+ return;
+ }
+
+ ArrayList<SpringAnimationHandler> handlers = new ArrayList<>();
+ handlers.add(handler);
+
+ SpringAnimation searchSpring = appsView.getSearchUiManager().getSpringForFling();
+ if (searchSpring != null) {
+ SpringAnimationHandler searchHandler =
+ new SpringAnimationHandler(Y_DIRECTION, handler.getFactory());
+ searchHandler.add(searchSpring, true /* setDefaultValues */);
+ handlers.add(searchHandler);
+ }
+
+ mSpringHandlers = handlers.toArray(new SpringAnimationHandler[handlers.size()]);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ mNoIntercept = !canInterceptTouch(ev);
+ if (mNoIntercept) {
+ return false;
+ }
+
+ // Now figure out which direction scroll events the controller will start
+ // calling the callbacks.
+ final int directionsToDetectScroll;
+ boolean ignoreSlopWhenSettling = false;
+
+ if (mCurrentAnimation != null) {
+ if (mCurrentAnimation.getProgressFraction() > 1 - RECATCH_REJECTION_FRACTION) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ } else if (mCurrentAnimation.getProgressFraction() < RECATCH_REJECTION_FRACTION ) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
+ } else {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
+ ignoreSlopWhenSettling = true;
+ }
+ } else {
+ if (mLauncher.isInState(ALL_APPS)) {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
+ mStartContainerType = ContainerType.ALLAPPS;
+ } else {
+ directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
+ mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ?
+ ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+ }
+ }
+
+ mDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, ignoreSlopWhenSettling);
+
+ if (mSpringHandlers == null) {
+ initSprings();
+ }
+ }
+
+ if (mNoIntercept) {
+ return false;
+ }
+
+ onControllerTouchEvent(ev);
+ return mDetector.isDraggingOrSettling();
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ for (SpringAnimationHandler h : mSpringHandlers) {
+ h.addMovement(ev);
+ }
+ return mDetector.onTouchEvent(ev);
+ }
+
+ @Override
+ public void onDragStart(boolean start) {
+ if (mCurrentAnimation == null) {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+
+ mDragPauseDetector = new DragPauseDetector(this::onDragPauseDetected);
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
+ if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+ mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_TWO_TARGETS);
+ }
+
+ mOverviewProgressRange = new FloatRange();
+ mOverviewProgressRange.start = mLauncher.isInState(NORMAL)
+ ? MIN_PROGRESS_TO_OVERVIEW
+ : 1 - MAX_PROGRESS_TO_OVERVIEW;
+ mOverviewProgressRange.end = mOverviewProgressRange.start
+ + MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW;
+
+ // Build current animation
+ mFromState = mLauncher.getStateManager().getState();
+ mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
+
+ if (mToState == NORMAL && mLauncher.getStateManager().getLastState() == OVERVIEW) {
+ mToState = OVERVIEW;
+ mDragPauseDetector.addDisabledFlags(FLAG_DISABLED_BACK_TARGET);
+ }
+
+ mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder();
+ mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
+ mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
+
+ if (!TouchInteractionService.isConnected()) {
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
+ }
+
+ mCurrentAnimation.getTarget().addListener(this);
+ mStartProgress = 0;
+ mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
+ mCurrentAnimation.dispatchOnStart();
+ } else {
+ mCurrentAnimation.pause();
+ mStartProgress = mCurrentAnimation.getProgressFraction();
+
+ mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
+ updatePauseDetectorRangeFlag();
+ }
+
+ for (SpringAnimationHandler h : mSpringHandlers) {
+ h.skipToEnd();
+ }
+ }
+
+ private float getShiftRange() {
+ return mLauncher.getAllAppsController().getShiftRange();
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float deltaProgress = mProgressMultiplier * displacement;
+ mCurrentAnimation.setPlayFraction(deltaProgress + mStartProgress);
+
+ updatePauseDetectorRangeFlag();
+ mDragPauseDetector.onDrag(velocity);
+
+ return true;
+ }
+
+ private void updatePauseDetectorRangeFlag() {
+ if (mOverviewProgressRange.contains(mCurrentAnimation.getProgressFraction())) {
+ mDragPauseDetector.clearDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
+ } else {
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE);
+ }
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
+
+ final int logAction;
+ LauncherState targetState;
+ final float progress = mCurrentAnimation.getProgressFraction();
+
+ if (fling) {
+ logAction = Touch.FLING;
+ targetState = velocity < 0 ? ALL_APPS : mLauncher.getStateManager().getLastState();
+ // snap to top or bottom using the release velocity
+ } else {
+ logAction = Touch.SWIPE;
+ targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
+ }
+
+ if (fling && targetState == ALL_APPS) {
+ for (SpringAnimationHandler h : mSpringHandlers) {
+ // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
+ h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
+ }
+ }
+
+ float endProgress;
+
+ if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
+ targetState = OVERVIEW;
+ endProgress = OVERVIEW.getVerticalProgress(mLauncher);
+ if (mFromState == NORMAL) {
+ endProgress = 1 - endProgress;
+ }
+ } else if (targetState == mToState) {
+ endProgress = 1;
+ } else {
+ endProgress = 0;
+ }
+
+ LauncherState targetStateFinal = targetState;
+ mCurrentAnimation.setEndAction(() ->
+ onSwipeInteractionCompleted(targetStateFinal, logAction));
+
+ float nextFrameProgress = Utilities.boundToRange(
+ progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
+
+ ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
+ anim.setFloatValues(nextFrameProgress, endProgress);
+ anim.setDuration(
+ SwipeDetector.calculateDuration(velocity, Math.abs(endProgress - progress)));
+ anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
+ anim.start();
+ }
+
+ private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+ if (targetState != mFromState) {
+ // Transition complete. log the action
+ mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
+ mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
+ mStartContainerType,
+ mFromState.containerType,
+ mToState.containerType,
+ mLauncher.getWorkspace().getCurrentPage());
+ }
+ clearState();
+
+ // TODO: mQuickOverviewAnimation might still be running in which changing a state instantly
+ // may cause a jump. Animate the state change with a short duration in this case?
+ mLauncher.getStateManager().goToState(targetState, false /* animated */);
+ }
+
+ private void onDragPauseDetected() {
+ final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1);
+ twoStepAnimator.setDuration(mCurrentAnimation.getDuration());
+ StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
+
+ // Change the current animation to only play the vertical handle
+ AnimatorSet anim = new AnimatorSet();
+ anim.playTogether(mTaggedAnimatorSetBuilder.getAnimationsForTag(
+ handlers[SWIPE_HANDLER_INDEX]));
+ anim.play(twoStepAnimator);
+ mCurrentAnimation = mCurrentAnimation.cloneFor(anim);
+
+ AnimatorSetBuilder builder = new AnimatorSetBuilder();
+ AnimationConfig config = new AnimationConfig();
+ config.duration = QUICK_SNAP_TO_OVERVIEW_DURATION;
+ for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
+ handlers[i].setStateWithAnimation(OVERVIEW, builder, config);
+ }
+ mQuickOverviewAnimation = builder.build();
+ mQuickOverviewAnimation.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ onQuickOverviewAnimationComplete(twoStepAnimator);
+ }
+ });
+ mQuickOverviewAnimation.start();
+ }
+
+ private void onQuickOverviewAnimationComplete(ValueAnimator animator) {
+ if (mAnimatingToOverview) {
+ return;
+ }
+
+ // For the remainder to the interaction, the user can either go to the ALL_APPS state or
+ // the OVERVIEW state.
+ // The remaining state handlers are on the OVERVIEW state. Create one animation towards the
+ // ALL_APPS state and only call it when the user moved above the current range.
+ AnimationConfig config = new AnimationConfig();
+ config.duration = (long) (2 * getShiftRange());
+ config.userControlled = true;
+
+ AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder();
+ StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
+ for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
+ handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config);
+ }
+
+ mCroppedAnimationController = new CroppedAnimationController(
+ AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration),
+ new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0));
+ animator.addUpdateListener(mCroppedAnimationController);
+ }
+
+ private void clearState() {
+ mCurrentAnimation = null;
+ mTaggedAnimatorSetBuilder = null;
+ if (mDragPauseDetector != null) {
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_CANCEL_STATE);
+ }
+ mDragPauseDetector = null;
+
+ if (mQuickOverviewAnimation != null) {
+ mQuickOverviewAnimation.cancel();
+ mQuickOverviewAnimation = null;
+ }
+ mCroppedAnimationController = null;
+ mAnimatingToOverview = false;
+
+ mDetector.finishedScrolling();
+ }
+
+ /**
+ * {@link AnimatorUpdateListener} which controls another animation for a fraction of range
+ */
+ private static class CroppedAnimationController implements AnimatorUpdateListener {
+
+ private final AnimatorPlaybackController mTarget;
+ private final FloatRange mRange;
+
+ CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) {
+ mTarget = target;
+ mRange = range;
+ }
+
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator valueAnimator) {
+ float fraction = valueAnimator.getAnimatedFraction();
+
+ if (mRange.start < mRange.end) {
+ if (fraction <= mRange.start) {
+ mTarget.setPlayFraction(0);
+ } else if (fraction >= mRange.end) {
+ mTarget.setPlayFraction(1);
+ } else {
+ mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+ }
+ } else if (mRange.start > mRange.end) {
+ if (fraction >= mRange.start) {
+ mTarget.setPlayFraction(0);
+ } else if (fraction <= mRange.end) {
+ mTarget.setPlayFraction(1);
+ } else {
+ mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+ }
+ } else {
+ // mRange.start == mRange.end
+ mTarget.setPlayFraction(0);
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 20abdc7..7bd4366 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,22 +16,42 @@
package com.android.launcher3.uioverrides;
-import android.content.Intent;
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PointF;
import android.view.View.AccessibilityDelegate;
-import android.widget.PopupMenu;
-import android.widget.Toast;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.R;
-import com.android.launcher3.VerticalSwipeController;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
-import com.android.launcher3.widget.WidgetsFullSheet;
+import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.RecentsView;
+import com.android.systemui.shared.recents.view.RecentsTransition;
public class UiFactory {
+ private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION =
+ "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS";
+
+ public static final boolean USE_HARDWARE_BITMAP = false; // FeatureFlags.IS_DOGFOOD_BUILD;
+
public static TouchController[] createTouchControllers(Launcher launcher) {
- return new TouchController[] {new VerticalSwipeController(launcher)};
+ if (FeatureFlags.ENABLE_TWO_SWIPE_TARGETS) {
+ return new TouchController[] {
+ new IgnoreTouchesInQuickScrub(),
+ new EdgeSwipeController(launcher),
+ new TwoStepSwipeController(launcher),
+ new OverviewSwipeController(launcher)};
+ } else {
+ return new TouchController[] {
+ new IgnoreTouchesInQuickScrub(),
+ new TwoStepSwipeController(launcher),
+ new OverviewSwipeController(launcher)};
+ }
}
public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
@@ -44,28 +64,28 @@
new RecentsViewStateController(launcher)};
}
- public static void onWorkspaceLongPress(Launcher launcher) {
- PopupMenu menu = new PopupMenu(launcher, launcher.getWorkspace().getPageIndicator());
- menu.getMenu().add(R.string.wallpaper_button_text).setOnMenuItemClickListener((i) -> {
- launcher.onClickWallpaperPicker(null);
- return true;
- });
- menu.getMenu().add(R.string.widget_button_text).setOnMenuItemClickListener((i) -> {
- if (launcher.getPackageManager().isSafeMode()) {
- Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
- } else {
- WidgetsFullSheet.show(launcher, true /* animated */);
- }
- return true;
- });
- if (launcher.hasSettings()) {
- menu.getMenu().add(R.string.settings_button_text).setOnMenuItemClickListener((i) -> {
- launcher.startActivity(new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
- .setPackage(launcher.getPackageName())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
- return true;
- });
+ public static void onWorkspaceLongPress(Launcher launcher, PointF touchPoint) {
+ 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) {
+ return RecentsTransition.createHardwareBitmap(width, height, renderer::render);
+ } else {
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ renderer.render(new Canvas(result));
+ return result;
}
- menu.show();
+ }
+
+ public static void resetOverview(Launcher launcher) {
+ RecentsView recents = launcher.getOverviewPanel();
+ recents.reset();
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
new file mode 100644
index 0000000..8533502
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Workspace;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.RecentsView.PageCallbacks;
+import com.android.quickstep.RecentsView.ScrollState;
+
+public class WorkspaceCard extends View implements PageCallbacks, OnClickListener {
+
+ private final Rect mTempRect = new Rect();
+
+ private Launcher mLauncher;
+ private Workspace mWorkspace;
+
+ private float mLinearInterpolationForPage2 = 1;
+ private float mTranslateXPage0, mTranslateXPage1;
+ private float mExtraScrollShift;
+
+ private boolean mIsWorkspaceScrollingEnabled;
+
+ public WorkspaceCard(Context context) {
+ this(context, null);
+ }
+
+ public WorkspaceCard(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WorkspaceCard(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setOnClickListener(this);
+ }
+
+ /**
+ * Draw nothing.
+ */
+ @Override
+ public void draw(Canvas canvas) { }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ // Initiate data
+ mLinearInterpolationForPage2 = RecentsView.getScaledDownPageRect(
+ mLauncher.getDeviceProfile(), mLauncher, mTempRect);
+
+ float[] scale = OverviewState.getScaleAndTranslationForPageRect(mLauncher, 0, mTempRect);
+ mTranslateXPage0 = scale[1];
+ mTranslateXPage1 = OverviewState
+ .getScaleAndTranslationForPageRect(mLauncher,
+ getResources().getDimension(R.dimen.workspace_overview_offset_x) / scale[0],
+ mTempRect)[1];
+
+ mExtraScrollShift = 0;
+ if (mWorkspace != null && getWidth() > 0) {
+ float workspaceWidth = mWorkspace.getNormalChildWidth() * scale[0];
+ mExtraScrollShift = (workspaceWidth - getWidth()) / 2;
+ setScaleX(workspaceWidth / getWidth());
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ mLauncher.getStateManager().goToState(NORMAL);
+ }
+
+ public void setup(Launcher launcher) {
+ mLauncher = launcher;
+ mWorkspace = mLauncher.getWorkspace();
+ }
+
+ public void setWorkspaceScrollingEnabled(boolean isEnabled) {
+ mIsWorkspaceScrollingEnabled = isEnabled;
+ }
+
+ @Override
+ public int onPageScroll(ScrollState scrollState) {
+ float factor = scrollState.linearInterpolation;
+ float translateX = scrollState.distanceFromScreenCenter;
+ if (mIsWorkspaceScrollingEnabled) {
+ float shift = factor * (mTranslateXPage1 - mTranslateXPage0);
+ mWorkspace.setTranslationX(shift + mTranslateXPage0);
+ translateX += shift;
+ }
+
+ setTranslationX(translateX);
+
+ // If the workspace card is still the first page, shift all the other pages.
+ if (scrollState.linearInterpolation > mLinearInterpolationForPage2) {
+ scrollState.prevPageExtraWidth = 0;
+ } else if (mLinearInterpolationForPage2 > 0) {
+ scrollState.prevPageExtraWidth = mExtraScrollShift *
+ (1 - scrollState.linearInterpolation / mLinearInterpolationForPage2);
+ } else {
+ scrollState.prevPageExtraWidth = mExtraScrollShift;
+ }
+ return SCROLL_TYPE_WORKSPACE;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java
index 1f6781e..214b3f3 100644
--- a/quickstep/src/com/android/quickstep/AnimatedFloat.java
+++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java
@@ -47,9 +47,7 @@
}
public ObjectAnimator animateToValue(float v) {
- if (mValueAnimator != null) {
- mValueAnimator.cancel();
- }
+ cancelAnimation();
mValueAnimator = ObjectAnimator.ofFloat(this, VALUE, v);
mValueAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -73,6 +71,12 @@
}
}
+ public void cancelAnimation() {
+ if (mValueAnimator != null) {
+ mValueAnimator.cancel();
+ }
+ }
+
public ObjectAnimator getCurrentAnimation() {
return mValueAnimator;
}
diff --git a/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
new file mode 100644
index 0000000..b3ebd77
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BaseSwipeInteractionHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.support.annotation.WorkerThread;
+
+import com.android.launcher3.states.InternalStateHandler;
+import com.android.quickstep.TouchConsumer.InteractionType;
+
+public abstract class BaseSwipeInteractionHandler extends InternalStateHandler {
+
+ protected Runnable mGestureEndCallback;
+
+ public void setGestureEndCallback(Runnable gestureEndCallback) {
+ mGestureEndCallback = gestureEndCallback;
+ }
+
+ public void reset() {}
+
+ @WorkerThread
+ public abstract void onGestureStarted();
+
+ @WorkerThread
+ public abstract void onGestureEnded(float endVelocity);
+
+ public abstract void updateInteractionType(@InteractionType int interactionType);
+
+ @WorkerThread
+ public abstract void onQuickScrubEnd();
+
+ @WorkerThread
+ public abstract void onQuickScrubProgress(float progress);
+
+ @WorkerThread
+ public abstract void updateDisplacement(float displacement);
+}
diff --git a/quickstep/src/com/android/quickstep/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/LauncherLayoutListener.java
new file mode 100644
index 0000000..fbdbe7a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/LauncherLayoutListener.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.graphics.Rect;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+
+/**
+ * Floating view which shows the task snapshot allowing it to be dragged and placed.
+ */
+public class LauncherLayoutListener extends AbstractFloatingView implements Insettable {
+
+ private final Launcher mLauncher;
+ private WindowTransformSwipeHandler mHandler;
+
+ public LauncherLayoutListener(Launcher launcher) {
+ super(launcher, null);
+ mLauncher = launcher;
+ setVisibility(INVISIBLE);
+ }
+
+ public void setHandler(WindowTransformSwipeHandler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ if (mHandler != null) {
+ mHandler.onLauncherLayoutChanged();
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ return false;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (mIsOpen) {
+ mIsOpen = false;
+ // We don't support animate.
+ mLauncher.getDragLayer().removeView(this);
+
+ if (mHandler != null) {
+ mHandler.layoutListenerClosed();
+ }
+ }
+ }
+
+ public void open() {
+ if (!mIsOpen) {
+ mLauncher.getDragLayer().addView(this);
+ mIsOpen = true;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ setMeasuredDimension(1, 1);
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // We should probably log the weather
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_QUICKSTEP_PREVIEW) != 0;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
new file mode 100644
index 0000000..6e92d83
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static 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;
+
+/**
+ * Helper class for batching input events
+ */
+@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();
+
+ // We use two arrays and swap the current index when one array is being consumed
+ private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()};
+ private int mCurrentIndex = 0;
+
+ private final Runnable mMainFrameCallback = this::frameCallbackForMainChoreographer;
+ private final Runnable mInterimFrameCallback = this::frameCallbackForInterimChoreographer;
+
+ private final Choreographer mMainChoreographer;
+
+ private final TouchConsumer mConsumer;
+
+ private Choreographer mInterimChoreographer;
+ private Choreographer mCurrentChoreographer;
+
+ private Runnable mCurrentRunnable;
+
+ public MotionEventQueue(Choreographer choreographer, TouchConsumer consumer) {
+ mMainChoreographer = choreographer;
+ mConsumer = consumer;
+
+ mCurrentChoreographer = mMainChoreographer;
+ mCurrentRunnable = mMainFrameCallback;
+ setInterimChoreographerLocked(consumer.getIntrimChoreographer(this));
+ }
+
+ public void setInterimChoreographer(Choreographer choreographer) {
+ synchronized (mExecutionLock) {
+ synchronized (mArrays) {
+ 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()) {
+ ChoreographerCompat.postInputFrame(mCurrentChoreographer, mCurrentRunnable);
+ }
+
+ int eventAction = event.getAction();
+ if (eventAction == ACTION_MOVE && array.lastEventAction == ACTION_MOVE) {
+ // Replace and recycle the last event
+ array.set(array.size() - 1, event).recycle();
+ } else {
+ array.add(event);
+ array.lastEventAction = eventAction;
+ }
+ }
+ }
+
+ private void frameCallbackForMainChoreographer() {
+ runFor(mMainChoreographer);
+ }
+
+ private void frameCallbackForInterimChoreographer() {
+ runFor(mInterimChoreographer);
+ }
+
+ private void runFor(Choreographer caller) {
+ synchronized (mExecutionLock) {
+ EventArray array = swapAndGetCurrentArray(caller);
+ int size = array.size();
+ for (int i = 0; i < size; i++) {
+ MotionEvent event = array.get(i);
+ 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();
+ array.lastEventAction = ACTION_CANCEL;
+ }
+ }
+
+ private EventArray swapAndGetCurrentArray(Choreographer caller) {
+ synchronized (mArrays) {
+ if (caller != mCurrentChoreographer) {
+ return mEmptyArray;
+ }
+ EventArray current = mArrays[mCurrentIndex];
+ mCurrentIndex = mCurrentIndex ^ 1;
+ return current;
+ }
+ }
+
+ 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;
+
+ public EventArray() {
+ super(4);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index cca2729..7a74176 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -55,4 +55,8 @@
public void addCallback(int stateMask, Runnable callback) {
mCallbacks.put(stateMask, callback);
}
+
+ public int getState() {
+ return mState;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index dc7d648..ff7d434 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -15,6 +15,12 @@
*/
package com.android.quickstep;
+
+import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SWITCH;
+import static com.android.quickstep.TouchConsumer.isInteractionQuick;
+
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.RectEvaluator;
@@ -26,48 +32,41 @@
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.os.Build;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.support.annotation.BinderThread;
import android.support.annotation.UiThread;
-import android.util.DisplayMetrics;
-import android.view.Choreographer;
-import android.view.Choreographer.FrameCallback;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
+import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.TouchConsumer.InteractionType;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import java.util.concurrent.atomic.AtomicBoolean;
-
@TargetApi(Build.VERSION_CODES.O)
-public class NavBarSwipeInteractionHandler extends InternalStateHandler implements FrameCallback {
+public class NavBarSwipeInteractionHandler extends BaseSwipeInteractionHandler implements
+ OnResumeCallback {
private static final int STATE_LAUNCHER_READY = 1 << 0;
- private static final int STATE_RECENTS_DELAY_COMPLETE = 1 << 1;
- private static final int STATE_LOAD_PLAN_READY = 1 << 2;
- private static final int STATE_RECENTS_FULLY_VISIBLE = 1 << 3;
private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 4;
private static final int STATE_SCALED_SNAPSHOT_RECENTS = 1 << 5;
private static final int STATE_SCALED_SNAPSHOT_APP = 1 << 6;
- private static final long RECENTS_VIEW_VISIBILITY_DELAY = 120;
- private static final long RECENTS_VIEW_VISIBILITY_DURATION = 150;
- private static final long DEFAULT_SWIPE_DURATION = 200;
+ private static final long MAX_SWIPE_DURATION = 200;
+ private static final long MIN_SWIPE_DURATION = 80;
// Ideal velocity for a smooth transition
private static final float PIXEL_PER_MS = 2f;
@@ -90,9 +89,6 @@
// animated to 1, so allow for a smooth transition.
private final AnimatedFloat mActivityMultiplier = new AnimatedFloat(this::updateFinalShift);
- private final Choreographer mChoreographer;
- private final AtomicBoolean mFrameScheduled = new AtomicBoolean(false);
-
private final int mRunningTaskId;
private final Context mContext;
@@ -101,34 +97,42 @@
private Launcher mLauncher;
private SnapshotDragView mDragView;
private RecentsView mRecentsView;
+ private QuickScrubController mQuickScrubController;
private Hotseat mHotseat;
- private RecentsTaskLoadPlan mLoadPlan;
+
+ private boolean mWasLauncherAlreadyVisible;
private boolean mLauncherReady;
private boolean mTouchEndHandled;
+ private float mCurrentDisplacement;
+
+ private @InteractionType int mInteractionType;
+ private boolean mStartedQuickScrubFromHome;
private Bitmap mTaskSnapshot;
- // These are updated on the binder thread, and eventually picked up on doFrame
- private volatile float mCurrentDisplacement;
- private volatile float mEndVelocity;
- private volatile boolean mTouchEnded = false;
-
- NavBarSwipeInteractionHandler(
- RunningTaskInfo runningTaskInfo, Choreographer choreographer, Context context) {
- mRunningTaskId = runningTaskInfo.id;
- mChoreographer = choreographer;
+ NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context,
+ @InteractionType int interactionType) {
mContext = context;
+ mInteractionType = interactionType;
+ mRunningTaskId = runningTaskInfo.id;
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+ // TODO: If in multi window mode, dp = dp.getMultiWindowProfile()
+ dp = dp.copy(mContext);
+ // TODO: Use different insets for multi-window mode
+ dp.updateInsets(mStableInsets);
+ RecentsView.getPageRect(dp, mContext, mTargetRect);
+ mSourceRect.set(0, 0, dp.widthPx - mStableInsets.left - mStableInsets.right,
+ dp.heightPx - mStableInsets.top - mStableInsets.bottom);
+
// Build the state callback
mStateCallback = new MultiStateCallback();
mStateCallback.addCallback(STATE_LAUNCHER_READY, this::onLauncherReady);
- mStateCallback.addCallback(STATE_LOAD_PLAN_READY | STATE_RECENTS_DELAY_COMPLETE,
- this::setTaskPlanToUi);
mStateCallback.addCallback(STATE_SCALED_SNAPSHOT_APP, this::resumeLastTask);
- mStateCallback.addCallback(STATE_RECENTS_FULLY_VISIBLE | STATE_SCALED_SNAPSHOT_RECENTS
- | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
+ mStateCallback.addCallback(
+ STATE_SCALED_SNAPSHOT_RECENTS | STATE_ACTIVITY_MULTIPLIER_COMPLETE,
this::onAnimationToLauncherComplete);
mStateCallback.addCallback(STATE_LAUNCHER_READY | STATE_SCALED_SNAPSHOT_APP,
this::cleanupLauncher);
@@ -138,12 +142,8 @@
mLauncherReady = true;
executeFrameUpdate();
- // Wait for some time before loading recents so that the first frame is fast
- new Handler().postDelayed(() -> mStateCallback.setState(STATE_RECENTS_DELAY_COMPLETE),
- RECENTS_VIEW_VISIBILITY_DELAY);
-
- long duration = Math.min(DEFAULT_SWIPE_DURATION,
- Math.max((long) (-mCurrentDisplacement / PIXEL_PER_MS), 0));
+ long duration = Math.min(MAX_SWIPE_DURATION,
+ Math.max((long) (-mCurrentDisplacement / PIXEL_PER_MS), MIN_SWIPE_DURATION));
if (mCurrentShift.getCurrentAnimation() != null) {
ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
@@ -168,110 +168,102 @@
@Override
public void onLauncherResume() {
+ TraceHelper.partitionSection("TouchInt", "Launcher On resume");
mDragView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mDragView.getViewTreeObserver().removeOnPreDrawListener(this);
mStateCallback.setState(STATE_LAUNCHER_READY);
+ TraceHelper.partitionSection("TouchInt", "Launcher drawn");
return true;
}
});
}
@Override
- protected void init(Launcher launcher, boolean alreadyOnHome) {
- AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
- launcher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome);
-
+ protected boolean init(Launcher launcher, boolean alreadyOnHome) {
+ launcher.setOnResumeCallback(this);
mLauncher = launcher;
+ mRecentsView = launcher.getOverviewPanel();
+ mRecentsView.showTask(mRunningTaskId);
+ mHotseat = mLauncher.getHotseat();
+ mWasLauncherAlreadyVisible = alreadyOnHome;
+
+ AbstractFloatingView.closeAllOpenViews(mLauncher, alreadyOnHome);
+ mLauncher.getStateManager().goToState(LauncherState.OVERVIEW, alreadyOnHome);
+
mDragView = new SnapshotDragView(mLauncher, mTaskSnapshot);
mLauncher.getDragLayer().addView(mDragView);
mDragView.setPivotX(0);
mDragView.setPivotY(0);
- mRecentsView = mLauncher.getOverviewPanel();
- mHotseat = mLauncher.getHotseat();
+
+ if (isInteractionQuick(mInteractionType)) {
+ updateUiForQuickScrub();
+ }
// Optimization
- mLauncher.getAppsView().setVisibility(View.GONE);
- mRecentsView.setVisibility(View.GONE);
+ if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ // All-apps search box is visible in vertical bar layout.
+ mLauncher.getAppsView().setVisibility(View.GONE);
+ }
+ TraceHelper.partitionSection("TouchInt", "Launcher on new intent");
+ return false;
}
- /**
- * This is updated on the binder thread and is picked up on the UI thread during the next
- * scheduled frame.
- * TODO: Instead of continuously scheduling frames, post the motion events to UI thread
- * (can ignore all continuous move events until the last move).
- */
- @BinderThread
- public void updateDisplacement(float displacement) {
- mCurrentDisplacement = displacement;
- scheduleFrameIfNeeded();
- }
+ public void updateInteractionType(@InteractionType int interactionType) {
+ Preconditions.assertUIThread();
+ if (mInteractionType != INTERACTION_NORMAL) {
+ throw new IllegalArgumentException(
+ "Can't change interaction type from " + mInteractionType);
+ }
+ if (!isInteractionQuick(interactionType)) {
+ throw new IllegalArgumentException(
+ "Can't change interaction type to " + interactionType);
+ }
+ mInteractionType = interactionType;
- @BinderThread
- public void endTouch(float endVelocity) {
- mEndVelocity = endVelocity;
- mTouchEnded = true;
- scheduleFrameIfNeeded();
- }
-
- private void scheduleFrameIfNeeded() {
- boolean alreadyScheduled = mFrameScheduled.getAndSet(true);
- if (!alreadyScheduled) {
- // TODO: Here we might end up scheduling one additional frame in some race conditions.
- // This can be avoided by synchronising postFrameCallback as well
- mChoreographer.postFrameCallback(this);
+ if (mLauncher != null) {
+ updateUiForQuickScrub();
}
}
- @Override
- public void doFrame(long l) {
- mFrameScheduled.set(false);
+ private void updateUiForQuickScrub() {
+ mStartedQuickScrubFromHome = mWasLauncherAlreadyVisible;
+ mQuickScrubController = mRecentsView.getQuickScrubController();
+ mQuickScrubController.onQuickScrubStart(mStartedQuickScrubFromHome);
+ animateToProgress(1f, MAX_SWIPE_DURATION);
+ if (mStartedQuickScrubFromHome) {
+ mDragView.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @UiThread
+ public void updateDisplacement(float displacement) {
+ mCurrentDisplacement = displacement;
executeFrameUpdate();
}
private void executeFrameUpdate() {
if (mLauncherReady) {
final float displacement = -mCurrentDisplacement;
- int hotseatHeight = mHotseat.getHeight();
- float translation = Utilities.boundToRange(displacement, 0, hotseatHeight);
- float shift = hotseatHeight == 0 ? 0 : translation / hotseatHeight;
+ int hotseatSize = getHotseatSize();
+ float translation = Utilities.boundToRange(displacement, 0, hotseatSize);
+ float shift = hotseatSize == 0 ? 0 : translation / hotseatSize;
mCurrentShift.updateValue(shift);
}
-
- if (mTouchEnded) {
- if (mTouchEndHandled) {
- return;
- }
- mTouchEndHandled = true;
- animateToFinalShift();
- }
}
@UiThread
private void updateFinalShift() {
- if (!mLauncherReady) {
+ if (!mLauncherReady || mStartedQuickScrubFromHome) {
return;
}
- if (mTargetRect.isEmpty()) {
- DragLayer dl = mLauncher.getDragLayer();
- mSourceRect.set(0, 0, dl.getWidth(), dl.getHeight());
- Rect targetPadding = RecentsView.getPadding(mLauncher);
- Rect insets = dl.getInsets();
- mTargetRect.set(
- targetPadding.left + insets.left,
- targetPadding.top + insets.top,
- mSourceRect.right - targetPadding.right - insets.right,
- mSourceRect.bottom - targetPadding.bottom - insets.bottom);
- mTargetRect.top += mLauncher.getResources()
- .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
- }
-
float shift = mCurrentShift.value * mActivityMultiplier.value;
- int hotseatHeight = mHotseat.getHeight();
- mHotseat.setTranslationY((1 - shift) * hotseatHeight);
+ AllAppsTransitionController controller = mLauncher.getAllAppsController();
+ float range = getHotseatSize() / controller.getShiftRange();
+ controller.setProgress(1 + (1 - shift) * range);
mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
@@ -280,50 +272,54 @@
mDragView.setTranslationY(mCurrentRect.top - mStableInsets.top * scale * shift);
mDragView.setScaleX(scale);
mDragView.setScaleY(scale);
+ // TODO: mDragView.getViewBounds().setClipLeft((int) (mStableInsets.left * shift));
mDragView.getViewBounds().setClipTop((int) (mStableInsets.top * shift));
+ // TODO: mDragView.getViewBounds().setClipRight((int) (mStableInsets.right * shift));
mDragView.getViewBounds().setClipBottom((int) (mStableInsets.bottom * shift));
}
- @UiThread
- public void setRecentsTaskLoadPlan(RecentsTaskLoadPlan loadPlan) {
- mLoadPlan = loadPlan;
- mStateCallback.setState(STATE_LOAD_PLAN_READY);
+ private int getHotseatSize() {
+ return mLauncher.getDeviceProfile().isVerticalBarLayout()
+ ? mHotseat.getWidth() : mHotseat.getHeight();
}
- private void setTaskPlanToUi() {
- mRecentsView.update(mLoadPlan);
- mRecentsView.setVisibility(View.VISIBLE);
-
- // Animate alpha
- mRecentsView.setAlpha(0);
- mRecentsView.animate().alpha(1).setDuration(RECENTS_VIEW_VISIBILITY_DURATION)
- .withEndAction(() -> mStateCallback.setState(STATE_RECENTS_FULLY_VISIBLE));
- }
+ @Override
+ public void onGestureStarted() { }
@UiThread
- private void animateToFinalShift() {
+ public void onGestureEnded(float endVelocity) {
+ if (mTouchEndHandled) {
+ return;
+ }
+ mTouchEndHandled = true;
+
Resources res = mContext.getResources();
float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity);
- boolean isFling = Math.abs(mEndVelocity) > flingThreshold;
+ boolean isFling = Math.abs(endVelocity) > flingThreshold;
- long duration = DEFAULT_SWIPE_DURATION;
+ long duration = MAX_SWIPE_DURATION;
final float endShift;
if (!isFling) {
endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
} else {
- endShift = mEndVelocity < 0 ? 1 : 0;
+ endShift = endVelocity < 0 ? 1 : 0;
float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity);
- if (Math.abs(mEndVelocity) > minFlingVelocity && mLauncherReady) {
- float distanceToTravel = (endShift - mCurrentShift.value) * mHotseat.getHeight();
+ if (Math.abs(endVelocity) > minFlingVelocity && mLauncherReady) {
+ float distanceToTravel = (endShift - mCurrentShift.value) * getHotseatSize();
// we want the page's snap velocity to approximately match the velocity at
// which the user flings, so we scale the duration by a value near to the
// derivative of the scroll interpolator at zero, ie. 5.
- duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / mEndVelocity));
+ duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
}
}
- ObjectAnimator anim = mCurrentShift.animateToValue(endShift).setDuration(duration);
+ animateToProgress(endShift, duration);
+ }
+
+ /** Animates to the given progress, where 0 is the current app and 1 is overview. */
+ private void animateToProgress(float progress, long duration) {
+ ObjectAnimator anim = mCurrentShift.animateToValue(progress).setDuration(duration);
anim.setInterpolator(Interpolators.SCROLL);
anim.addListener(new AnimationSuccessListener() {
@Override
@@ -337,30 +333,60 @@
@UiThread
private void resumeLastTask() {
- TaskKey key = null;
- if (mLoadPlan != null) {
- Task task = mLoadPlan.getTaskStack().findTaskWithId(mRunningTaskId);
+ RecentsTaskLoadPlan loadPlan = RecentsModel.getInstance(mContext).getLastLoadPlan();
+ if (loadPlan != null) {
+ Task task = loadPlan.getTaskStack().findTaskWithId(mRunningTaskId);
if (task != null) {
- key = task.key;
+ ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(task.key, opts,
+ null, null);
}
}
+ }
- if (key == null) {
- // TODO: We need a better way for this
- key = new TaskKey(mRunningTaskId, 0, null, UserHandle.myUserId(), 0);
+ public void reset() {
+ mCurrentShift.cancelAnimation();
+ if (mGestureEndCallback != null) {
+ mGestureEndCallback.run();
}
-
- ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
- ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(key, opts, null, null);
}
private void cleanupLauncher() {
+ reset();
+
// TODO: These should be done as part of ActivityOptions#OnAnimationStarted
- mHotseat.setTranslationY(0);
+ mLauncher.getStateManager().reapplyState();
mLauncher.setOnResumeCallback(() -> mDragView.close(false));
}
private void onAnimationToLauncherComplete() {
+ reset();
+
mDragView.close(false);
+ View currentRecentsPage = mRecentsView.getPageAt(mRecentsView.getCurrentPage());
+ if (currentRecentsPage instanceof TaskView) {
+ ((TaskView) currentRecentsPage).animateIconToScale(1f);
+ }
+ if (mInteractionType == INTERACTION_QUICK_SWITCH) {
+ if (mQuickScrubController != null) {
+ mQuickScrubController.onQuickSwitch();
+ }
+ }
+ }
+
+ public void onQuickScrubEnd() {
+ if (mQuickScrubController != null) {
+ mQuickScrubController.onQuickScrubEnd();
+ } else {
+ // TODO:
+ }
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ if (mQuickScrubController != null) {
+ mQuickScrubController.onQuickScrubProgress(progress);
+ } else {
+ // TODO:
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
new file mode 100644
index 0000000..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
new file mode 100644
index 0000000..488cd72
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
@@ -0,0 +1,474 @@
+/*
+ * 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 android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+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;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.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;
+import android.view.VelocityTracker;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.TraceHelper;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.AssistDataReceiver;
+import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RecentsAnimationListener;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.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
+ */
+public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
+ private static final String TAG = "ActivityTouchConsumer";
+
+ private static final long LAUNCHER_DRAW_TIMEOUT_MS = 150;
+
+ private final RunningTaskInfo mRunningTask;
+ private final RecentsModel mRecentsModel;
+ 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();
+ private int mActivePointerId = INVALID_POINTER_ID;
+ private boolean mTouchThresholdCrossed;
+ private int mTouchSlop;
+ private float mStartDisplacement;
+ 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, Choreographer backgroundThreadChoreographer,
+ @HitTarget int downHitTarget) {
+ super(base);
+ mRunningTask = runningTaskInfo;
+ mRecentsModel = recentsModel;
+ mHomeIntent = homeIntent;
+ mVelocityTracker = VelocityTracker.obtain();
+ mISystemUiProxy = systemUiProxy;
+ mMainThreadExecutor = mainThreadExecutor;
+ mBackgroundThreadChoreographer = backgroundThreadChoreographer;
+ mDownHitTarget = downHitTarget;
+ }
+
+ @Override
+ public void accept(MotionEvent ev) {
+ if (mVelocityTracker == null) {
+ return;
+ }
+ switch (ev.getActionMasked()) {
+ case ACTION_DOWN: {
+ TraceHelper.beginSection("TouchInt");
+ mActivePointerId = ev.getPointerId(0);
+ mDownPos.set(ev.getX(), ev.getY());
+ mLastPos.set(mDownPos);
+ mTouchSlop = ViewConfiguration.get(this).getScaledPagingTouchSlop();
+ mTouchThresholdCrossed = false;
+
+ // 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();
+ mDisplayRotation = display.getRotation();
+ WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+ break;
+ }
+ case ACTION_POINTER_UP: {
+ int ptrIdx = ev.getActionIndex();
+ int ptrId = ev.getPointerId(ptrIdx);
+ if (ptrId == mActivePointerId) {
+ final int newPointerIdx = ptrIdx == 0 ? 1 : 0;
+ mDownPos.set(
+ ev.getX(newPointerIdx) - (mLastPos.x - mDownPos.x),
+ ev.getY(newPointerIdx) - (mLastPos.y - mDownPos.y));
+ mLastPos.set(ev.getX(newPointerIdx), ev.getY(newPointerIdx));
+ mActivePointerId = ev.getPointerId(newPointerIdx);
+ }
+ break;
+ }
+ case ACTION_MOVE: {
+ int pointerIndex = ev.findPointerIndex(mActivePointerId);
+ if (pointerIndex == INVALID_POINTER_ID) {
+ break;
+ }
+ mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+
+ float displacement = ev.getY(pointerIndex) - mDownPos.y;
+ if (isNavBarOnRight()) {
+ displacement = ev.getX(pointerIndex) - mDownPos.x;
+ } else if (isNavBarOnLeft()) {
+ displacement = mDownPos.x - ev.getX(pointerIndex);
+ }
+ if (!mTouchThresholdCrossed) {
+ mTouchThresholdCrossed = Math.abs(displacement) >= mTouchSlop;
+ if (mTouchThresholdCrossed) {
+ mStartDisplacement = Math.signum(displacement) * mTouchSlop;
+
+ if (isUsingScreenShot()) {
+ startTouchTrackingForScreenshotAnimation();
+ } 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 if (mInteractionHandler != null) {
+ // Move
+ mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
+ }
+ break;
+ }
+ case ACTION_CANCEL:
+ // TODO: Should be different than ACTION_UP
+ case ACTION_UP: {
+ TraceHelper.endSection("TouchInt");
+
+ finishTouchTracking();
+ break;
+ }
+ }
+ }
+
+ private void notifyGestureStarted() {
+ if (mInteractionHandler == null) {
+ return;
+ }
+ // Notify the handler that the gesture has actually started
+ mInteractionHandler.onGestureStarted();
+
+ // Notify the system that we have started tracking the event
+ if (mISystemUiProxy != null) {
+ executeSafely(mISystemUiProxy::onRecentsAnimationStarted);
+ }
+ }
+
+ private boolean isNavBarOnRight() {
+ return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
+ }
+
+ private boolean isNavBarOnLeft() {
+ return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
+ }
+
+ private boolean isUsingScreenShot() {
+ return Utilities.getPrefs(this).getBoolean("pref_use_screenshot_for_swipe_up", false);
+ }
+
+ /**
+ * Called when the gesture has started.
+ */
+ private void startTouchTrackingForScreenshotAnimation() {
+ // Create the shared handler
+ final NavBarSwipeInteractionHandler handler =
+ new NavBarSwipeInteractionHandler(mRunningTask, this, INTERACTION_NORMAL);
+
+ TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
+
+ // Start the recents activity on a background thread
+ BackgroundExecutor.get().submit(() -> {
+ // Get the snap shot before
+ handler.setTaskSnapshot(getCurrentTaskSnapshot());
+
+ // Start the launcher activity with our custom handler
+ Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
+ startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
+ TraceHelper.partitionSection("TouchInt", "Home started");
+ });
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTask.id, null);
+ mInteractionHandler = handler;
+ mInteractionHandler.setGestureEndCallback(mEventQueue::reset);
+ }
+
+ private Bitmap getCurrentTaskSnapshot() {
+ TraceHelper.beginSection("TaskSnapshot");
+ // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
+ Point displaySize = new Point();
+ Display display = getSystemService(WindowManager.class).getDefaultDisplay();
+ display.getRealSize(displaySize);
+ int rotation = display.getRotation();
+ // The rotation is backwards in landscape, so flip it.
+ if (rotation == Surface.ROTATION_270) {
+ rotation = Surface.ROTATION_90;
+ } else if (rotation == Surface.ROTATION_90) {
+ rotation = Surface.ROTATION_270;
+ }
+ try {
+ return mISystemUiProxy.screenshot(new Rect(), displaySize.x, displaySize.y, 0, 100000,
+ false, rotation).toBitmap();
+ } catch (Exception e) {
+ Log.e(TAG, "Error capturing snapshot", e);
+
+ // Return a dummy bitmap
+ Bitmap bitmap = Bitmap.createBitmap(displaySize.x, displaySize.y, Config.RGB_565);
+ bitmap.eraseColor(Color.WHITE);
+ return bitmap;
+ } finally {
+ TraceHelper.endSection("TaskSnapshot");
+ }
+ }
+
+ private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
+ // Create the shared handler
+ final WindowTransformSwipeHandler handler =
+ new WindowTransformSwipeHandler(mRunningTask, this);
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTask.id, null);
+ mInteractionHandler = handler;
+ handler.setGestureEndCallback(mEventQueue::reset);
+
+ CountDownLatch drawWaitLock = new CountDownLatch(1);
+ handler.setLauncherOnDrawCallback(() -> {
+ drawWaitLock.countDown();
+ if (handler == mInteractionHandler) {
+ switchToMainChoreographer();
+ }
+ });
+ handler.initWhenReady();
+
+ TraceHelper.beginSection("RecentsController");
+ Runnable startActivity = () -> ActivityManagerWrapper.getInstance()
+ .startRecentsActivity(mHomeIntent,
+ new AssistDataReceiver() {
+ @Override
+ public void onHandleAssistData(Bundle bundle) {
+ mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
+ }
+ },
+ new RecentsAnimationListener() {
+ public void onAnimationStart(
+ RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
+ Rect minimizedHomeBounds) {
+ if (mInteractionHandler == handler) {
+ 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.onRecentsAnimationCanceled();
+ }
+ }
+ }, null, null);
+
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ startActivity.run();
+ try {
+ drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ // We have waited long enough for launcher to draw
+ }
+ } else {
+ // We should almost always get touch-town on background thread. This is an edge case
+ // when the background Choreographer has not yet initialized.
+ BackgroundExecutor.get().submit(startActivity);
+ }
+ }
+
+ /**
+ * Called when the gesture has ended. Does not correlate to the completion of the interaction as
+ * the animation can still be running.
+ */
+ private void finishTouchTracking() {
+ if (mTouchThresholdCrossed && mInteractionHandler != null) {
+ mVelocityTracker.computeCurrentVelocity(1000,
+ ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
+
+ float velocity = isNavBarOnRight() ? mVelocityTracker.getXVelocity(mActivePointerId)
+ : isNavBarOnLeft() ? -mVelocityTracker.getXVelocity(mActivePointerId)
+ : mVelocityTracker.getYVelocity(mActivePointerId);
+ mInteractionHandler.onGestureEnded(velocity);
+ } else if (!isUsingScreenShot()) {
+ // 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;
+ }
+
+ @Override
+ public void reset() {
+ // Clean up the old interaction handler
+ if (mInteractionHandler != null) {
+ final BaseSwipeInteractionHandler handler = mInteractionHandler;
+ mInteractionHandler = null;
+ mMainThreadExecutor.execute(handler::reset);
+ }
+ }
+
+ @Override
+ public void updateTouchTracking(int interactionType) {
+ notifyGestureStarted();
+
+ if (isUsingScreenShot()) {
+ mMainThreadExecutor.execute(() -> {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.updateInteractionType(interactionType);
+ }
+ });
+ } else {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.updateInteractionType(interactionType);
+ }
+ }
+ }
+
+ @Override
+ public Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ mEventQueue = queue;
+ return isUsingScreenShot() ? null : mBackgroundThreadChoreographer;
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onQuickScrubEnd();
+ }
+ }
+
+ @Override
+ public void onQuickScrubProgress(float progress) {
+ if (mInteractionHandler != null) {
+ mInteractionHandler.onQuickScrubProgress(progress);
+ }
+ }
+
+ public void switchToMainChoreographer() {
+ mEventQueue.setInterimChoreographer(null);
+ }
+
+ @Override
+ public void preProcessMotionEvent(MotionEvent ev) {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.addMovement(ev);
+ if (ev.getActionMasked() == ACTION_POINTER_UP) {
+ mVelocityTracker.clear();
+ }
+ }
+ }
+}
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
new file mode 100644
index 0000000..f28d51c
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -0,0 +1,169 @@
+/*
+ * 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.view.HapticFeedbackConstants;
+
+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.
+ *
+ * The behavior is to evenly divide the progress into sections, each of which scrolls one page.
+ * The first and last section set an alarm to auto-advance backwards or forwards, respectively.
+ */
+public class QuickScrubController implements OnAlarmListener {
+
+ 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 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, RecentsView recentsView) {
+ mLauncher = launcher;
+ mRecentsView = recentsView;
+ if (ENABLE_AUTO_ADVANCE) {
+ mAutoAdvanceAlarm = new Alarm();
+ mAutoAdvanceAlarm.setOnAlarmListener(this);
+ }
+ }
+
+ public void onQuickScrubStart(boolean startingFromHome) {
+ mInQuickScrub = true;
+ mStartPage = startingFromHome ? 0 : mRecentsView.getFirstTaskIndex();
+ mQuickScrubSection = 0;
+ mHasAlarmRun = false;
+ mLauncher.getUserEventDispatcher().resetActionDurationMillis();
+ }
+
+ public void onQuickScrubEnd() {
+ 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) {
+ int quickScrubSection = Math.round(progress * NUM_QUICK_SCRUB_SECTIONS);
+ if (quickScrubSection != mQuickScrubSection) {
+ int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
+ goToPageWithHaptic(pageToGoTo);
+ 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() {
+ 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;
+ mRecentsView.snapToPage(pageToGoTo, duration);
+ mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+ }
+
+ @Override
+ public void onAlarm(Alarm alarm) {
+ int currPage = mRecentsView.getNextPage();
+ if (mQuickScrubSection == NUM_QUICK_SCRUB_SECTIONS
+ && currPage < mRecentsView.getPageCount() - 1) {
+ goToPageWithHaptic(currPage + 1);
+ } else if (mQuickScrubSection == 0 && currPage > mStartPage) {
+ goToPageWithHaptic(currPage - 1);
+ }
+ 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
new file mode 100644
index 0000000..1f9c728
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationInterpolator.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.graphics.Rect;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Helper class to interpolate the animation between a task view representation and an actual
+ * window.
+ */
+public class RecentsAnimationInterpolator {
+
+ public static class TaskWindowBounds {
+ public float taskScale = 1f;
+ public float taskX = 0f;
+ public float taskY = 0f;
+
+ public float winScale = 1f;
+ public float winX = 0f;
+ public float winY = 0f;
+ public Rect winCrop = new Rect();
+
+ @Override
+ public String toString() {
+ return "taskScale=" + taskScale + " taskX=" + taskX + " taskY=" + taskY
+ + " winScale=" + winScale + " winX=" + winX + " winY=" + winY
+ + " winCrop=" + winCrop;
+ }
+ }
+
+ private TaskWindowBounds mTmpTaskWindowBounds = new TaskWindowBounds();
+ private Rect mTmpInsets = new Rect();
+
+ private Rect mWindow;
+ private Rect mInsetWindow;
+ private Rect mInsets;
+ private Rect mTask;
+ private Rect mTaskInsets;
+ private Rect mThumbnail;
+
+ private float mInitialTaskScale;
+ private float mInitialTaskTranslationX;
+ private float mFinalTaskScale;
+ private Rect mScaledTask;
+ private Rect mTargetTask;
+ private Rect mSrcWindow;
+
+ public RecentsAnimationInterpolator(Rect window, Rect insets, Rect task, Rect taskInsets,
+ float taskScale, float taskTranslationX) {
+ mWindow = window;
+ mInsets = insets;
+ mTask = task;
+ mTaskInsets = taskInsets;
+ mInsetWindow = new Rect(window);
+ Utilities.insetRect(mInsetWindow, insets);
+
+ mThumbnail = new Rect(task);
+ Utilities.insetRect(mThumbnail, taskInsets);
+ mInitialTaskScale = taskScale;
+ mInitialTaskTranslationX = taskTranslationX;
+ mFinalTaskScale = (float) mInsetWindow.width() / mThumbnail.width();
+ mScaledTask = new Rect(task);
+ Utilities.scaleRectAboutCenter(mScaledTask, mFinalTaskScale);
+ Rect finalScaledTaskInsets = new Rect(taskInsets);
+ Utilities.scaleRect(finalScaledTaskInsets, mFinalTaskScale);
+ mTargetTask = new Rect(mInsetWindow);
+ mTargetTask.offsetTo(window.top + insets.top - finalScaledTaskInsets.top,
+ window.left + insets.left - finalScaledTaskInsets.left);
+
+ float initialWinScale = 1f / mFinalTaskScale;
+ Rect scaledWindow = new Rect(mInsetWindow);
+ Utilities.scaleRectAboutCenter(scaledWindow, initialWinScale);
+ Rect scaledInsets = new Rect(insets);
+ Utilities.scaleRect(scaledInsets, initialWinScale);
+ mSrcWindow = new Rect(scaledWindow);
+ mSrcWindow.offsetTo(mThumbnail.left - scaledInsets.left,
+ mThumbnail.top - scaledInsets.top);
+ }
+
+ public TaskWindowBounds interpolate(float t) {
+ mTmpTaskWindowBounds.taskScale = Utilities.mapRange(t,
+ mInitialTaskScale, mFinalTaskScale);
+ mTmpTaskWindowBounds.taskX = Utilities.mapRange(t,
+ mInitialTaskTranslationX, mTargetTask.left - mScaledTask.left);
+ mTmpTaskWindowBounds.taskY = Utilities.mapRange(t,
+ 0, mTargetTask.top - mScaledTask.top);
+
+ float taskScale = Utilities.mapRange(t, 1, mFinalTaskScale);
+ mTmpTaskWindowBounds.winScale = taskScale / mFinalTaskScale;
+ mTmpTaskWindowBounds.winX = Utilities.mapRange(t,
+ mSrcWindow.left, 0);
+ mTmpTaskWindowBounds.winY = Utilities.mapRange(t,
+ mSrcWindow.top, 0);
+
+ mTmpInsets.set(mInsets);
+ Utilities.scaleRect(mTmpInsets, (1f - t));
+ mTmpTaskWindowBounds.winCrop.set(mWindow);
+ Utilities.insetRect(mTmpTaskWindowBounds.winCrop, mTmpInsets);
+
+ return mTmpTaskWindowBounds;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
new file mode 100644
index 0000000..4e11220
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -0,0 +1,78 @@
+/*
+ * 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 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;
+
+/**
+ * Wrapper around RecentsAnimationController to help with some synchronization
+ */
+public class RecentsAnimationWrapper {
+
+ public RecentsAnimationControllerCompat controller;
+ public RemoteAnimationTargetCompat[] targets;
+
+ private boolean mInputConsumerEnabled;
+
+ public synchronized void setController(
+ RecentsAnimationControllerCompat controller, RemoteAnimationTargetCompat[] targets) {
+ TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
+ this.controller = controller;
+ this.targets = targets;
+
+ if (mInputConsumerEnabled) {
+ enableInputConsumer();
+ }
+ }
+
+ /**
+ * @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();
+ }
+ }
+ }
+ });
+ }
+
+ public void enableInputConsumer() {
+ mInputConsumerEnabled = true;
+ 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
new file mode 100644
index 0000000..7fe7751
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -0,0 +1,216 @@
+/*
+ * 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.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;
+
+/**
+ * Singleton class to load and manage recents model.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class RecentsModel extends TaskStackChangeListener {
+
+ // We do not need any synchronization for this variable as its only written on UI thread.
+ private static RecentsModel INSTANCE;
+
+ public static RecentsModel getInstance(final Context context) {
+ if (INSTANCE == null) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ INSTANCE = new RecentsModel(context.getApplicationContext());
+ } else {
+ try {
+ return new MainThreadExecutor().submit(
+ () -> RecentsModel.getInstance(context)).get();
+ } catch (InterruptedException|ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ 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;
+
+ private RecentsTaskLoadPlan mLastLoadPlan;
+ private int mLastLoadPlanId;
+ private int mTaskChangeId;
+ private ISystemUiProxy mSystemUiProxy;
+ private boolean mClearAssistCacheOnStackChange = true;
+
+ private RecentsModel(Context context) {
+ mContext = context;
+
+ Resources res = context.getResources();
+ mRecentsTaskLoader = new RecentsTaskLoader(mContext,
+ res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
+ 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();
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
+
+ mTaskChangeId = 1;
+ loadTasks(-1, null);
+ }
+
+ public RecentsTaskLoader getRecentsTaskLoader() {
+ return mRecentsTaskLoader;
+ }
+
+ /**
+ * Preloads the task plan
+ * @param taskId The running task id or -1
+ * @param callback The callback to receive the task plan once its complete or null. This is
+ * always called on the UI thread.
+ * @return the request id associated with this call.
+ */
+ public int loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
+ final int requestId = mTaskChangeId;
+
+ // Fail fast if nothing has changed.
+ if (mLastLoadPlanId == mTaskChangeId) {
+ if (callback != null) {
+ final RecentsTaskLoadPlan plan = mLastLoadPlan;
+ mMainThreadExecutor.execute(() -> callback.accept(plan));
+ }
+ return requestId;
+ }
+
+ BackgroundExecutor.get().submit(() -> {
+ // Preload the plan
+ RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
+ PreloadOptions opts = new PreloadOptions();
+ opts.loadTitles = false;
+ loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
+ // Set the load plan on UI thread
+ mMainThreadExecutor.execute(() -> {
+ mLastLoadPlan = loadPlan;
+ mLastLoadPlanId = requestId;
+
+ if (callback != null) {
+ callback.accept(loadPlan);
+ }
+ });
+ });
+ return requestId;
+ }
+
+ @Override
+ public void onTaskStackChanged() {
+ mTaskChangeId++;
+
+ Preconditions.assertUIThread();
+ if (mClearAssistCacheOnStackChange) {
+ mCachedAssistData.clear();
+ } else {
+ mClearAssistCacheOnStackChange = true;
+ }
+ }
+
+ public boolean isLoadPlanValid(int resultId) {
+ return mTaskChangeId == resultId;
+ }
+
+ public RecentsTaskLoadPlan getLastLoadPlan() {
+ return mLastLoadPlan;
+ }
+
+ public void setSystemUiProxy(ISystemUiProxy systemUiProxy) {
+ mSystemUiProxy = systemUiProxy;
+ }
+
+ 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 675f456..ec0716c 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -16,15 +16,38 @@
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;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.OverviewState;
+import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.recents.model.Task;
@@ -39,24 +62,55 @@
/**
* A list of recent tasks.
*/
-public class RecentsView extends PagedView {
+public class RecentsView extends PagedView implements Insettable {
+ private static final Rect sTempStableInsets = new Rect();
+
+ public static final int SCROLL_TYPE_NONE = 0;
+ public static final int SCROLL_TYPE_TASK = 1;
+ public static final int SCROLL_TYPE_WORKSPACE = 2;
+
+ private final Launcher mLauncher;
+ private QuickScrubController mQuickScrubController;
+ private final ScrollState mScrollState = new ScrollState();
private boolean mOverviewStateEnabled;
private boolean mTaskStackListenerRegistered;
+ private LayoutTransition mLayoutTransition;
+ private Runnable mNextPageSwitchRunnable;
+ /**
+ * TODO: Call reloadIdNeeded in onTaskStackChanged.
+ */
private TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
- for (int i = 0; i < getChildCount(); i++) {
+ for (int i = mFirstTaskIndex; i < getChildCount(); i++) {
final TaskView taskView = (TaskView) getChildAt(i);
if (taskView.getTask().key.id == taskId) {
- taskView.getThumbnail().setThumbnail(snapshot);
+ taskView.getThumbnail().setThumbnail(taskView.getTask(), snapshot);
return;
}
}
}
};
+ private RecentsViewStateController mStateController;
+ private int mFirstTaskIndex;
+
+ private final RecentsModel mModel;
+ private int mLoadPlanId = -1;
+
+ // Only valid until the launcher state changes to NORMAL
+ private int mRunningTaskId = -1;
+
+ private Bitmap mScrim;
+ private Paint mFadePaint;
+ private Shader mFadeShader;
+ private Matrix mFadeMatrix;
+ private boolean mScrimOnLeft;
+
+ private boolean mFirstTaskIconScaledDown = false;
+
public RecentsView(Context context) {
this(context, null);
}
@@ -67,16 +121,45 @@
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setWillNotDraw(false);
- setPageSpacing((int) getResources().getDimension(R.dimen.recents_page_spacing));
+ setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
+ enableFreeScroll(true);
+ setupLayoutTransition();
+ setClipToOutline(true);
+
+ mLauncher = Launcher.getLauncher(context);
+ mQuickScrubController = new QuickScrubController(mLauncher, this);
+ mModel = RecentsModel.getInstance(context);
+
+ mScrollState.isRtl = mIsRtl;
+ }
+
+ 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 taskView;
+ }
+ }
+ return null;
+ }
+
+ private void setupLayoutTransition() {
+ // We want to show layout transitions when pages are deleted, to close the gap.
+ mLayoutTransition = new LayoutTransition();
+ mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
+ mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
+
+ mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
+ mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
+ setLayoutTransition(mLayoutTransition);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
- Rect padding = getPadding(Launcher.getLauncher(getContext()));
- setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ mFirstTaskIndex = getPageCount();
}
@Override
@@ -97,50 +180,129 @@
updateTaskStackListenerState();
}
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ DeviceProfile dp = Launcher.getLauncher(getContext()).getDeviceProfile();
+ Rect padding = getPadding(dp, getContext());
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ lp.bottomMargin = padding.bottom;
+ setLayoutParams(lp);
+
+ setPadding(padding.left, padding.top, padding.right, 0);
+
+ if (dp.isVerticalBarLayout()) {
+ boolean wasScrimOnLeft = mScrimOnLeft;
+ mScrimOnLeft = dp.isSeascape();
+
+ if (mScrim == null || wasScrimOnLeft != mScrimOnLeft) {
+ Drawable scrim = getContext().getDrawable(mScrimOnLeft
+ ? R.drawable.recents_horizontal_scrim_left
+ : R.drawable.recents_horizontal_scrim_right);
+ if (scrim instanceof BitmapDrawable) {
+ mScrim = ((BitmapDrawable) scrim).getBitmap();
+ mFadePaint = new Paint();
+ mFadePaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
+ mFadeShader = new BitmapShader(mScrim, TileMode.CLAMP, TileMode.REPEAT);
+ mFadeMatrix = new Matrix();
+ } else {
+ mScrim = null;
+ }
+ }
+ } else {
+ mScrim = null;
+ mFadePaint = null;
+ mFadeShader = null;
+ mFadeMatrix = null;
+ }
+ }
+
+ public int getFirstTaskIndex() {
+ return mFirstTaskIndex;
+ }
+
+ public boolean isTaskViewVisible(TaskView tv) {
+ // For now, just check if it's the active task or an adjacent task
+ return Math.abs(indexOfChild(tv) - getNextPage()) <= 1;
+ }
+
+ public TaskView getTaskView(int taskId) {
+ for (int i = getFirstTaskIndex(); i < getChildCount(); i++) {
+ TaskView tv = (TaskView) getChildAt(i);
+ if (tv.getTask().key.id == taskId) {
+ return tv;
+ }
+ }
+ return null;
+ }
+
+ public void setStateController(RecentsViewStateController stateController) {
+ mStateController = stateController;
+ }
+
+ public RecentsViewStateController getStateController() {
+ return mStateController;
+ }
+
public void setOverviewStateEnabled(boolean enabled) {
mOverviewStateEnabled = enabled;
updateTaskStackListenerState();
}
- public void update(RecentsTaskLoadPlan loadPlan) {
- final RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
- setCurrentPage(0);
+ public void setNextPageSwitchRunnable(Runnable r) {
+ mNextPageSwitchRunnable = r;
+ }
+
+ @Override
+ protected void onPageEndTransition() {
+ super.onPageEndTransition();
+ if (mNextPageSwitchRunnable != null) {
+ mNextPageSwitchRunnable.run();
+ mNextPageSwitchRunnable = null;
+ }
+ }
+
+ private void applyLoadPlan(RecentsTaskLoadPlan loadPlan) {
+ final RecentsTaskLoader loader = mModel.getRecentsTaskLoader();
TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
if (stack == null) {
removeAllViews();
return;
}
+ int oldChildCount = getChildCount();
+
// Ensure there are as many views as there are tasks in the stack (adding and trimming as
// necessary)
final LayoutInflater inflater = LayoutInflater.from(getContext());
- final ArrayList<Task> tasks = stack.getTasks();
- for (int i = getChildCount(); i < tasks.size(); i++) {
+ final ArrayList<Task> tasks = new ArrayList<>(stack.getTasks());
+ setLayoutTransition(null);
+
+ final int requiredChildCount = tasks.size() + mFirstTaskIndex;
+ for (int i = getChildCount(); i < requiredChildCount; i++) {
final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
addView(taskView);
}
- while (getChildCount() > tasks.size()) {
+ while (getChildCount() > requiredChildCount) {
final TaskView taskView = (TaskView) getChildAt(getChildCount() - 1);
removeView(taskView);
loader.unloadTaskData(taskView.getTask());
}
+ setLayoutTransition(mLayoutTransition);
- // Rebind all task views
+ // Rebind and reset all task views
for (int i = tasks.size() - 1; i >= 0; i--) {
final Task task = tasks.get(i);
- final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1);
+ final TaskView taskView = (TaskView) getChildAt(tasks.size() - i - 1 + mFirstTaskIndex);
taskView.bind(task);
+ taskView.resetVisualProperties();
loader.loadTaskData(task);
}
- }
+ updateCurveProperties();
+ applyIconScale(false /* animate */);
- public void launchTaskWithId(int taskId) {
- for (int i = 0; i < getChildCount(); i++) {
- final TaskView taskView = (TaskView) getChildAt(i);
- if (taskView.getTask().key.id == taskId) {
- taskView.launchTask(false /* animate */);
- return;
- }
+ if (oldChildCount != getChildCount()) {
+ mQuickScrubController.snapToPageForCurrentQuickScrubSection();
}
}
@@ -149,25 +311,254 @@
&& getWindowVisibility() == VISIBLE;
if (registerStackListener != mTaskStackListenerRegistered) {
if (registerStackListener) {
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance()
+ .registerTaskStackListener(mTaskStackListener);
+ reloadIfNeeded();
} else {
- ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
+ ActivityManagerWrapper.getInstance()
+ .unregisterTaskStackListener(mTaskStackListener);
}
mTaskStackListenerRegistered = registerStackListener;
}
}
- public static Rect getPadding(Launcher launcher) {
- DeviceProfile profile = launcher.getDeviceProfile();
- Rect stableInsets = new Rect();
- WindowManagerWrapper.getInstance().getStableInsets(stableInsets);
- Rect padding = profile.getWorkspacePadding(null);
- float taskWidth = profile.getCurrentWidth() - stableInsets.left - stableInsets.right;
- float taskHeight = profile.getCurrentHeight() - stableInsets.top - stableInsets.bottom;
- float overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
- - stableInsets.top;
- float overviewWidth = taskWidth * overviewHeight / taskHeight;
+ private static Rect getPadding(DeviceProfile profile, Context context) {
+ WindowManagerWrapper.getInstance().getStableInsets(sTempStableInsets);
+ Rect padding = new Rect(profile.workspacePadding);
+
+ float taskWidth = profile.widthPx - sTempStableInsets.left - sTempStableInsets.right;
+ float taskHeight = profile.heightPx - sTempStableInsets.top - sTempStableInsets.bottom;
+
+ float overviewHeight, overviewWidth;
+ if (profile.isVerticalBarLayout()) {
+ // Use the same padding on both sides for symmetry.
+ float availableWidth = taskWidth - 2 * Math.max(padding.left, padding.right);
+ float availableHeight = profile.availableHeightPx - padding.top
+ - sTempStableInsets.top - Math.max(padding.bottom,
+ profile.heightPx * (1 - OverviewState.getVerticalProgress(profile, context)));
+ float scaledRatio = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
+ overviewHeight = taskHeight * scaledRatio;
+ overviewWidth = taskWidth * scaledRatio;
+
+ } else {
+ overviewHeight = profile.availableHeightPx - padding.top - padding.bottom
+ - sTempStableInsets.top;
+ overviewWidth = taskWidth * overviewHeight / taskHeight;
+ }
+
+ padding.bottom = profile.availableHeightPx - padding.top - sTempStableInsets.top
+ - Math.round(overviewHeight);
padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
return padding;
}
+
+ /**
+ * Sets the {@param outRect} to match the position of the first tile such that it is scaled
+ * down to match the 2nd taskView.
+ * @return returns the factor which determines the scaling factor for the second task.
+ */
+ public static float getScaledDownPageRect(DeviceProfile dp, Context context, Rect outRect) {
+ getPageRect(dp, context, outRect);
+
+ int pageSpacing = context.getResources()
+ .getDimensionPixelSize(R.dimen.recents_page_spacing);
+ float halfScreenWidth = dp.widthPx * 0.5f;
+ float halfPageWidth = outRect.width() * 0.5f;
+ float pageCenter = outRect.right + pageSpacing + halfPageWidth;
+ float distanceFromCenter = Math.abs(halfScreenWidth - pageCenter);
+ float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
+ float linearInterpolation = Math.min(1, distanceFromCenter / distanceToReachEdge);
+
+ float scale = 1 - CURVE_INTERPOLATOR.getInterpolation(linearInterpolation) * CURVE_FACTOR;
+
+ int topMargin = context.getResources()
+ .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ outRect.top -= topMargin;
+ Utilities.scaleRectAboutCenter(outRect, scale);
+ outRect.top += (int) (scale * topMargin);
+ return linearInterpolation;
+ }
+
+ public static void getPageRect(DeviceProfile grid, Context context, Rect outRect) {
+ Rect targetPadding = getPadding(grid, context);
+ Rect insets = grid.getInsets();
+ outRect.set(
+ targetPadding.left + insets.left,
+ targetPadding.top + insets.top,
+ grid.widthPx - targetPadding.right - insets.right,
+ grid.heightPx - targetPadding.bottom - insets.bottom);
+ outRect.top += context.getResources()
+ .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ }
+
+ @Override
+ public void computeScroll() {
+ super.computeScroll();
+ updateCurveProperties();
+ }
+
+ /**
+ * Scales and adjusts translation of adjacent pages as if on a curved carousel.
+ */
+ public void updateCurveProperties() {
+ if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
+ return;
+ }
+ final int halfPageWidth = mScrollState.halfPageWidth = getNormalChildWidth() / 2;
+ mScrollState.lastScrollType = SCROLL_TYPE_NONE;
+ final int screenCenter = mInsets.left + getPaddingLeft() + getScrollX() + halfPageWidth;
+ final int halfScreenWidth = getMeasuredWidth() / 2;
+ final int pageSpacing = mPageSpacing;
+
+ final int pageCount = getPageCount();
+ for (int i = 0; i < pageCount; i++) {
+ View page = getPageAt(i);
+ int pageCenter = page.getLeft() + halfPageWidth;
+ mScrollState.distanceFromScreenCenter = screenCenter - pageCenter;
+ float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
+ mScrollState.linearInterpolation = Math.min(1,
+ Math.abs(mScrollState.distanceFromScreenCenter) / distanceToReachEdge);
+ mScrollState.lastScrollType = ((PageCallbacks) page).onPageScroll(mScrollState);
+ }
+ }
+
+ public void onTaskDismissed(TaskView taskView) {
+ ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
+ removeView(taskView);
+ if (getTaskCount() == 0) {
+ mLauncher.getStateManager().goToState(NORMAL);
+ }
+ }
+
+ public void reset() {
+ mRunningTaskId = -1;
+ setCurrentPage(0);
+ }
+
+ public int getTaskCount() {
+ return getChildCount() - mFirstTaskIndex;
+ }
+
+ public int getRunningTaskId() {
+ return mRunningTaskId;
+ }
+
+ /**
+ * Reloads the view if anything in recents changed.
+ */
+ public void reloadIfNeeded() {
+ if (!mModel.isLoadPlanValid(mLoadPlanId)) {
+ mLoadPlanId = mModel.loadTasks(mRunningTaskId, this::applyLoadPlan);
+ }
+ }
+
+ /**
+ * Ensures that the first task in the view represents {@param task} and reloads the view
+ * if needed. This allows the swipe-up gesture to assume that the first tile always
+ * corresponds to the correct task.
+ * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
+ * is called.
+ * Also scrolls the view to this task
+ */
+ public void showTask(int runningTaskId) {
+ boolean needsReload = false;
+ if (getTaskCount() == 0) {
+ needsReload = true;
+ // Add an empty view for now
+ setLayoutTransition(null);
+ final TaskView taskView = (TaskView) LayoutInflater.from(getContext())
+ .inflate(R.layout.task, this, false);
+ addView(taskView, mFirstTaskIndex);
+ setLayoutTransition(mLayoutTransition);
+ }
+ if (!needsReload) {
+ needsReload = !mModel.isLoadPlanValid(mLoadPlanId);
+ }
+ if (needsReload) {
+ mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
+ }
+ mRunningTaskId = runningTaskId;
+ setCurrentPage(mFirstTaskIndex);
+ if (mCurrentPage >= mFirstTaskIndex) {
+ getPageAt(mCurrentPage).setAlpha(0);
+ }
+ }
+
+ public QuickScrubController getQuickScrubController() {
+ return mQuickScrubController;
+ }
+
+ public void setFirstTaskIconScaledDown(boolean isScaledDown, boolean animate) {
+ if (mFirstTaskIconScaledDown == isScaledDown) {
+ return;
+ }
+ mFirstTaskIconScaledDown = isScaledDown;
+ applyIconScale(animate);
+ }
+
+ private void applyIconScale(boolean animate) {
+ float scale = mFirstTaskIconScaledDown ? 0 : 1;
+ TaskView firstTask = (TaskView) getChildAt(mFirstTaskIndex);
+ if (firstTask != null) {
+ if (animate) {
+ firstTask.animateIconToScale(scale);
+ } else {
+ firstTask.setIconScale(scale);
+ }
+ }
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ if (mScrim == null) {
+ super.draw(canvas);
+ return;
+ }
+
+ final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
+
+ int length = mScrim.getWidth();
+ int height = getHeight();
+ int saveCount = canvas.getSaveCount();
+
+ int scrimLeft;
+ if (mScrimOnLeft) {
+ scrimLeft = getScrollX();
+ } else {
+ scrimLeft = getScrollX() + getWidth() - length;
+ }
+ canvas.saveLayer(scrimLeft, 0, scrimLeft + length, height, null, flags);
+ super.draw(canvas);
+
+ mFadeMatrix.setTranslate(scrimLeft, 0);
+ mFadeShader.setLocalMatrix(mFadeMatrix);
+ mFadePaint.setShader(mFadeShader);
+ canvas.drawRect(scrimLeft, 0, scrimLeft + length, height, mFadePaint);
+ canvas.restoreToCount(saveCount);
+ }
+
+ public interface PageCallbacks {
+
+ /**
+ * Updates the page UI based on scroll params and returns the type of scroll
+ * effect performed.
+ *
+ * @see #SCROLL_TYPE_NONE
+ * @see #SCROLL_TYPE_TASK
+ * @see #SCROLL_TYPE_WORKSPACE
+ */
+ int onPageScroll(ScrollState scrollState);
+ }
+
+ public static class ScrollState {
+
+ public boolean isRtl;
+ public int lastScrollType;
+
+ public int halfPageWidth;
+ public float distanceFromScreenCenter;
+ public float linearInterpolation;
+
+ public float prevPageExtraWidth;
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RemoteRunnable.java b/quickstep/src/com/android/quickstep/RemoteRunnable.java
new file mode 100644
index 0000000..ec7cad4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RemoteRunnable.java
@@ -0,0 +1,33 @@
+/*
+ * 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.os.RemoteException;
+import android.util.Log;
+
+@FunctionalInterface
+public interface RemoteRunnable {
+
+ void run() throws RemoteException;
+
+ static void executeSafely(RemoteRunnable r) {
+ try {
+ r.run();
+ } catch (final RemoteException e) {
+ Log.e("RemoteRunnable", "Error calling remote method", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/TaskMenuView.java b/quickstep/src/com/android/quickstep/TaskMenuView.java
new file mode 100644
index 0000000..6bbcb37
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskMenuView.java
@@ -0,0 +1,230 @@
+/*
+ * 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.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Outline;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+
+/**
+ * Contains options for a recent task when long-pressing its icon.
+ */
+public class TaskMenuView extends AbstractFloatingView {
+
+ private static final Rect sTempRect = new Rect();
+
+ /** Note that these will be shown in order from top to bottom, if available for the task. */
+ private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
+ new TaskSystemShortcut.AppInfo(),
+ new TaskSystemShortcut.SplitScreen(),
+ new TaskSystemShortcut.Pin(),
+ new TaskSystemShortcut.Install(),
+ };
+
+ private static final long OPEN_CLOSE_DURATION = 220;
+
+ private Launcher mLauncher;
+ private TextView mTaskIconAndName;
+ private AnimatorSet mOpenCloseAnimator;
+ private TaskView mTaskView;
+
+ public TaskMenuView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mLauncher = Launcher.getLauncher(context);
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ float r = getResources().getDimensionPixelSize(R.dimen.task_menu_background_radius);
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), r);
+ }
+ });
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTaskIconAndName = findViewById(R.id.task_icon_and_name);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ DragLayer dl = mLauncher.getDragLayer();
+ if (!dl.isEventOverView(this, ev)) {
+ // TODO: log this once we have a new container type for it?
+ close(true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // TODO
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_TASK_MENU) != 0;
+ }
+
+ public static boolean showForTask(TaskView taskView) {
+ Launcher launcher = Launcher.getLauncher(taskView.getContext());
+ final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate(
+ R.layout.task_menu, launcher.getDragLayer(), false);
+ return taskMenuView.populateAndShowForTask(taskView);
+ }
+
+ private boolean populateAndShowForTask(TaskView taskView) {
+ if (isAttachedToWindow()) {
+ return false;
+ }
+ mLauncher.getDragLayer().addView(this);
+ mTaskView = taskView;
+ addMenuOptions(mTaskView);
+ orientAroundTaskView(mTaskView);
+ post(this::animateOpen);
+ return true;
+ }
+
+ private void addMenuOptions(TaskView taskView) {
+ Drawable icon = taskView.getTask().icon.getConstantState().newDrawable();
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
+ icon.setBounds(0, 0, iconSize, iconSize);
+ mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
+ mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, taskView.getTask()));
+ mTaskIconAndName.setOnClickListener(v -> close(true));
+
+ for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
+ OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, taskView);
+ if (onClickListener != null) {
+ addMenuOption(menuOption, onClickListener);
+ }
+ }
+ }
+
+ private void addMenuOption(TaskSystemShortcut menuOption, OnClickListener onClickListener) {
+ DeepShortcutView menuOptionView = (DeepShortcutView) mLauncher.getLayoutInflater().inflate(
+ R.layout.system_shortcut, this, false);
+ menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId);
+ menuOptionView.getBubbleText().setText(menuOption.labelResId);
+ menuOptionView.setOnClickListener(onClickListener);
+ addView(menuOptionView);
+ }
+
+ private void orientAroundTaskView(TaskView taskView) {
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+ Rect insets = mLauncher.getDragLayer().getInsets();
+ int x = sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left;
+ setX(Utilities.isRtl(getResources()) ? -x : x);
+ setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top);
+ }
+
+ private void animateOpen() {
+ animateOpenOrClosed(false);
+ mIsOpen = true;
+ }
+
+ private void animateClose() {
+ animateOpenOrClosed(true);
+ }
+
+ private void animateOpenOrClosed(boolean closing) {
+ if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ mOpenCloseAnimator = LauncherAnimUtils.createAnimatorSet();
+ mOpenCloseAnimator.play(createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, closing));
+ mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (closing) {
+ closeComplete();
+ }
+ }
+ });
+ mOpenCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
+ mOpenCloseAnimator.setDuration(OPEN_CLOSE_DURATION);
+ mOpenCloseAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
+ mOpenCloseAnimator.start();
+ }
+
+ private void closeComplete() {
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+
+ private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
+ float fromRadius = iconSize / 2;
+ float toRadius = getResources().getDimensionPixelSize(
+ R.dimen.task_menu_background_radius);
+ Point iconCenter = new Point(getWidth() / 2, mTaskIconAndName.getPaddingTop() + iconSize / 2);
+ Rect fromRect = new Rect(iconCenter.x, iconCenter.y, iconCenter.x, iconCenter.y);
+ Rect toRect = new Rect(0, 0, getWidth(), getHeight());
+ return new RoundedRectRevealOutlineProvider(fromRadius, toRadius, fromRect, toRect) {
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return true;
+ }
+ };
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
new file mode 100644
index 0000000..66969c6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.view.View;
+
+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;
+
+/**
+ * Factory class to create and add an overlays on the TaskView
+ */
+public class TaskOverlayFactory {
+
+ private static TaskOverlayFactory sInstance;
+
+ public static TaskOverlayFactory get(Context context) {
+ Preconditions.assertUIThread();
+ if (sInstance == null) {
+ sInstance = Utilities.getOverrideObject(TaskOverlayFactory.class,
+ context.getApplicationContext(), R.string.task_overlay_factory_class);
+ }
+ return sInstance;
+ }
+
+ public TaskOverlay createOverlay(View thumbnailView) {
+ return new TaskOverlay();
+ }
+
+ public static class TaskOverlay {
+
+ 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
new file mode 100644
index 0000000..75d8619
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -0,0 +1,173 @@
+/*
+ * 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.app.ActivityOptions;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.view.View;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+
+import java.util.function.Consumer;
+
+/**
+ * Represents a system shortcut that can be shown for a recent task.
+ */
+public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
+
+ private static final String TAG = "TaskSystemShortcut";
+
+ protected T mSystemShortcut;
+
+ protected TaskSystemShortcut(T systemShortcut) {
+ super(systemShortcut.iconResId, systemShortcut.labelResId);
+ mSystemShortcut = systemShortcut;
+ }
+
+ protected TaskSystemShortcut(int iconResId, int labelResId) {
+ super(iconResId, labelResId);
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ return null;
+ }
+
+ public View.OnClickListener getOnClickListener(final Launcher launcher, final TaskView view) {
+ Task task = view.getTask();
+
+ ShortcutInfo dummyInfo = new ShortcutInfo();
+ dummyInfo.intent = new Intent();
+ ComponentName component = task.getTopComponent();
+ dummyInfo.intent.setComponent(component);
+ dummyInfo.user = UserHandle.of(task.key.userId);
+ dummyInfo.title = TaskUtils.getTitle(launcher, task);
+
+ return getOnClickListenerForTask(launcher, task, dummyInfo);
+ }
+
+ protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher,
+ final Task task, final ItemInfo dummyInfo) {
+ return mSystemShortcut.getOnClickListener(launcher, dummyInfo);
+ }
+
+ public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
+ public AppInfo() {
+ super(new SystemShortcut.AppInfo());
+ }
+ }
+
+ public static class SplitScreen extends TaskSystemShortcut {
+
+ private Handler mHandler;
+
+ public SplitScreen() {
+ super(R.drawable.ic_split_screen, R.string.recent_task_option_split_screen);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
+ if (launcher.getDeviceProfile().isMultiWindowMode) {
+ return null;
+ }
+ final Task task = taskView.getTask();
+ if (!task.isDockable) {
+ return null;
+ }
+ return (v -> {
+ final ActivityOptions options = ActivityOptionsCompat.makeSplitScreenOptions(true);
+ final Consumer<Boolean> resultCallback = success -> {
+ if (success) {
+ launcher.<RecentsView>getOverviewPanel().removeView(taskView);
+ }
+ };
+ ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(
+ task.key, options, resultCallback, mHandler);
+
+ // TODO improve transition; see:
+ // DockedFirstAnimationFrameEvent
+ // RecentsTransitionHelper#composeDockAnimationSpec
+ // WindowManagerWrapper#overridePendingAppTransitionMultiThumbFuture
+ });
+ }
+ }
+
+ public static class Pin extends TaskSystemShortcut {
+
+ private Handler mHandler;
+
+ public Pin() {
+ super(R.drawable.ic_pin, R.string.recent_task_option_pin);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(Launcher launcher, TaskView taskView) {
+ ISystemUiProxy sysUiProxy = RecentsModel.getInstance(launcher).getSystemUiProxy();
+ if (sysUiProxy == null) {
+ return null;
+ }
+ if (!ActivityManagerWrapper.getInstance().isLockToAppEnabled()) {
+ return null;
+ }
+ return view -> {
+ Consumer<Boolean> resultCallback = success -> {
+ if (success) {
+ try {
+ sysUiProxy.startScreenPinning(taskView.getTask().key.id);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to start screen pinning: ", e);
+ }
+ }
+ };
+ taskView.launchTask(true, resultCallback, mHandler);
+ };
+ }
+ }
+
+ public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
+ public Install() {
+ super(new SystemShortcut.Install());
+ }
+
+ @Override
+ protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task,
+ ItemInfo itemInfo) {
+ if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher,
+ task.getTopComponent().getPackageName())) {
+ return mSystemShortcut.createOnClickListener(launcher, itemInfo);
+ }
+ return null;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
index 6e8bbeb..9b9c6f2 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
@@ -22,36 +22,44 @@
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ComposeShader;
import android.graphics.LightingColorFilter;
+import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Shader;
import android.util.AttributeSet;
-import android.widget.FrameLayout;
+import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
+import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
/**
* A task in the Recents view.
*/
-public class TaskThumbnailView extends FrameLayout {
+public class TaskThumbnailView extends View {
+ private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
+
+ private final float mCornerRadius;
+ private final float mFadeLength;
+
+ private final TaskOverlay mOverlay;
+ private final Paint mPaint = new Paint();
+
+ private final Matrix mMatrix = new Matrix();
+
+ private Task mTask;
private ThumbnailData mThumbnailData;
-
- private Rect mThumbnailRect = new Rect();
- private float mThumbnailScale;
-
- private Matrix mMatrix = new Matrix();
- private Paint mDrawPaint = new Paint();
- protected Paint mBgFillPaint = new Paint();
protected BitmapShader mBitmapShader;
private float mDimAlpha = 1f;
- private LightingColorFilter mLightingColorFilter = new LightingColorFilter(Color.WHITE, 0);
public TaskThumbnailView(Context context) {
this(context, null);
@@ -63,32 +71,36 @@
public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setWillNotDraw(false);
- setClipToOutline(true);
+ mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
+ mFadeLength = getResources().getDimension(R.dimen.task_fade_length);
+ mOverlay = TaskOverlayFactory.get(context).createOverlay(this);
+ }
+
+ public void bind() {
+ mOverlay.reset();
}
/**
* Updates this thumbnail.
*/
- public void setThumbnail(ThumbnailData thumbnailData) {
+ public void setThumbnail(Task task, ThumbnailData thumbnailData) {
+ mTask = task;
+ mPaint.setColor(task == null ? Color.BLACK : task.colorBackground | 0xFF000000);
+
if (thumbnailData != null && thumbnailData.thumbnail != null) {
Bitmap bm = thumbnailData.thumbnail;
bm.prepareToDraw();
- mThumbnailScale = thumbnailData.scale;
mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
- mDrawPaint.setShader(mBitmapShader);
- mThumbnailRect.set(0, 0,
- bm.getWidth() - thumbnailData.insets.left - thumbnailData.insets.right,
- bm.getHeight() - thumbnailData.insets.top - thumbnailData.insets.bottom);
+ mPaint.setShader(mBitmapShader);
mThumbnailData = thumbnailData;
updateThumbnailMatrix();
- updateThumbnailPaintFilter();
} else {
mBitmapShader = null;
- mDrawPaint.setShader(null);
- mThumbnailRect.setEmpty();
mThumbnailData = null;
+ mPaint.setShader(null);
+ mOverlay.reset();
}
+ updateThumbnailPaintFilter();
}
/**
@@ -99,81 +111,106 @@
updateThumbnailPaintFilter();
}
+ public Rect getInsets() {
+ if (mThumbnailData != null) {
+ return mThumbnailData.insets;
+ }
+ return new Rect();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
- int viewWidth = getMeasuredWidth();
- int viewHeight = getMeasuredHeight();
- int thumbnailWidth = Math.min(viewWidth,
- (int) (mThumbnailRect.width() * mThumbnailScale));
- int thumbnailHeight = Math.min(viewHeight,
- (int) (mThumbnailRect.height() * mThumbnailScale));
-
- if (mBitmapShader != null && thumbnailWidth > 0 && thumbnailHeight > 0) {
- // Draw the background, there will be some small overdraw with the thumbnail
- if (thumbnailWidth < viewWidth) {
- // Portrait thumbnail on a landscape task view
- canvas.drawRect(Math.max(0, thumbnailWidth), 0, viewWidth, viewHeight,
- mBgFillPaint);
- }
- if (thumbnailHeight < viewHeight) {
- // Landscape thumbnail on a portrait task view
- canvas.drawRect(0, Math.max(0, thumbnailHeight), viewWidth, viewHeight,
- mBgFillPaint);
- }
-
- // Draw the thumbnail
- canvas.drawRect(0, 0, thumbnailWidth, thumbnailHeight, mDrawPaint);
- } else {
- canvas.drawRect(0, 0, viewWidth, viewHeight, mBgFillPaint);
- }
+ canvas.drawRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(),
+ mCornerRadius, mCornerRadius, mPaint);
}
private void updateThumbnailPaintFilter() {
int mul = (int) (mDimAlpha * 255);
if (mBitmapShader != null) {
- mLightingColorFilter = new LightingColorFilter(Color.argb(255, mul, mul, mul), 0);
- mDrawPaint.setColorFilter(mLightingColorFilter);
- mDrawPaint.setColor(0xFFffffff);
- mBgFillPaint.setColorFilter(mLightingColorFilter);
+ mPaint.setColorFilter(getLightingColorFilter(mul));
} else {
- mDrawPaint.setColorFilter(null);
- mDrawPaint.setColor(Color.argb(255, mul, mul, mul));
+ mPaint.setColorFilter(null);
+ mPaint.setColor(Color.argb(255, mul, mul, mul));
}
invalidate();
}
private void updateThumbnailMatrix() {
- mThumbnailScale = 1f;
if (mBitmapShader != null && mThumbnailData != null) {
+ float scale = mThumbnailData.scale;
+ float thumbnailWidth = mThumbnailData.thumbnail.getWidth() -
+ (mThumbnailData.insets.left + mThumbnailData.insets.right) * scale;
+ float thumbnailHeight = mThumbnailData.thumbnail.getHeight() -
+ (mThumbnailData.insets.top + mThumbnailData.insets.bottom) * scale;
+ final float thumbnailScale;
+
if (getMeasuredWidth() == 0) {
// If we haven't measured , skip the thumbnail drawing and only draw the background
// color
- mThumbnailScale = 0f;
+ thumbnailScale = 0f;
} else {
- float invThumbnailScale = 1f / mThumbnailScale;
final Configuration configuration =
getContext().getApplicationContext().getResources().getConfiguration();
final DeviceProfile profile = Launcher.getLauncher(getContext()).getDeviceProfile();
- if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
- if (mThumbnailData.orientation == Configuration.ORIENTATION_PORTRAIT) {
- // If we are in the same orientation as the screenshot, just scale it to the
- // width of the task view
- mThumbnailScale = (float) getMeasuredWidth() / mThumbnailRect.width();
- } else {
- // Scale the landscape thumbnail up to app size, then scale that to the task
- // view size to match other portrait screenshots
- mThumbnailScale = invThumbnailScale *
- ((float) getMeasuredWidth() / profile.getCurrentWidth());
- }
+ if (configuration.orientation == mThumbnailData.orientation) {
+ // If we are in the same orientation as the screenshot, just scale it to the
+ // width of the task view
+ thumbnailScale = getMeasuredWidth() / thumbnailWidth;
+ } else if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ // Scale the landscape thumbnail up to app size, then scale that to the task
+ // view size to match other portrait screenshots
+ thumbnailScale = ((float) getMeasuredWidth() / profile.widthPx);
} else {
// Otherwise, scale the screenshot to fit 1:1 in the current orientation
- mThumbnailScale = invThumbnailScale;
+ thumbnailScale = 1;
}
}
- mMatrix.setTranslate(-mThumbnailData.insets.left, -mThumbnailData.insets.top);
- mMatrix.postScale(mThumbnailScale, mThumbnailScale);
+ mMatrix.setTranslate(-mThumbnailData.insets.left * scale,
+ -mThumbnailData.insets.top * scale);
+ mMatrix.postScale(thumbnailScale, thumbnailScale);
mBitmapShader.setLocalMatrix(mMatrix);
+
+ float bitmapHeight = Math.max(thumbnailHeight * thumbnailScale, 0);
+ Shader shader = mBitmapShader;
+ if (bitmapHeight < getMeasuredHeight()) {
+ int color = mPaint.getColor();
+ LinearGradient fade = new LinearGradient(
+ 0, bitmapHeight - mFadeLength, 0, bitmapHeight,
+ color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
+ shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+ }
+
+ float bitmapWidth = Math.max(thumbnailWidth * thumbnailScale, 0);
+ if (bitmapWidth < getMeasuredWidth()) {
+ int color = mPaint.getColor();
+ LinearGradient fade = new LinearGradient(
+ bitmapWidth - mFadeLength, 0, bitmapWidth, 0,
+ color & 0x00FFFFFF, color, Shader.TileMode.CLAMP);
+ shader = new ComposeShader(fade, shader, Mode.DST_OVER);
+ }
+ mPaint.setShader(shader);
}
+
+ mOverlay.setTaskInfo(mTask, mThumbnailData, mMatrix);
invalidate();
}
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ updateThumbnailMatrix();
+ }
+
+ private static LightingColorFilter getLightingColorFilter(int dimColor) {
+ if (dimColor < 0) {
+ dimColor = 0;
+ } else if (dimColor > 255) {
+ dimColor = 255;
+ }
+ if (sDimFilterCache[dimColor] == null) {
+ sDimFilterCache[dimColor] =
+ new LightingColorFilter(Color.argb(255, dimColor, dimColor, dimColor), 0);
+ }
+ return sDimFilterCache[dimColor];
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
new file mode 100644
index 0000000..b31d42f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * Contains helpful methods for retrieving data from {@link Task}s.
+ * TODO: remove this once we switch to getting the icon and label from IconCache.
+ */
+public class TaskUtils {
+
+ private static final String TAG = "TaskUtils";
+
+ public static CharSequence getTitle(Launcher launcher, Task task) {
+ LauncherAppsCompat launcherAppsCompat = LauncherAppsCompat.getInstance(launcher);
+ UserManagerCompat userManagerCompat = UserManagerCompat.getInstance(launcher);
+ PackageManager packageManager = launcher.getPackageManager();
+ UserHandle user = UserHandle.of(task.key.userId);
+ ApplicationInfo applicationInfo = launcherAppsCompat.getApplicationInfo(
+ task.getTopComponent().getPackageName(), 0, user);
+ if (applicationInfo == null) {
+ Log.e(TAG, "Failed to get title for task " + task);
+ return "";
+ }
+ return userManagerCompat.getBadgedLabelForUser(
+ applicationInfo.loadLabel(packageManager), user);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 029afd6..b407f75 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -16,30 +16,50 @@
package com.android.quickstep;
+import static com.android.quickstep.RecentsView.SCROLL_TYPE_TASK;
+import static com.android.quickstep.RecentsView.SCROLL_TYPE_WORKSPACE;
+
+import android.animation.TimeInterpolator;
import android.app.ActivityOptions;
import android.content.Context;
-import android.graphics.Rect;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.os.Handler;
import android.util.AttributeSet;
+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.launcher3.uioverrides.OverviewState;
+import com.android.quickstep.RecentsView.PageCallbacks;
+import com.android.quickstep.RecentsView.ScrollState;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskCallbacks;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.function.Consumer;
/**
* A task in the Recents view.
*/
-public class TaskView extends FrameLayout implements TaskCallbacks {
+public class TaskView extends FrameLayout implements TaskCallbacks, PageCallbacks {
+
+ /** Designates how "curvy" the carousel is from 0 to 1, where 0 is a straight line. */
+ public static final float CURVE_FACTOR = 0.25f;
+ /** A circular curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
+ public static final TimeInterpolator CURVE_INTERPOLATOR
+ = x -> (float) (1 - Math.sqrt(1 - Math.pow(x, 2)));
+
+ /**
+ * The alpha of a black scrim on a page in the carousel as it leaves the screen.
+ * In the resting position of the carousel, the adjacent pages have about half this scrim.
+ */
+ private static final float MAX_PAGE_SCRIM_ALPHA = 0.8f;
+
+ private static final long SCALE_ICON_DURATION = 120;
private Task mTask;
private TaskThumbnailView mSnapshotView;
@@ -55,13 +75,13 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setOnClickListener((view) -> {
- launchTask(true /* animate */);
- });
+ setOnClickListener((view) -> launchTask(true /* animate */));
+ setOutlineProvider(new TaskOutlineProvider(getResources()));
}
@Override
protected void onFinishInflate() {
+ super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
}
@@ -74,6 +94,7 @@
mTask.removeCallback(this);
}
mTask = task;
+ mSnapshotView.bind();
task.addCallback(this);
}
@@ -86,51 +107,106 @@
}
public void launchTask(boolean animate) {
+ launchTask(animate, null, null);
+ }
+
+ public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
+ Handler resultCallbackHandler) {
if (mTask != null) {
final ActivityOptions opts;
if (animate) {
- // Calculate the bounds of the thumbnail to animate from
- final Rect bounds = new Rect();
- final int[] pos = new int[2];
- mSnapshotView.getLocationInWindow(pos);
- bounds.set(pos[0], pos[1],
- pos[0] + mSnapshotView.getWidth(),
- pos[1] + mSnapshotView.getHeight());
- AppTransitionAnimationSpecsFuture animFuture =
- new AppTransitionAnimationSpecsFuture(getHandler()) {
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- ArrayList<AppTransitionAnimationSpecCompat> specs =
- new ArrayList<>();
- specs.add(new AppTransitionAnimationSpecCompat(mTask.key.id, null,
- bounds));
- return specs;
- }
- };
- opts = RecentsTransition.createAspectScaleAnimation(
- getContext(), getHandler(), true /* scaleUp */, animFuture, null);
+ opts = Launcher.getLauncher(getContext()).getActivityLaunchOptions(this, false);
} else {
opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0);
}
ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
- opts, null, null);
+ opts, resultCallback, resultCallbackHandler);
}
}
@Override
public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
- mSnapshotView.setThumbnail(thumbnailData);
+ mSnapshotView.setThumbnail(task, thumbnailData);
mIconView.setImageDrawable(task.icon);
+ mIconView.setOnClickListener(icon -> TaskMenuView.showForTask(this));
+ mIconView.setOnLongClickListener(icon -> TaskMenuView.showForTask(this));
}
@Override
public void onTaskDataUnloaded() {
- mSnapshotView.setThumbnail(null);
+ mSnapshotView.setThumbnail(null, null);
mIconView.setImageDrawable(null);
+ mIconView.setOnLongClickListener(null);
}
@Override
public void onTaskWindowingModeChanged() {
// Do nothing
}
+
+ public void animateIconToScale(float scale) {
+ mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
+ }
+
+ protected void setIconScale(float iconScale) {
+ mIconView.animate().cancel();
+ mIconView.setScaleX(iconScale);
+ mIconView.setScaleY(iconScale);
+ }
+
+ public void resetVisualProperties() {
+ setScaleX(1f);
+ setScaleY(1f);
+ setTranslationX(0f);
+ setTranslationY(0f);
+ setAlpha(1f);
+ }
+
+ @Override
+ public int onPageScroll(ScrollState scrollState) {
+ float curveInterpolation =
+ CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
+ float scale = 1 - curveInterpolation * CURVE_FACTOR;
+ setScaleX(scale);
+ setScaleY(scale);
+
+ // Make sure the biggest card (i.e. the one in front) shows on top of the adjacent ones.
+ setTranslationZ(scale);
+
+ mSnapshotView.setDimAlpha(1 - curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
+
+ float translation =
+ scrollState.distanceFromScreenCenter * curveInterpolation * CURVE_FACTOR;
+
+ if (scrollState.lastScrollType == SCROLL_TYPE_WORKSPACE) {
+ // Make sure that the task cards do not overlap with the workspace card
+ float min = scrollState.halfPageWidth * (1 - scale);
+ if (scrollState.isRtl) {
+ setTranslationX(Math.min(translation, min) - scrollState.prevPageExtraWidth);
+ } else {
+ setTranslationX(Math.max(translation, -min) + scrollState.prevPageExtraWidth);
+ }
+ } else {
+ setTranslationX(translation);
+ }
+ scrollState.prevPageExtraWidth = 0;
+ return SCROLL_TYPE_TASK;
+ }
+
+ private static final class TaskOutlineProvider extends ViewOutlineProvider {
+
+ private final int mMarginTop;
+ private final float mRadius;
+
+ TaskOutlineProvider(Resources res) {
+ mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
+ mRadius = res.getDimension(R.dimen.task_corner_radius);
+ }
+
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, mMarginTop, view.getWidth(),
+ view.getHeight(), mRadius);
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
new file mode 100644
index 0000000..f35f6a6
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TouchConsumer.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.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;
+
+@TargetApi(Build.VERSION_CODES.O)
+@FunctionalInterface
+public interface TouchConsumer extends Consumer<MotionEvent> {
+
+ static boolean isInteractionQuick(@InteractionType int interactionType) {
+ return interactionType == INTERACTION_QUICK_SCRUB ||
+ interactionType == INTERACTION_QUICK_SWITCH;
+ }
+
+ @IntDef(flag = true, value = {
+ INTERACTION_NORMAL,
+ INTERACTION_QUICK_SWITCH,
+ INTERACTION_QUICK_SCRUB
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface InteractionType {}
+ int INTERACTION_NORMAL = 0;
+ int INTERACTION_QUICK_SWITCH = 1;
+ int INTERACTION_QUICK_SCRUB = 2;
+
+ default void reset() { }
+
+ default void updateTouchTracking(@InteractionType int interactionType) { }
+
+ default void onQuickScrubEnd() { }
+
+ default void onQuickScrubProgress(float progress) { }
+
+ /**
+ * Called on the binder thread to allow the consumer to process the motion event before it is
+ * posted on a handler thread.
+ */
+ default void preProcessMotionEvent(MotionEvent ev) { }
+
+ default Choreographer getIntrimChoreographer(MotionEventQueue queue) {
+ return null;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index b57a417..c166292 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -15,85 +15,157 @@
*/
package com.android.quickstep;
-import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_POINTER_DOWN;
+import static android.view.MotionEvent.ACTION_POINTER_UP;
+import static android.view.MotionEvent.ACTION_UP;
+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;
-import android.app.ActivityOptions;
import android.app.Service;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Point;
import android.graphics.PointF;
-import android.graphics.Rect;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseArray;
import android.view.Choreographer;
-import android.view.Display;
import android.view.MotionEvent;
-import android.view.VelocityTracker;
+import android.view.View;
import android.view.ViewConfiguration;
-import android.view.WindowManager;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.MainThreadExecutor;
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.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.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.BackgroundExecutor;
+import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
/**
* Service connected by system-UI for handling touch interaction.
*/
+@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";
- private static RecentsTaskLoader sRecentsTaskLoader;
+ /**
+ * A background thread used for handling UI for another window.
+ */
+ private static HandlerThread sRemoteUiThread;
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@Override
- public void onMotionEvent(MotionEvent ev) {
- handleMotionEvent(ev);
+ public void onPreMotionEvent(@HitTarget int downHitTarget) throws RemoteException {
+ TraceHelper.beginSection("SysUiBinder");
+ onBinderPreMotionEvent(downHitTarget);
+ TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget);
}
@Override
- public void onBind(ISystemUiProxy iSystemUiProxy) throws RemoteException {
+ public void onMotionEvent(MotionEvent ev) {
+ mEventQueue.queue(ev);
+
+ String name = sMotionEventNames.get(ev.getActionMasked());
+ if (name != null){
+ TraceHelper.partitionSection("SysUiBinder", name);
+ }
+ }
+
+ @Override
+ public void onBind(ISystemUiProxy iSystemUiProxy) {
mISystemUiProxy = iSystemUiProxy;
+ mRecentsModel.setSystemUiProxy(mISystemUiProxy);
+ 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() {
+ mEventQueue.onQuickSwitch();
+ TraceHelper.endSection("SysUiBinder", "onQuickSwitch");
+ }
+
+ @Override
+ public void onQuickScrubStart() {
+ mEventQueue.onQuickScrubStart();
+ sQuickScrubEnabled = true;
+ TraceHelper.partitionSection("SysUiBinder", "onQuickScrubStart");
+ }
+
+ @Override
+ public void onQuickScrubProgress(float progress) {
+ mEventQueue.onQuickScrubProgress(progress);
+ }
+
+ @Override
+ public void onQuickScrubEnd() {
+ mEventQueue.onQuickScrubEnd();
+ TraceHelper.endSection("SysUiBinder", "onQuickScrubEnd");
+ sQuickScrubEnabled = false;
}
};
+ private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
+
+ private static boolean sConnected = false;
+ private static boolean sQuickScrubEnabled = false;
+
+ public static boolean isConnected() {
+ return sConnected;
+ }
+
+ public static boolean isQuickScrubEnabled() {
+ return sQuickScrubEnabled;
+ }
+
private ActivityManagerWrapper mAM;
private RunningTaskInfo mRunningTask;
+ private RecentsModel mRecentsModel;
private Intent mHomeIntent;
private ComponentName mLauncher;
- private Choreographer mChoreographer;
+ private MotionEventQueue mEventQueue;
private MainThreadExecutor mMainThreadExecutor;
-
- private int mDisplayRotation;
- private final Point mDisplaySize = new Point();
- private final PointF mDownPos = new PointF();
- private final PointF mLastPos = new PointF();
- private int mActivePointerId = INVALID_POINTER_ID;
- private VelocityTracker mVelocityTracker;
- private int mTouchSlop;
- private NavBarSwipeInteractionHandler mInteractionHandler;
-
private ISystemUiProxy mISystemUiProxy;
+ private Choreographer mMainThreadChoreographer;
+ private Choreographer mBackgroundThreadChoreographer;
+ private MotionEventQueue mNoOpEventQueue;
+
@Override
public void onCreate() {
super.onCreate();
mAM = ActivityManagerWrapper.getInstance();
+ mRecentsModel = RecentsModel.getInstance(this);
+ mMainThreadExecutor = new MainThreadExecutor();
mHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
@@ -101,18 +173,25 @@
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ResolveInfo info = getPackageManager().resolveActivity(mHomeIntent, 0);
mLauncher = new ComponentName(getPackageName(), info.activityInfo.name);
- mHomeIntent.setComponent(mLauncher);
+ // Clear the packageName as system can fail to dedupe it b/64108432
+ mHomeIntent.setComponent(mLauncher).setPackage(null);
- Resources res = getResources();
- if (sRecentsTaskLoader == null) {
- sRecentsTaskLoader = new RecentsTaskLoader(this,
- res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
- res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
- sRecentsTaskLoader.startLoader(this);
- }
+ mMainThreadChoreographer = Choreographer.getInstance();
+ mNoOpEventQueue = new MotionEventQueue(mMainThreadChoreographer, mNoOpTouchConsumer);
+ mEventQueue = mNoOpEventQueue;
- mChoreographer = Choreographer.getInstance();
- mMainThreadExecutor = new MainThreadExecutor();
+ sConnected = true;
+
+ // Temporarily disable model preload
+ // new ModelPreload().start(this);
+ initBackgroundChoreographer();
+ }
+
+ @Override
+ public void onDestroy() {
+ sConnected = false;
+ sQuickScrubEnabled = false;
+ super.onDestroy();
}
@Override
@@ -121,145 +200,156 @@
return mMyBinder;
}
- public static RecentsTaskLoader getRecentsTaskLoader() {
- return sRecentsTaskLoader;
+ private void onBinderPreMotionEvent(@HitTarget int downHitTarget) {
+ mRunningTask = mAM.getRunningTask();
+
+ mEventQueue.reset();
+
+ 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));
+ }
}
- private void handleMotionEvent(MotionEvent ev) {
- if (ev.getActionMasked() != MotionEvent.ACTION_DOWN && mVelocityTracker == null) {
- return;
+ private MotionEventQueue getLauncherEventQueue() {
+ Launcher launcher = (Launcher) LauncherAppState.getInstance(this).getModel().getCallback();
+ if (launcher == null) {
+ return mNoOpEventQueue;
}
- switch (ev.getActionMasked()) {
- case MotionEvent.ACTION_DOWN: {
- mActivePointerId = ev.getPointerId(0);
+
+ View target = launcher.getDragLayer();
+ return new MotionEventQueue(mMainThreadChoreographer,
+ new LauncherTouchConsumer(launcher, target));
+ }
+
+ 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(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());
- mLastPos.set(mDownPos);
- mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
- Display display = getSystemService(WindowManager.class).getDefaultDisplay();
- display.getRealSize(mDisplaySize);
- mDisplayRotation = display.getRotation();
+ mHadWindowFocusOnDown = mTarget.hasWindowFocus();
+ } else if (!mTrackingStarted && mHadWindowFocusOnDown) {
+ switch (action) {
+ case ACTION_POINTER_UP:
+ case ACTION_POINTER_DOWN:
+ if (!mTrackingStarted) {
+ mInvalidated = true;
+ }
+ break;
+ case ACTION_MOVE: {
+ float displacement = ev.getY() - mDownPos.y;
+ if (Math.abs(displacement) >= mTouchSlop) {
+ mTrackingStarted = true;
+ mTarget.getLocationOnScreen(mLocationOnScreen);
- mRunningTask = mAM.getRunningTask();
- if (mRunningTask == null || mRunningTask.topActivity.equals(mLauncher)) {
- // TODO: We could drive all-apps in this case. For now just ignore swipe.
- break;
- }
-
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- mVelocityTracker.clear();
- }
- mVelocityTracker.addMovement(ev);
- if (mInteractionHandler != null) {
- mInteractionHandler.endTouch(0);
- mInteractionHandler = null;
- }
- 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);
- mVelocityTracker.clear();
- }
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- int pointerIndex = ev.findPointerIndex(mActivePointerId);
- if (pointerIndex == INVALID_POINTER_ID) {
- break;
- }
- mVelocityTracker.addMovement(ev);
- mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-
- float displacement = ev.getY(pointerIndex) - mDownPos.y;
- if (mInteractionHandler == null) {
- if (Math.abs(displacement) >= mTouchSlop) {
- startTouchTracking();
+ // Send a down event only when mTouchSlop is crossed.
+ MotionEvent down = MotionEvent.obtain(ev);
+ down.setAction(ACTION_DOWN);
+ sendEvent(down);
+ down.recycle();
+ }
}
- } else {
- // Move
- mInteractionHandler.updateDisplacement(displacement);
}
- break;
}
- case MotionEvent.ACTION_CANCEL:
- // TODO: Should be different than ACTION_UP
- case MotionEvent.ACTION_UP: {
- endInteraction();
- break;
+ if (mTrackingStarted) {
+ sendEvent(ev);
+ }
+
+ if (action == ACTION_UP || action == ACTION_CANCEL) {
+ mInvalidated = true;
}
}
- }
- private void startTouchTracking() {
- // Create the shared handler
- final NavBarSwipeInteractionHandler handler =
- new NavBarSwipeInteractionHandler(mRunningTask, mChoreographer, this);
-
- // Preload and start the recents activity on a background thread
- final Context context = this;
- final RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(context);
- final int taskId = mRunningTask.id;
-
- BackgroundExecutor.get().submit(() -> {
- // Get the snap shot before
- handler.setTaskSnapshot(getCurrentTaskSnapshot());
-
- // Start the launcher activity with our custom handler
- Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
- startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
-
- /*
- ActivityManagerWrapper.getInstance().startRecentsActivity(null, options,
- ActivityOptions.makeCustomAnimation(this, 0, 0), UserHandle.myUserId(),
- null, null);
- */
-
- // Preload the plan
- RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
- PreloadOptions opts = new PreloadOptions();
- opts.loadTitles = false;
- loadPlan.preloadPlan(opts, loader, taskId, UserHandle.myUserId());
- // Set the load plan on UI thread
- mMainThreadExecutor.execute(() -> handler.setRecentsTaskLoadPlan(loadPlan));
- });
- mInteractionHandler = handler;
- }
-
- private void endInteraction() {
- if (mInteractionHandler != null) {
- mVelocityTracker.computeCurrentVelocity(1000,
- ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
-
- mInteractionHandler.endTouch(mVelocityTracker.getYVelocity(mActivePointerId));
- mInteractionHandler = null;
- }
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
-
- private Bitmap getCurrentTaskSnapshot() {
- if (mISystemUiProxy == null) {
- Log.e(TAG, "Never received systemUIProxy");
- return null;
+ private void sendEvent(MotionEvent ev) {
+ int flags = ev.getEdgeFlags();
+ ev.setEdgeFlags(flags | EDGE_NAV_BAR);
+ ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
+ mTarget.dispatchTouchEvent(ev);
+ ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
+ ev.setEdgeFlags(flags);
}
- // TODO: We are using some hardcoded layers for now, to best approximate the activity layers
- try {
- return mISystemUiProxy.screenshot(new Rect(), mDisplaySize.x, mDisplaySize.y, 0, 100000,
- false, mDisplayRotation).toBitmap();
- } catch (RemoteException e) {
- Log.e(TAG, "Error capturing snapshot", e);
- return null;
+ @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() {
+ if (sRemoteUiThread == null) {
+ sRemoteUiThread = new HandlerThread("remote-ui");
+ sRemoteUiThread.start();
+ }
+ new Handler(sRemoteUiThread.getLooper()).post(() ->
+ mBackgroundThreadChoreographer = Choreographer.getInstance());
}
}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
new file mode 100644
index 0000000..19942c3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -0,0 +1,821 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import 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;
+import static com.android.quickstep.TouchConsumer.isInteractionQuick;
+import static com.android.systemui.shared.recents.utilities.Utilities.postAtFrontOfQueueAsynchronously;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.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;
+import android.support.annotation.UiThread;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.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;
+import com.android.systemui.shared.system.WindowManagerWrapper;
+
+import java.util.StringJoiner;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class WindowTransformSwipeHandler extends BaseSwipeInteractionHandler {
+ private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
+ private static final boolean DEBUG_STATES = false;
+
+ // Launcher UI related states
+ private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
+ private static final int STATE_LAUNCHER_STARTED = 1 << 1;
+ private static final int STATE_LAUNCHER_DRAWN = 1 << 2;
+ private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 3;
+
+ // Internal initialization states
+ private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
+
+ // Interaction finish states
+ private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 5;
+ private static final int STATE_SCALED_CONTROLLER_APP = 1 << 6;
+
+ private static final int STATE_HANDLER_INVALIDATED = 1 << 7;
+ private static final int STATE_GESTURE_STARTED = 1 << 8;
+ private static final int STATE_GESTURE_CANCELLED = 1 << 9;
+
+ // States for quick switch/scrub
+ private static final int STATE_SWITCH_TO_SCREENSHOT_COMPLETE = 1 << 10;
+ private static final int STATE_QUICK_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_STARTED;
+
+ // For debugging, keep in sync with above states
+ private static final String[] STATES = new String[] {
+ "STATE_LAUNCHER_PRESENT",
+ "STATE_LAUNCHER_STARTED",
+ "STATE_LAUNCHER_DRAWN",
+ "STATE_ACTIVITY_MULTIPLIER_COMPLETE",
+ "STATE_APP_CONTROLLER_RECEIVED",
+ "STATE_SCALED_CONTROLLER_RECENTS",
+ "STATE_SCALED_CONTROLLER_APP",
+ "STATE_HANDLER_INVALIDATED",
+ "STATE_GESTURE_STARTED",
+ "STATE_GESTURE_CANCELLED",
+ "STATE_SWITCH_TO_SCREENSHOT_COMPLETE",
+ "STATE_QUICK_SWITCH",
+ "STATE_QUICK_SCRUB_START",
+ "STATE_QUICK_SCRUB_END"
+ };
+
+ private static final long MAX_SWIPE_DURATION = 200;
+ private static final long MIN_SWIPE_DURATION = 80;
+
+ private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
+
+ // The bounds of the source app in device coordinates
+ private final Rect mSourceStackBounds = new Rect();
+ // The insets of the source app
+ private final Rect mSourceInsets = new Rect();
+ // The source app bounds with the source insets applied, in the source app window coordinates
+ private final RectF mSourceRect = new RectF();
+ // The bounds of the task view in launcher window coordinates
+ private final RectF mTargetRect = new RectF();
+ // Doesn't change after initialized, used as an anchor when changing mTargetRect
+ private final RectF mInitialTargetRect = new RectF();
+ // The insets to be used for clipping the app window, which can be larger than mSourceInsets
+ // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
+ // app window coordinates.
+ private final RectF mSourceWindowClipInsets = new RectF();
+
+ // The bounds of launcher (not including insets) in device coordinates
+ private final Rect mHomeStackBounds = new Rect();
+ // The clip rect in source app window coordinates
+ private final Rect mClipRect = new Rect();
+ private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
+ private DeviceProfile mDp;
+ private int mTransitionDragLength;
+
+ // Shift in the range of [0, 1].
+ // 0 => preview snapShot is completely visible, and hotseat is completely translated down
+ // 1 => preview snapShot is completely aligned with the recents view and hotseat is completely
+ // visible.
+ private final AnimatedFloat mCurrentShift = new AnimatedFloat(this::updateFinalShift);
+
+ private final MainThreadExecutor mMainExecutor = new MainThreadExecutor();
+
+ private final Context mContext;
+ private final int mRunningTaskId;
+
+ private MultiStateCallback mStateCallback;
+ private AnimatorPlaybackController mLauncherTransitionController;
+
+ private Launcher mLauncher;
+ private LauncherLayoutListener mLauncherLayoutListener;
+ private RecentsView mRecentsView;
+ private QuickScrubController mQuickScrubController;
+
+ private Runnable mLauncherDrawnCallback;
+
+ private boolean mWasLauncherAlreadyVisible;
+
+ private float mCurrentDisplacement;
+ private boolean mGestureStarted;
+ private int mLogAction = Touch.SWIPE;
+
+ private @InteractionType int mInteractionType = INTERACTION_NORMAL;
+
+ private InputConsumerController mInputConsumer =
+ InputConsumerController.getRecentsAnimationInputConsumer();
+
+ private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
+ private Matrix mTmpMatrix = new Matrix();
+
+ WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context) {
+ mContext = context;
+ mRunningTaskId = runningTaskInfo.id;
+ mInputConsumer.registerInputConsumer();
+ initStateCallbacks();
+ }
+
+ private void initStateCallbacks() {
+ mStateCallback = new MultiStateCallback() {
+ @Override
+ public void setState(int stateFlag) {
+ debugNewState(stateFlag);
+ 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_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_SCALED_CONTROLLER_APP,
+ this::resumeLastTask);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_ACTIVITY_MULTIPLIER_COMPLETE
+ | STATE_SCALED_CONTROLLER_RECENTS,
+ this::switchToScreenshot);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
+ | STATE_ACTIVITY_MULTIPLIER_COMPLETE
+ | STATE_SCALED_CONTROLLER_RECENTS
+ | STATE_SWITCH_TO_SCREENSHOT_COMPLETE,
+ this::setupLauncherUiAfterSwipeUpAnimation);
+
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_SCALED_CONTROLLER_APP,
+ this::reset);
+
+ mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+ this::invalidateHandlerWithLauncher);
+
+ mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_QUICK_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) {
+ Handler handler = mMainExecutor.getHandler();
+ if (Looper.myLooper() == handler.getLooper()) {
+ mStateCallback.setState(stateFlag);
+ } else {
+ postAtFrontOfQueueAsynchronously(handler, () -> mStateCallback.setState(stateFlag));
+ }
+ }
+
+ private void initTransitionEndpoints(DeviceProfile dp) {
+ mDp = dp;
+ mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
+ mSourceStackBounds.width() - mSourceInsets.right,
+ mSourceStackBounds.height() - mSourceInsets.bottom);
+
+ 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);
+
+ // Calculate the clip based on the target rect (since the content insets and the
+ // launcher insets may differ, so the aspect ratio of the target rect can differ
+ // from the source rect. The difference between the target rect (scaled to the
+ // source rect) is the amount to clip on each edge.
+ RectF scaledTargetRect = new RectF(mTargetRect);
+ Utilities.scaleRectFAboutCenter(scaledTargetRect,
+ mSourceRect.width() / mTargetRect.width());
+ scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
+ mSourceWindowClipInsets.set(
+ Math.max(scaledTargetRect.left, 0),
+ Math.max(scaledTargetRect.top, 0),
+ Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
+ Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
+ mSourceRect.set(scaledTargetRect);
+
+ Rect targetInsets = dp.getInsets();
+ if (dp.isVerticalBarLayout()) {
+ int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
+ mTransitionDragLength = dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset;
+ } else {
+ mTransitionDragLength = dp.heightPx - tempRect.bottom;
+ }
+ }
+
+ private long getFadeInDuration() {
+ if (mCurrentShift.getCurrentAnimation() != null) {
+ ObjectAnimator anim = mCurrentShift.getCurrentAnimation();
+ long theirDuration = anim.getDuration() - anim.getCurrentPlayTime();
+
+ // TODO: Find a better heuristic
+ return Math.min(MAX_SWIPE_DURATION, Math.max(theirDuration, MIN_SWIPE_DURATION));
+ } else {
+ return MAX_SWIPE_DURATION;
+ }
+ }
+
+ @Override
+ protected boolean init(final Launcher launcher, boolean alreadyOnHome) {
+ if (launcher == mLauncher) {
+ return true;
+ }
+ if (mLauncher != null) {
+ // The launcher may have been recreated as a result of device rotation.
+ int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
+ initStateCallbacks();
+ mStateCallback.setState(oldState);
+ mLauncherLayoutListener.setHandler(null);
+ }
+ 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(mLauncher, mWasLauncherAlreadyVisible);
+
+
+ if (mWasLauncherAlreadyVisible && !mLauncher.getAppTransitionManager().isAnimating()) {
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
+ mLauncherTransitionController = mLauncher.getStateManager()
+ .createAnimationToNewWorkspace(OVERVIEW, accuracy);
+ mLauncherTransitionController.dispatchOnStart();
+ mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
+
+ mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_DRAWN);
+ } else {
+ TraceHelper.beginSection("WTS-init");
+ mLauncher.getStateManager().goToState(OVERVIEW, false);
+ TraceHelper.partitionSection("WTS-init", "State changed");
+
+ // TODO: Implement a better animation for fading in
+ View rootView = mLauncher.getRootView();
+ rootView.setAlpha(0);
+ rootView.getViewTreeObserver().addOnDrawListener(new OnDrawListener() {
+
+ @Override
+ public void onDraw() {
+ TraceHelper.endSection("WTS-init", "Launcher frame is drawn");
+ rootView.post(() ->
+ rootView.getViewTreeObserver().removeOnDrawListener(this));
+ if (launcher != mLauncher) {
+ return;
+ }
+
+ mStateCallback.setState(STATE_LAUNCHER_DRAWN);
+ }
+ });
+
+ // 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();
+ }
+
+ public void setLauncherOnDrawCallback(Runnable callback) {
+ mLauncherDrawnCallback = callback;
+ }
+
+ private void launcherFrameDrawn() {
+ View rootView = mLauncher.getRootView();
+ if (rootView.getAlpha() < 1) {
+ if (mGestureStarted) {
+ final MultiStateCallback callback = mStateCallback;
+ rootView.animate().alpha(1)
+ .setDuration(getFadeInDuration())
+ .withEndAction(() -> callback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE));
+ } else {
+ rootView.setAlpha(1);
+ mStateCallback.setState(STATE_ACTIVITY_MULTIPLIER_COMPLETE);
+ }
+ }
+ if (mLauncherDrawnCallback != null) {
+ mLauncherDrawnCallback.run();
+ }
+ }
+
+ private void initializeLauncherAnimationController() {
+ mLauncherLayoutListener.setHandler(this);
+ onLauncherLayoutChanged();
+ }
+
+ public void updateInteractionType(@InteractionType int interactionType) {
+ if (mInteractionType != INTERACTION_NORMAL) {
+ throw new IllegalArgumentException(
+ "Can't change interaction type from " + mInteractionType);
+ }
+ if (!isInteractionQuick(interactionType)) {
+ throw new IllegalArgumentException(
+ "Can't change interaction type to " + interactionType);
+ }
+ mInteractionType = interactionType;
+
+ 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 onQuickInteractionStart() {
+ mQuickScrubController.onQuickScrubStart(false);
+ }
+
+ @WorkerThread
+ public void updateDisplacement(float displacement) {
+ mCurrentDisplacement = displacement;
+
+ float translation = Utilities.boundToRange(-mCurrentDisplacement, 0, mTransitionDragLength);
+ float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
+ mCurrentShift.updateValue(shift);
+ }
+
+ /**
+ * Called by {@link #mLauncherLayoutListener} when launcher layout changes
+ */
+ public void onLauncherLayoutChanged() {
+ initTransitionEndpoints(mLauncher.getDeviceProfile());
+
+ if (!mWasLauncherAlreadyVisible) {
+ float startProgress;
+ AllAppsTransitionController controller = mLauncher.getAllAppsController();
+
+ if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ startProgress = 1;
+ } else {
+ float scrollRange = Math.max(controller.getShiftRange(), 1);
+ startProgress = (mTransitionDragLength / scrollRange) + 1;
+ }
+ AnimatorSet anim = new AnimatorSet();
+ ObjectAnimator shiftAnim = ObjectAnimator.ofFloat(controller, ALL_APPS_PROGRESS,
+ startProgress, OVERVIEW.getVerticalProgress(mLauncher));
+ shiftAnim.setInterpolator(LINEAR);
+ anim.play(shiftAnim);
+
+ // TODO: Link this animation to state animation, so that it is cancelled
+ // automatically on state change
+ anim.setDuration(mTransitionDragLength * 2);
+ mLauncherTransitionController =
+ AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
+ mLauncherTransitionController.setPlayFraction(mCurrentShift.value);
+ }
+ }
+
+ @WorkerThread
+ private void updateFinalShift() {
+ float shift = mCurrentShift.value;
+
+ synchronized (mRecentsAnimationWrapper) {
+ if (mRecentsAnimationWrapper.controller != null) {
+ RectF currentRect;
+ synchronized (mTargetRect) {
+ currentRect = mRectFEvaluator.evaluate(shift, mSourceRect, mTargetRect);
+ }
+
+ mClipRect.left = (int) (mSourceWindowClipInsets.left * shift);
+ mClipRect.top = (int) (mSourceWindowClipInsets.top * shift);
+ mClipRect.right = (int)
+ (mSourceStackBounds.width() - (mSourceWindowClipInsets.right * shift));
+ mClipRect.bottom = (int)
+ (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * shift));
+
+ mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
+
+ TransactionCompat transaction = new TransactionCompat();
+ for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
+ if (app.mode == MODE_CLOSING) {
+ transaction.setMatrix(app.leash, mTmpMatrix)
+ .setWindowCrop(app.leash, mClipRect)
+ .show(app.leash);
+ }
+ }
+ transaction.apply();
+ }
+ }
+
+ 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.
+ int firstTaskIndex = mRecentsView.getFirstTaskIndex();
+ View firstTask = mRecentsView.getPageAt(firstTaskIndex);
+ int scrollForFirstTask = mRecentsView.getScrollForPage(firstTaskIndex);
+ int offsetFromFirstTask = (scrollForFirstTask - mRecentsView.getScrollX());
+ if (offsetFromFirstTask != 0) {
+ synchronized (mTargetRect) {
+ mTargetRect.set(mInitialTargetRect);
+ Utilities.scaleRectFAboutCenter(mTargetRect, firstTask.getScaleX());
+ float offsetX = offsetFromFirstTask + firstTask.getTranslationX();
+ mTargetRect.offset(offsetX, 0);
+ }
+ }
+ };
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ runOnUi.run();
+ } else {
+ // The fling operation completed even before the launcher was drawn
+ mMainExecutor.execute(runOnUi);
+ }
+ }
+ }
+
+ public void onRecentsAnimationStart(RecentsAnimationControllerCompat controller,
+ RemoteAnimationTargetCompat[] apps, Rect homeContentInsets, Rect minimizedHomeBounds) {
+ if (apps != null) {
+ // Use the top closing app to determine the insets for the animation
+ for (RemoteAnimationTargetCompat target : apps) {
+ if (target.mode == MODE_CLOSING) {
+ DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+ if (minimizedHomeBounds != null) {
+ mHomeStackBounds.set(minimizedHomeBounds);
+ dp = dp.getMultiWindowProfile(mContext,
+ new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
+ dp.updateInsets(homeContentInsets);
+ } else {
+ mHomeStackBounds.set(new Rect(0, 0, dp.widthPx, dp.heightPx));
+ // TODO: Workaround for an existing issue where the home content insets are
+ // not valid immediately after rotation, just use the stable insets for now
+ Rect insets = new Rect();
+ WindowManagerWrapper.getInstance().getStableInsets(insets);
+ dp.updateInsets(insets);
+ }
+
+ // Initialize the start and end animation bounds
+ // TODO: Remove once platform is updated
+ try {
+ mSourceInsets.set(target.getContentInsets());
+ } catch (Error e) {
+ // TODO: Remove once platform is updated, use stable insets as fallback
+ WindowManagerWrapper.getInstance().getStableInsets(mSourceInsets);
+ }
+ mSourceStackBounds.set(target.sourceContainerBounds);
+
+ initTransitionEndpoints(dp);
+ break;
+ }
+ }
+ }
+
+ mRecentsAnimationWrapper.setController(controller, apps);
+ setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
+ }
+
+ 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();
+ }
+
+ /**
+ * Notifies the launcher that the swipe gesture has started. This can be called multiple times
+ * on both background and UI threads
+ */
+ private void notifyGestureStarted() {
+ final Launcher curLauncher = mLauncher;
+ if (curLauncher != null) {
+ curLauncher.onQuickstepGestureStarted(mWasLauncherAlreadyVisible);
+ }
+ }
+
+ @WorkerThread
+ public void onGestureEnded(float endVelocity) {
+ Resources res = mContext.getResources();
+ float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity);
+ boolean isFling = Math.abs(endVelocity) > flingThreshold;
+
+ long duration = MAX_SWIPE_DURATION;
+ final float endShift;
+ if (!isFling) {
+ endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
+ mLogAction = Touch.SWIPE;
+ } else {
+ endShift = endVelocity < 0 ? 1 : 0;
+ float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity);
+ if (Math.abs(endVelocity) > minFlingVelocity && mTransitionDragLength > 0) {
+ float distanceToTravel = (endShift - mCurrentShift.value) * mTransitionDragLength;
+
+ // we want the page's snap velocity to approximately match the velocity at
+ // which the user flings, so we scale the duration by a value near to the
+ // derivative of the scroll interpolator at zero, ie. 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);
+ anim.setInterpolator(Interpolators.SCROLL);
+ anim.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ setStateOnUiThread((Float.compare(mCurrentShift.value, 0) == 0)
+ ? STATE_SCALED_CONTROLLER_APP : STATE_SCALED_CONTROLLER_RECENTS);
+ }
+ });
+ anim.start();
+ }
+
+ @UiThread
+ private void resumeLastTask() {
+ mRecentsAnimationWrapper.finish(false /* toHome */, null);
+ doLogGesture(false /* toLauncher */);
+ }
+
+ public void reset() {
+ if (mInteractionType != INTERACTION_QUICK_SCRUB) {
+ // Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
+ // invalidated after the quick scrub ends
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ }
+ }
+
+ private void invalidateHandler() {
+ mCurrentShift.cancelAnimation();
+
+ if (mGestureEndCallback != null) {
+ mGestureEndCallback.run();
+ }
+
+ 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() {
+ if (mWasLauncherAlreadyVisible && mLauncherTransitionController != null) {
+ mLauncherTransitionController.setPlayFraction(1);
+ }
+ }
+
+ 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
+ ThumbnailData thumbnail =
+ mRecentsAnimationWrapper.controller.screenshotTask(app.taskId);
+ TaskView taskView = mRecentsView.updateThumbnail(app.taskId, thumbnail);
+ if (taskView != null) {
+ // Defer finishing the animation until the next launcher frame with the
+ // new thumbnail
+ 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();
+ }
+ }
+ 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 the first icon.
+ mRecentsView.setFirstTaskIconScaledDown(false /* isScaledDown */, true /* animate */);
+
+ reset();
+ }
+
+ public void onQuickScrubEnd() {
+ setStateOnUiThread(STATE_QUICK_SCRUB_END);
+ }
+
+ 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
+ // after the gesture ends
+ setStateOnUiThread(STATE_HANDLER_INVALIDATED);
+ }
+
+ public void onQuickScrubProgress(float progress) {
+ 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 void debugNewState(int stateFlag) {
+ if (!DEBUG_STATES) {
+ return;
+ }
+
+ int state = mStateCallback.getState();
+ StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
+ String stateFlagStr = "Unknown-" + stateFlag;
+ for (int i = 0; i < STATES.length; i++) {
+ if ((state & (i << i)) != 0) {
+ currentStateStr.add(STATES[i]);
+ }
+ if (stateFlag == (1 << i)) {
+ stateFlagStr = STATES[i] + " (" + stateFlag + ")";
+ }
+ }
+ Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
+ + currentStateStr);
+ }
+}
diff --git a/res/drawable-hdpi/work_tab_user_education.png b/res/drawable-hdpi/work_tab_user_education.png
new file mode 100644
index 0000000..1879dfb
--- /dev/null
+++ b/res/drawable-hdpi/work_tab_user_education.png
Binary files differ
diff --git a/res/drawable-mdpi/work_tab_user_education.png b/res/drawable-mdpi/work_tab_user_education.png
new file mode 100644
index 0000000..65c7e63
--- /dev/null
+++ b/res/drawable-mdpi/work_tab_user_education.png
Binary files differ
diff --git a/res/drawable-xhdpi/work_tab_user_education.png b/res/drawable-xhdpi/work_tab_user_education.png
new file mode 100644
index 0000000..59df7a8
--- /dev/null
+++ b/res/drawable-xhdpi/work_tab_user_education.png
Binary files differ
diff --git a/res/drawable-xxhdpi/work_tab_user_education.png b/res/drawable-xxhdpi/work_tab_user_education.png
new file mode 100644
index 0000000..3c6aa20
--- /dev/null
+++ b/res/drawable-xxhdpi/work_tab_user_education.png
Binary files differ
diff --git a/res/drawable/all_apps_handle_landscape.xml b/res/drawable/all_apps_handle_landscape.xml
index 23826ab..15518ff 100644
--- a/res/drawable/all_apps_handle_landscape.xml
+++ b/res/drawable/all_apps_handle_landscape.xml
@@ -15,23 +15,26 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="13dp"
- android:height="13dp"
- android:viewportWidth="13.0"
- android:viewportHeight="13.0" >
+ android:width="@dimen/dynamic_grid_min_page_indicator_size"
+ android:height="@dimen/dynamic_grid_min_page_indicator_size"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0" >
- <path
- android:pathData="M2 8.5L6.5 4L11 8.5"
- android:strokeColor="?attr/workspaceAmbientShadowColor"
- android:strokeWidth="4"
- android:strokeLineCap="round"
- android:strokeLineJoin="round" />
+ <group
+ android:translateX="17.5"
+ android:translateY="17.5">
+ <path
+ android:pathData="M2 8.5L6.5 4L11 8.5"
+ android:strokeColor="?attr/workspaceAmbientShadowColor"
+ android:strokeWidth="4"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
- <path
- android:pathData="M2 8.5L6.5 4L11 8.5"
- android:strokeColor="?attr/workspaceTextColor"
- android:strokeWidth="2"
- android:strokeLineCap="round"
- android:strokeLineJoin="round" />
-
+ <path
+ android:pathData="M2 8.5L6.5 4L11 8.5"
+ android:strokeColor="?attr/workspaceTextColor"
+ android:strokeWidth="2"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round" />
+ </group>
</vector>
diff --git a/res/drawable/bg_deferred_app_widget.xml b/res/drawable/bg_deferred_app_widget.xml
new file mode 100644
index 0000000..07bae48
--- /dev/null
+++ b/res/drawable/bg_deferred_app_widget.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<inset
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:inset="8dp">
+ <color android:color="#77000000" />
+</inset>
diff --git a/quickstep/res/drawable/task_thumbnail_background.xml b/res/drawable/ic_close.xml
similarity index 62%
copy from quickstep/res/drawable/task_thumbnail_background.xml
copy to res/drawable/ic_close.xml
index 603380e..fc9ed49 100644
--- a/quickstep/res/drawable/task_thumbnail_background.xml
+++ b/res/drawable/ic_close.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,7 +12,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="#FF000000" />
- <corners android:radius="2dp" />
-</shape>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/all_apps_handle_portrait.xml b/res/drawable/ic_corp.xml
similarity index 61%
rename from res/drawable/all_apps_handle_portrait.xml
rename to res/drawable/ic_corp.xml
index 75aa448..48f5007 100644
--- a/res/drawable/all_apps_handle_portrait.xml
+++ b/res/drawable/ic_corp.xml
@@ -13,22 +13,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="16dp"
- android:height="13dp"
- android:viewportWidth="16.0"
- android:viewportHeight="13.0" >
-
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
<path
- android:pathData="M2 6h12"
- android:strokeColor="?attr/workspaceAmbientShadowColor"
- android:strokeWidth="4"
- android:strokeLineCap="round" />
-
- <path
- android:pathData="M2 6h12"
- android:strokeColor="?attr/workspaceTextColor"
- android:strokeWidth="2"
- android:strokeLineCap="round" />
+ android:pathData="M20,6h-4V4c0,-1.11 -0.89,-2 -2,-2h-4C8.89,2 8,2.89 8,4v2H4C2.89,6 2.01,6.89 2.01,8L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2V8C22,6.89 21.11,6 20,6zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2s2,0.9 2,2S13.1,15 12,15zM14,6h-4V4h4V6z"
+ android:fillColor="?android:attr/textColorHint"/>
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index 1bab189..08eba25 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="@dimen/options_menu_icon_size"
+ android:height="@dimen/options_menu_icon_size"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
diff --git a/res/drawable/ic_star_rating.xml b/res/drawable/ic_star_rating.xml
deleted file mode 100644
index 4e34fa3..0000000
--- a/res/drawable/ic_star_rating.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="12dp"
- android:height="12dp"
- android:viewportWidth="12"
- android:viewportHeight="12">
-
- <path
- android:fillColor="#FB8C00"
- android:fillType="evenOdd"
- android:strokeWidth="1"
- android:pathData="M 9.76511755 11.9348136 L 8.33665684 7.16088817 L 12.080006 4.41656311 L 7.49967039 4.41856896 L 6.03138903 0 L 4.57932894 4.41856896 L -1.34115008e-16 4.41656311 L 3.72612122 7.16088817 L 2.29967385 11.9348136 L 6.03138903 8.82574452 Z" />
-</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index 9e9222f..0c5a125 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -14,8 +14,8 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="@dimen/options_menu_icon_size"
+ android:height="@dimen/options_menu_icon_size"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index de2980f..4bb23b3 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
+ android:width="@dimen/options_menu_icon_size"
+ android:height="@dimen/options_menu_icon_size"
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="?android:attr/textColorPrimary"
android:pathData="M26,28v12c0,1,0.8,2,2,2h12c1,0,2-1,2-2V28c0-1.2-1-2-2-2H28C26.8,26,26,26.8,26,28z M8,42h12c1.2,0,2-1,2-2V28
c0-1.2-0.8-2-2-2H8c-1,0-2,0.8-2,2v12C6,41,7,42,8,42z M6,8v12c0,1.2,1,2,2,2h12c1.2,0,2-0.8,2-2V8c0-1-0.8-2-2-2H8C7,6,6,7,6,8z
M32.6,4.6l-8,8c-0.8,0.8-0.8,2,0,2.8l8,8c0.8,0.8,2,0.8,2.8,0l8-8c0.8-0.8,0.8-2,0-2.8l-8-8C34.6,3.8,33.4,3.8,32.6,4.6z"/>
diff --git a/res/drawable/round_rect_primary.xml b/res/drawable/round_rect_primary.xml
index 2c47e06..16310f8 100644
--- a/res/drawable/round_rect_primary.xml
+++ b/res/drawable/round_rect_primary.xml
@@ -17,5 +17,5 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimary" />
- <corners android:radius="2dp" />
+ <corners android:radius="@dimen/bg_round_rect_radius" />
</shape>
diff --git a/quickstep/res/drawable/task_thumbnail_background.xml b/res/drawable/tooltip_frame.xml
similarity index 82%
rename from quickstep/res/drawable/task_thumbnail_background.xml
rename to res/drawable/tooltip_frame.xml
index 603380e..0319051 100644
--- a/quickstep/res/drawable/task_thumbnail_background.xml
+++ b/res/drawable/tooltip_frame.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+ Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +14,8 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="#FF000000" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorBackground" />
<corners android:radius="2dp" />
-</shape>
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/task_thumbnail_background.xml b/res/drawable/top_round_rect_primary.xml
similarity index 64%
copy from quickstep/res/drawable/task_thumbnail_background.xml
copy to res/drawable/top_round_rect_primary.xml
index 603380e..1caaa02 100644
--- a/quickstep/res/drawable/task_thumbnail_background.xml
+++ b/res/drawable/top_round_rect_primary.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!--
+ Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +14,13 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
- <solid android:color="#FF000000" />
- <corners android:radius="2dp" />
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="?android:attr/colorPrimary" />
+ <corners
+ android:topLeftRadius="@dimen/bg_round_rect_radius"
+ android:topRightRadius="@dimen/bg_round_rect_radius"
+ android:bottomLeftRadius="0dp"
+ android:bottomRightRadius="0dp"
+ />
</shape>
diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml
deleted file mode 100644
index 6a68f84..0000000
--- a/res/layout-land/all_apps_fast_scroller.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<merge
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto">
- <!-- Fast scroller popup -->
- <TextView
- android:id="@+id/fast_scroller_popup"
- style="@style/FastScrollerPopup"
- android:layout_alignParentEnd="true"
- android:layout_alignTop="@+id/apps_list_view"
- android:layout_marginTop="-5dp"
- android:layout_marginEnd="-45dp" />
-
- <com.android.launcher3.allapps.LandscapeFastScroller
- android:id="@+id/fast_scroller"
- android:layout_width="48dp"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="@+id/apps_list_view"
- android:layout_marginEnd="-88dp"
- android:layout_marginTop="14dp"
- launcher:canThumbDetach="true" />
-
-</merge>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
deleted file mode 100644
index 4ea32b4..0000000
--- a/res/layout-land/launcher.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Full screen view projects under the status bar and contains the background -->
-<com.android.launcher3.LauncherRootView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/launcher"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <com.android.launcher3.dragndrop.DragLayer
- android:id="@+id/drag_layer"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:background="?attr/workspaceStatusBarScrim"
- android:importantForAccessibility="no"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- The workspace contains 5 screens of cells -->
- <!-- DO NOT CHANGE THE ID -->
- <com.android.launcher3.Workspace
- android:theme="@style/HomeScreenElementTheme"
- android:id="@+id/workspace"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- launcher:pageIndicator="@id/page_indicator" />
-
- <com.android.launcher3.pageindicators.PageIndicatorLandscape
- android:id="@+id/page_indicator"
- android:theme="@style/HomeScreenElementTheme"
- android:layout_width="@dimen/dynamic_grid_min_page_indicator_size"
- android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
- android:layout_gravity="bottom|left">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/all_apps_handle_landscape"
- android:layout_gravity="center" />
- </com.android.launcher3.pageindicators.PageIndicatorLandscape>
-
- <include layout="@layout/gradient_bg" />
-
- <!-- DO NOT CHANGE THE ID -->
- <include layout="@layout/hotseat"
- android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="right"
- launcher:layout_ignoreInsets="true" />
-
- <include
- android:id="@+id/drop_target_bar"
- layout="@layout/drop_target_bar_vert" />
-
- <include layout="@layout/overview_panel"
- android:id="@+id/overview_panel"
- android:visibility="gone" />
-
- <include layout="@layout/all_apps"
- android:id="@+id/apps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
-
- </com.android.launcher3.dragndrop.DragLayer>
-
-</com.android.launcher3.LauncherRootView>
diff --git a/res/layout-sw720dp/all_apps_fast_scroller.xml b/res/layout-sw720dp/all_apps_fast_scroller.xml
deleted file mode 100644
index 12c15cc..0000000
--- a/res/layout-sw720dp/all_apps_fast_scroller.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<merge
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto">
- <!-- Fast scroller popup -->
- <TextView
- android:id="@+id/fast_scroller_popup"
- style="@style/FastScrollerPopup"
- android:layout_alignParentEnd="true"
- android:layout_alignTop="@+id/apps_list_view"
- android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
-
- <com.android.launcher3.views.RecyclerViewFastScroller
- android:id="@+id/fast_scroller"
- android:layout_width="@dimen/fastscroll_width"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentEnd="true"
- android:layout_alignTop="@+id/apps_list_view"
- android:layout_marginEnd="@dimen/fastscroll_end_margin"
- launcher:canThumbDetach="true" />
-
-</merge>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
deleted file mode 100644
index 660d0ed..0000000
--- a/res/layout-sw720dp/launcher.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Full screen view projects under the status bar and contains the background -->
-<com.android.launcher3.LauncherRootView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:id="@+id/launcher"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true">
-
- <com.android.launcher3.dragndrop.DragLayer
- android:id="@+id/drag_layer"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:importantForAccessibility="no"
- android:background="?attr/workspaceStatusBarScrim"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- The workspace contains 5 screens of cells -->
- <!-- DO NOT CHANGE THE ID -->
- <com.android.launcher3.Workspace
- android:theme="@style/HomeScreenElementTheme"
- android:layout_gravity="center"
- android:id="@+id/workspace"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:pageIndicator="@id/page_indicator">
- </com.android.launcher3.Workspace>
-
- <include layout="@layout/gradient_bg" />
-
- <!-- DO NOT CHANGE THE ID -->
- <include layout="@layout/hotseat"
- android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:layout_ignoreInsets="true" />
-
- <include
- android:id="@+id/drop_target_bar"
- layout="@layout/drop_target_bar_horz" />
-
- <include layout="@layout/overview_panel"
- android:id="@+id/overview_panel"
- android:visibility="gone" />
-
- <!-- Keep these behind the workspace so that they are not visible when
- we go into AllApps -->
- <include layout="@layout/page_indicator"
- android:id="@+id/page_indicator" />
-
- <include layout="@layout/all_apps"
- android:id="@+id/apps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
- </com.android.launcher3.dragndrop.DragLayer>
-
-</com.android.launcher3.LauncherRootView>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 832aaef..2ce6b8c 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -31,44 +31,7 @@
<include layout="@layout/all_apps_fast_scroller" />
- <RelativeLayout
- android:id="@+id/all_apps_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:paddingTop="30dp"
- android:layout_below="@id/search_container_all_apps" >
-
- <com.android.launcher3.allapps.PredictionRowView
- android:id="@+id/header_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
-
- <com.android.launcher3.views.SlidingTabStrip
- android:id="@+id/tabs"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_header_tab_height"
- android:layout_below="@id/header_content"
- android:orientation="horizontal">
- <Button
- android:id="@+id/tab_personal"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:text="@string/all_apps_personal_tab"
- android:textColor="@color/all_apps_tab_text"
- android:background="?android:attr/selectableItemBackground"/>
- <Button
- android:id="@+id/tab_work"
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:text="@string/all_apps_work_tab"
- android:textColor="@color/all_apps_tab_text"
- android:background="?android:attr/selectableItemBackground"/>
- </com.android.launcher3.views.SlidingTabStrip>
-
- </RelativeLayout>
+ <include layout="@layout/all_apps_floating_header" />
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
platform bug, which prevents using custom attributes in <include> tag -->
diff --git a/res/layout/all_apps_discovery_item.xml b/res/layout/all_apps_discovery_item.xml
deleted file mode 100644
index 728283f..0000000
--- a/res/layout/all_apps_discovery_item.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.discovery.AppDiscoveryItemView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clickable="true"
- android:background="?android:selectableItemBackground">
-
- <ImageView
- android:id="@+id/image"
- android:layout_width="56dp"
- android:layout_height="56dp"
- android:padding="8dp"
- android:scaleType="fitCenter"
- android:focusable="false"
- android:importantForAccessibility="no"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@id/image">
-
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textColor="?android:textColorSecondary"
- android:textSize="15sp"/>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <TextView
- android:id="@+id/rating"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?android:textColorSecondary"
- android:textSize="14sp"
- android:layout_gravity="center_vertical"
- android:includeFontPadding="false"/>
-
- <com.android.launcher3.discovery.RatingView
- android:id="@+id/rating_view"
- android:layout_width="70dp"
- android:layout_height="16dp"
- android:layout_marginLeft="5dp"
- android:layout_marginRight="5dp"
- android:layout_gravity="center_vertical"/>
-
- <TextView
- android:id="@+id/review_count"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dp"
- android:textColor="?android:textColorHint"
- android:textSize="14sp"
- android:layout_gravity="center_vertical"/>
-
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
-
- <TextView
- android:id="@+id/price"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textColor="?android:textColorHint"
- android:textSize="14sp"
- android:layout_marginRight="12dp"
- android:textAllCaps="true"/>
- </LinearLayout>
- </LinearLayout>
-
- <ImageView
- android:importantForAccessibility="no"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:paddingLeft="@dimen/dynamic_grid_edge_margin"
- android:paddingRight="@dimen/dynamic_grid_edge_margin"
- android:src="@drawable/all_apps_divider"
- android:scaleType="fitXY"
- android:focusable="false" />
-</com.android.launcher3.discovery.AppDiscoveryItemView>
\ No newline at end of file
diff --git a/res/layout/all_apps_discovery_loading_divider.xml b/res/layout/all_apps_discovery_loading_divider.xml
deleted file mode 100644
index 005847c..0000000
--- a/res/layout/all_apps_discovery_loading_divider.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="6dp"
- android:paddingLeft="@dimen/dynamic_grid_edge_margin"
- android:paddingRight="@dimen/dynamic_grid_edge_margin">
-
- <ProgressBar
- android:id="@+id/loadingProgressBar"
- style="@android:style/Widget.Material.ProgressBar.Horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="6dp"
- android:maxHeight="6dp"
- android:indeterminate="true"
- android:layout_gravity="center"/>
-
- <View
- android:id="@+id/loadedDivider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@drawable/all_apps_divider"
- android:layout_gravity="bottom"
- android:visibility="invisible"/>
-
-</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
index 12c15cc..5537bc6 100644
--- a/res/layout/all_apps_fast_scroller.xml
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -21,7 +21,7 @@
android:id="@+id/fast_scroller_popup"
style="@style/FastScrollerPopup"
android:layout_alignParentEnd="true"
- android:layout_alignTop="@+id/apps_list_view"
+ android:layout_below="@+id/search_container_all_apps"
android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
<com.android.launcher3.views.RecyclerViewFastScroller
@@ -30,7 +30,7 @@
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
- android:layout_alignTop="@+id/apps_list_view"
+ android:layout_below="@+id/search_container_all_apps"
android:layout_marginEnd="@dimen/fastscroll_end_margin"
launcher:canThumbDetach="true" />
diff --git a/res/layout/all_apps_floating_header.xml b/res/layout/all_apps_floating_header.xml
new file mode 100644
index 0000000..c4240f8
--- /dev/null
+++ b/res/layout/all_apps_floating_header.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.allapps.FloatingHeaderView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/all_apps_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/search_container_all_apps"
+ android:clipToPadding="false"
+ android:paddingTop="@dimen/all_apps_header_top_padding"
+ android:orientation="vertical" >
+
+ <com.android.launcher3.allapps.PersonalWorkSlidingTabStrip
+ android:id="@+id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/all_apps_header_tab_height"
+ android:layout_marginLeft="@dimen/all_apps_tabs_side_padding"
+ android:layout_marginRight="@dimen/all_apps_tabs_side_padding"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/tab_personal"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/all_apps_personal_tab"
+ android:textAllCaps="true"
+ android:textColor="@color/all_apps_tab_text"
+ android:textSize="14sp" />
+
+ <Button
+ android:id="@+id/tab_work"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/all_apps_work_tab"
+ android:textAllCaps="true"
+ android:textColor="@color/all_apps_tab_text"
+ android:textSize="14sp" />
+ </com.android.launcher3.allapps.PersonalWorkSlidingTabStrip>
+</com.android.launcher3.allapps.FloatingHeaderView>
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index fa1d591..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="30dp">
+ android:paddingTop="@dimen/all_apps_header_top_padding"
+ launcher:pageIndicator="@+id/tabs" >
<include layout="@layout/all_apps_rv_layout" />
<include layout="@layout/all_apps_rv_layout" />
-</com.android.launcher3.allapps.InterceptingViewPager>
\ No newline at end of file
+</com.android.launcher3.allapps.AllAppsPagedView>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar.xml b/res/layout/drop_target_bar.xml
new file mode 100644
index 0000000..d376bcf
--- /dev/null
+++ b/res/layout/drop_target_bar.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.DropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dynamic_grid_drop_target_size"
+ android:layout_gravity="center_horizontal|top"
+ android:focusable="false"
+ android:alpha="0"
+ android:theme="@style/HomeScreenElementTheme"
+ android:visibility="invisible">
+
+ <!-- Delete target -->
+ <com.android.launcher3.DeleteDropTarget
+ android:id="@+id/delete_target_text"
+ style="@style/DropTargetButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/remove_drop_target_label" />
+
+ <!-- App Info -->
+ <com.android.launcher3.InfoDropTarget
+ android:id="@+id/info_target_text"
+ style="@style/DropTargetButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/app_info_drop_target_label" />
+
+ <!-- Uninstall target -->
+ <com.android.launcher3.UninstallDropTarget
+ android:id="@+id/uninstall_target_text"
+ style="@style/DropTargetButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/uninstall_drop_target_label" />
+
+</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_horz.xml b/res/layout/drop_target_bar_horz.xml
deleted file mode 100644
index ed18192..0000000
--- a/res/layout/drop_target_bar_horz.xml
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.DropTargetBar
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:theme="@style/HomeScreenElementTheme"
- android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_drop_target_size"
- android:visibility="invisible"
- android:layout_gravity="center_horizontal|top"
- android:focusable="false">
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
-
- <!-- Delete target -->
-
- <com.android.launcher3.DeleteDropTarget
- launcher:hideParentOnDisable="true"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:id="@+id/delete_target_text"
- style="@style/DropTargetButton"
- android:text="@string/remove_drop_target_label" />
- </FrameLayout>
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
-
- <!-- App Info -->
-
- <com.android.launcher3.InfoDropTarget
- launcher:hideParentOnDisable="true"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:id="@+id/info_target_text"
- style="@style/DropTargetButton"
- android:text="@string/app_info_drop_target_label" />
- </FrameLayout>
-
- <FrameLayout
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1" >
-
- <!-- Uninstall target -->
-
- <com.android.launcher3.UninstallDropTarget
- launcher:hideParentOnDisable="true"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:id="@+id/uninstall_target_text"
- style="@style/DropTargetButton"
- android:text="@string/uninstall_drop_target_label" />
- </FrameLayout>
-
-</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_vert.xml b/res/layout/drop_target_bar_vert.xml
deleted file mode 100644
index 2394d0d..0000000
--- a/res/layout/drop_target_bar_vert.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.DropTargetBar
- android:theme="@style/HomeScreenElementTheme"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/dynamic_grid_drop_target_size"
- android:orientation="vertical"
- android:layout_height="match_parent"
- android:layout_gravity="left"
- android:visibility="invisible"
- android:focusable="false"
- android:paddingTop="@dimen/vert_drop_target_vertical_gap" >
-
- <!-- Delete target -->
- <com.android.launcher3.DeleteDropTarget
- android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_drop_target_size"
- android:gravity="center"
- android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
- android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
- android:id="@+id/delete_target_text" />
-
- <!-- Uninstall target -->
- <com.android.launcher3.UninstallDropTarget
- android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_drop_target_size"
- android:gravity="center"
- android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
- android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
- android:id="@+id/uninstall_target_text"
- android:layout_marginTop="@dimen/vert_drop_target_vertical_gap"/>
-
- <Space
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1" />
-
- <!-- App Info -->
- <com.android.launcher3.InfoDropTarget
- android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_drop_target_size"
- android:gravity="center"
- android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
- android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
- android:id="@+id/info_target_text"
- android:layout_marginBottom="64dp"/>
-
-</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_tool_tip.xml b/res/layout/drop_target_tool_tip.xml
new file mode 100644
index 0000000..a3efec4
--- /dev/null
+++ b/res/layout/drop_target_tool_tip.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/tooltip_frame"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:maxWidth="256dp"
+ android:paddingBottom="6.5dp"
+ android:paddingEnd="16dp"
+ android:paddingStart="16dp"
+ android:paddingTop="6.5dp"
+ android:textSize="14sp"
+ android:fontFamily="sans-serif"
+ android:textColor="?android:attr/colorForeground" />
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 582a83f..73c0e52 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -22,5 +22,6 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
- launcher:containerType="hotseat" />
+ launcher:containerType="hotseat"
+ android:importantForAccessibility="no" />
</com.android.launcher3.Hotseat>
diff --git a/res/layout-port/launcher.xml b/res/layout/launcher.xml
similarity index 65%
rename from res/layout-port/launcher.xml
rename to res/layout/launcher.xml
index f58a87e..314359b 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout/launcher.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,61 +12,65 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<!-- Full screen view projects under the status bar and contains the background -->
<com.android.launcher3.LauncherRootView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
-
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:background="?attr/workspaceStatusBarScrim"
android:fitsSystemWindows="true">
<com.android.launcher3.dragndrop.DragLayer
android:id="@+id/drag_layer"
- android:clipChildren="false"
- android:importantForAccessibility="no"
- android:clipToPadding="false"
- android:background="?attr/workspaceStatusBarScrim"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:importantForAccessibility="no">
<!-- The workspace contains 5 screens of cells -->
<!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.Workspace
- android:theme="@style/HomeScreenElementTheme"
android:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
- launcher:pageIndicator="@+id/page_indicator">
- </com.android.launcher3.Workspace>
+ android:theme="@style/HomeScreenElementTheme"
+ launcher:pageIndicator="@+id/page_indicator" />
- <include layout="@layout/gradient_bg" />
-
- <!-- DO NOT CHANGE THE ID -->
- <include layout="@layout/hotseat"
- android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:layout_ignoreInsets="true" />
-
- <include layout="@layout/overview_panel"
+ <include
android:id="@+id/overview_panel"
+ layout="@layout/overview_panel"
android:visibility="gone" />
+ <com.android.launcher3.views.AllAppsScrim
+ android:id="@+id/all_apps_scrim"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <!-- DO NOT CHANGE THE ID -->
+ <include
+ android:id="@+id/hotseat"
+ layout="@layout/hotseat"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
<!-- Keep these behind the workspace so that they are not visible when
- we go into AllApps -->
- <include layout="@layout/page_indicator"
- android:id="@+id/page_indicator" />
+ we go into AllApps -->
+ <com.android.launcher3.pageindicators.WorkspacePageIndicator
+ android:id="@+id/page_indicator"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
+ android:theme="@style/HomeScreenElementTheme" />
<include
android:id="@+id/drop_target_bar"
- layout="@layout/drop_target_bar_horz" />
+ layout="@layout/drop_target_bar" />
- <include layout="@layout/all_apps"
+ <include
android:id="@+id/apps_view"
+ layout="@layout/all_apps"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
deleted file mode 100644
index 2df511b..0000000
--- a/res/layout/page_indicator.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<com.android.launcher3.pageindicators.PageIndicatorLine
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/HomeScreenElementTheme"
- android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_min_page_indicator_size">
- <ImageView
- android:id="@+id/all_apps_handle"
- android:layout_width="48dp"
- android:layout_height="@dimen/dynamic_grid_min_page_indicator_size"
- android:src="@drawable/all_apps_handle_portrait"
- android:layout_gravity="top|center"
- android:scaleType="centerInside"/>
-</com.android.launcher3.pageindicators.PageIndicatorLine>
diff --git a/res/layout/system_shortcut_icon_only.xml b/res/layout/system_shortcut_icon_only.xml
index c59cb53..b8b5b8c 100644
--- a/res/layout/system_shortcut_icon_only.xml
+++ b/res/layout/system_shortcut_icon_only.xml
@@ -20,5 +20,6 @@
android:layout_height="@dimen/system_shortcut_header_icon_touch_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:tint="?android:attr/textColorHint"
+ android:tintMode="src_in"
android:padding="@dimen/system_shortcut_header_icon_padding"
android:theme="@style/PopupItem" />
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
deleted file mode 100644
index afa19b8..0000000
--- a/res/layout/user_folder.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.folder.Folder xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/round_rect_primary"
- android:elevation="5dp"
- android:orientation="vertical" >
-
- <com.android.launcher3.folder.FolderPagedView
- android:id="@+id/folder_content"
- android:clipToPadding="false"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:paddingTop="16dp"
- launcher:pageIndicator="@+id/folder_page_indicator" />
-
- <LinearLayout
- android:id="@+id/folder_footer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:orientation="horizontal"
- android:paddingLeft="12dp"
- android:paddingRight="12dp" >
-
- <com.android.launcher3.ExtendedEditText
- android:id="@+id/folder_name"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:background="@android:color/transparent"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:hint="@string/folder_hint_text"
- android:imeOptions="flagNoExtractUi"
- android:paddingBottom="@dimen/folder_label_padding_bottom"
- android:paddingTop="@dimen/folder_label_padding_top"
- android:singleLine="true"
- android:textColor="?android:attr/textColorTertiary"
- android:includeFontPadding="false"
- android:textColorHighlight="?android:attr/colorControlHighlight"
- android:textColorHint="?android:attr/textColorHint"
- android:textSize="@dimen/folder_label_text_size" />
-
- <com.android.launcher3.pageindicators.PageIndicatorDots
- android:id="@+id/folder_page_indicator"
- android:layout_gravity="center_vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:elevation="1dp"
- />
-
- </LinearLayout>
-
-</com.android.launcher3.folder.Folder>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index e8c6961..6bf9048 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="28dp"
- android:background="?android:attr/colorPrimary"
+ android:background="@drawable/top_round_rect_primary"
android:elevation="@dimen/deep_shortcuts_elevation"
android:layout_gravity="bottom"
android:theme="?attr/widgetsTheme">
diff --git a/res/layout/gradient_bg.xml b/res/layout/widgets_bottom_sheet_scrim.xml
similarity index 100%
rename from res/layout/gradient_bg.xml
rename to res/layout/widgets_bottom_sheet_scrim.xml
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 1535299..20eca9f 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -25,7 +25,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <FrameLayout
+ <com.android.launcher3.views.TopRoundedCornerView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -60,5 +60,5 @@
android:layout_gravity="bottom"
android:background="?attr/allAppsNavBarScrimColor"
android:focusable="false" />
- </FrameLayout>
+ </com.android.launcher3.views.TopRoundedCornerView>
</com.android.launcher3.widget.WidgetsFullSheet>
\ No newline at end of file
diff --git a/res/layout/work_tab_bottom_user_education_view.xml b/res/layout/work_tab_bottom_user_education_view.xml
new file mode 100644
index 0000000..dc6854e
--- /dev/null
+++ b/res/layout/work_tab_bottom_user_education_view.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.views.BottomUserEducationView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:background="?android:attr/colorAccent"
+ android:elevation="2dp"
+ android:orientation="horizontal">
+
+ <ImageView
+ android:layout_width="134dp"
+ android:layout_height="134dp"
+ android:layout_marginTop="28dp"
+ android:layout_marginLeft="20dp"
+ android:src="@drawable/work_tab_user_education"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="24dp"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/close_bottom_user_tip"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginTop="12dp"
+ android:layout_marginEnd="12dp"
+ android:layout_gravity="right"
+ android:src="@drawable/ic_close"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginEnd="24dp"
+ android:fontFamily="roboto-medium"
+ android:text="@string/bottom_work_tab_user_education_title"
+ android:textColor="@android:color/white"
+ android:textSize="20sp"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="24dp"
+ android:text="@string/bottom_work_tab_user_education_body"
+ android:textColor="@android:color/white"
+ android:textSize="14sp"/>
+
+ </LinearLayout>
+
+</com.android.launcher3.views.BottomUserEducationView>
\ No newline at end of file
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
new file mode 100644
index 0000000..21ff55e
--- /dev/null
+++ b/res/layout/work_tab_footer.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.views.WorkFooterContainer
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="@dimen/all_apps_work_profile_tab_footer_bottom_padding"
+ android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
+ android:paddingRight="@dimen/dynamic_grid_cell_padding_x"
+ android:paddingTop="@dimen/all_apps_work_profile_tab_footer_top_padding">
+
+ <ImageView
+ android:id="@+id/work_footer_divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:focusable="false"
+ android:importantForAccessibility="no"
+ android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
+ android:paddingTop="@dimen/all_apps_divider_margin_vertical"
+ android:scaleType="fitXY"
+ android:src="@drawable/all_apps_divider"/>
+
+ <com.android.launcher3.allapps.WorkModeSwitch
+ android:id="@+id/work_mode_toggle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/work_footer_divider"/>
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignBaseline="@id/work_mode_toggle"
+ android:layout_alignParentStart="true"
+ android:ellipsize="end"
+ android:lines="1"
+ android:text="@string/work_profile_toggle_label"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="16sp"/>
+
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_below="@android:id/title"
+ android:layout_marginTop="8dp"
+ android:src="@drawable/ic_corp"/>
+
+ <TextView
+ android:id="@+id/managed_by_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_marginTop="8dp"
+ android:layout_toEndOf="@android:id/icon"
+ android:ellipsize="end"
+ android:gravity="center_vertical"
+ android:lines="1"
+ android:minHeight="24dp"
+ android:paddingStart="12dp"
+ android:textColor="?android:attr/textColorHint"
+ android:textSize="13sp"/>
+
+</com.android.launcher3.views.WorkFooterContainer>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 5a776a6..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> kortpaaie en <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> kennisgewings vir <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Maak toe"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Kennisgewing is toegemaak"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoonlik"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Kry werkprogramme hier"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Elke werkprogram het \'n oranje kenteken en word deur jou organisasie veilig gehou. Skuif programme na jou Tuisskerm vir makliker toegang."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Bestuur deur jou organisasie"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Kennisgewings en programme is af"</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 5225dba..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> አቋራጮች እና <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ማሳወቂያዎች ለ<xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"አሰናብት"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ማሳወቂያ ተሰናብቷል"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"የግል"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ሥራ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"የሥራ መገለጫ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"የስራ መተግበሪያዎችን እዚህ ያግኙ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"እያንዳንዱ የሥራ መተግበሪያ ብርቱካናማ ባጅ አለው እና በእርስዎ ድርጅት በኩል ደህንነቱ ተጠብቋል። ለቀለለ መዳረሻ መተግበሪያዎችን ወደ የእርስዎ መነሻ ማያ ገጽ ያንቀሳቅሱ።"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"በእርስዎ ድርጅት የሚተዳደር"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ማሳወቂያዎች እና መተግበሪያዎች ጠፍተዋል"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 5f2d58d..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>
@@ -129,4 +137,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"هناك <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> اختصار و<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> إشعار عن <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"تجاهل"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"تم تجاهل الإشعار"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصية"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"للعمل"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"الملف الشخصي للعمل"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"البحث عن تطبيقات العمل هنا"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"يحتوي كل تطبيق للعمل على شارة برتقالية اللون ويظل تحت حماية مؤسستك. يمكنك نقل التطبيقات إلى شاشتك الرئيسية لتسهيل الوصول."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ملف شخصي للعمل تديره مؤسستك"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"الإشعارات والتطبيقات متوقفة."</string>
</resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index dddb6fa..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> üçün <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> qısayol və <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> bildiriş"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Rədd edin"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Bildiriş rədd edildi"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Şəxsi"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Burada iş tətbiqləri axtarın"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Hər bir iş tətbiqində təşkilat tərəfindən qorunduğunu göstərən narıncı nişan var. Tətbiqləri daha asan giriş üçün Əsas Səhifə Ekranına köçürün."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Təşkilatınız tərəfindən idarə olunur"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Bildiriş və tətbiqlər deaktivdir"</string>
</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 4fa86d2..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>
@@ -129,4 +134,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Prečice (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) i obaveštenja (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) za <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obaveštenje je odbačeno"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil za Work"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovde"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Svaka poslovna aplikacija ima narandžastu značku i štiti je vaša organizacija. Premestite aplikacije na početni ekran da biste im lakše pristupali."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Ovim upravlja organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Obaveštenja i aplikacije su isključeni"</string>
</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 1d7797d..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Ярлыкі (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) і апавяшчэнні (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) для <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Адхіліць"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Апавяшчэнне адхілена"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Асабістыя"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Праца"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Працоўны профіль"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Знайдзіце працоўныя праграмы тут"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Кожная працоўная праграма мае аранжавы значок і знаходзіцца пад аховай вашай арганізацыі. Для больш простага доступу перамясціце праграмы на свой Галоўны экран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Пад кіраваннем вашай арганізацыі"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Апавяшчэнні і праграмы выключаны"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index b429492..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> преки пътища и <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> известия за <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Отхвърляне"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Известието е отхвърлено"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лични"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Служебни"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Служебен потребителски профил"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Тук можете да намерите служебните приложения"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Всяко служебно приложение има оранжева значка и организацията ви се грижи за сигурността му. За по-лесен достъп преместете приложенията на началния си екран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Управлява се от организацията ви"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Известията и приложенията са изключени"</string>
</resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 897a8db..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> এর জন্য <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>টি শর্টকাট এবং <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>টি বিজ্ঞপ্তি"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"খারিজ করুন"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"বিজ্ঞপ্তি খারিজ করা হয়েছে"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ব্যক্তিগত"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"অফিস"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"অফিসের প্রোফাইল"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"এখানে কাজের অ্যাপ্সগুলি খুঁজুন"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"প্রতিটি কাজের অ্যাপে একটি করে কমলা ব্যাজ রয়েছে এবং অ্যাপগুলি আপনার প্রতিষ্ঠানের দ্বারা সুরক্ষিত। সহজে অ্যাক্সেস করার জন্য অ্যাপগুলি হোম স্ক্রিনে রাখুন।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"আপনার প্রতিষ্ঠানের দ্বারা পরিচালিত"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"বিজ্ঞপ্তি এবং অ্যাপ বন্ধ আছে"</string>
</resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index b062b88..84b2b78 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -54,12 +54,17 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Dopušta aplikaciji čitanje postavki i prečica na početnom ekranu."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"zapisuj postavke na početnom ekranu i prečice"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Dopušta aplikaciji promjenu postavki i prečica na početnom ekranu."</string>
- <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nema dozvolu da uspostavlja telefonske pozive"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nema odobrenje da uspostavlja telefonske pozive"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju dodatka"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može se deinstalirati."</string>
<string name="folder_hint_text" msgid="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>
@@ -129,4 +134,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Za aplikaciju <xliff:g id="APP_NAME">%3$s</xliff:g> broj prečica je <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>, a broj obavještenja je <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obavještenje je odbačeno"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Lične"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Poslovne"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pronađite poslovne aplikacije ovdje"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Svaka poslovna aplikacija ima narandžastu značku i osigurava je vaša organizacija. Premjestite aplikacije na Početni ekran, radi lakšeg pristupa."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Upravlja vaša organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikacije i aplikacije su isključene"</string>
</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 008531a..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> dreceres i <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificacions per a <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignora"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"S\'ha ignorat la notificació"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Feina"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil professional"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Cerca aplicacions per a la feina aquí"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Totes les aplicacions per a la feina tenen una insígnia taronja que indica que estan protegides per la teva organització. Mou les aplicacions a la pantalla d\'inici per poder-hi accedir més fàcilment."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gestionat per la teva organització"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Les notificacions i les aplicacions estan desactivades"</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 7cd71c2..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Zkratky (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) a oznámení (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) aplikace <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Zavřít"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Oznámení bylo zavřeno"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobní"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovní"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovní profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Zde naleznete pracovní aplikace"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Každý pracovní profil má oranžový odznak a je zabezpečen vaší organizací. Aplikace si můžete pro jednoduchost přesunout na plochu."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Spravováno vaší organizací"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Oznámení a aplikace jsou vypnuty"</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 9a91bc4..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> genveje og <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> underretninger for <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Afvis"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Underretningen blev afvist"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlige"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Arbejde"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbejdsprofil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find arbejdsapps her"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Alle arbejdsapps har et orange badge og beskyttes af din organisation. Flyt apps til din startskærm, så du nemmere kan få adgang til dem."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administreret af din organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Underretninger og apps er slået fra"</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 6bc0875..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> Verknüpfungen und <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> Benachrichtigungen für <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Schließen"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Benachrichtigung geschlossen"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Geschäftlich"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Arbeitsprofil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hier findest du Apps für die Arbeit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Jede App für die Arbeit hat ein orangefarbenes Logo. Deine Organisation kümmert sich um den entsprechenden Schutz. Damit du leichter auf Apps zugreifen kannst, verschiebe sie auf deinen Startbildschirm."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Wird von deiner Organisation verwaltet"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Benachrichtigungen und Apps sind deaktiviert"</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index cdb255d..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> συντομεύσεις και <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ειδοποιήσεις για την εφαρμογή <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Παράβλεψη"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Η ειδοποίηση παραβλέφθηκε"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Προσωπικές"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Εργασίας"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Προφίλ εργασίας"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Βρείτε όλες τις εφαρμογές εργασίας εδώ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Κάθε εφαρμογή εργασίας φέρει ένα πορτοκαλί σήμα και διατηρείται ασφαλής από τον οργανισμό σας. Μετακινήστε τις εφαρμογές εργασίας στην Αρχική οθόνη, για να έχετε πιο εύκολη πρόσβαση."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Διαχειριζόμενο από τον οργανισμό σας"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Οι ειδοποιήσεις και οι εφαρμογές είναι απενεργοποιημένες"</string>
</resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index afd6757..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shortcuts and <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifications for <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Dismiss"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index afd6757..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shortcuts and <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifications for <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Dismiss"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index afd6757..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shortcuts and <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifications for <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Dismiss"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification dismissed"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Work"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work profile"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Find work apps here"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Each work app has an orange badge and is kept secure by your organisation. Move apps to your Home screen for easier access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Managed by your organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifications and apps are off"</string>
</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 05f1e06..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> accesos directos y <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificaciones de <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Descartar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Se descartó la notificación"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personales"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Laborales"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Apps de trabajo"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Cada app de trabajo tiene una insignia naranja y está protegida por tu organización. Mueve las apps a la pantalla principal para acceder a ellas con mayor facilidad."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administrado por tu organización"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las apps están desactivadas"</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index e7e1cf8..39da94c 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -46,7 +46,7 @@
<string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
<string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
<string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
- <string name="app_info_drop_target_label" msgid="692894985365717661">"Datos de aplicación"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Información de la aplicación"</string>
<string name="install_drop_target_label" msgid="2539096853673231757">"Instalar"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
@@ -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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> accesos directos y <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificaciones de <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificación ignorada"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabajo"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabajo"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Aplicaciones de trabajo"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Cada aplicación de trabajo tiene una insignia naranja y está protegida por tu organización. Mueve las aplicaciones a la pantalla de inicio para acceder a ellas más fácilmente."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administrada por tu organización"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Las notificaciones y las aplicaciones están desactivadas"</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index ccc85a3..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> otseteed ja <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> märguannet rakendusele <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Loobu"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Märguandest loobuti"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Isiklik"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Töö"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Tööprofiil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Töörakendused leiate siit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Igal töörakendusel on oranž märk ja teie organisatsioon tagab selle turvalisuse. Teisaldage rakendused avaekraanile, et neile oleks lihtsam juurde pääseda."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Haldab teie organisatsioon"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Märguanded ja rakendused on välja lülitatud"</string>
</resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 68478de..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> aplikazioaren <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> lasterbide eta <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> jakinarazpen"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Baztertu"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Baztertu egin da jakinarazpena"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pertsonalak"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Lanekoak"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Laneko profila"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hemen dituzu laneko aplikazioak"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Laneko aplikazio bakoitzak bereizgarri laranja bat dauka eta erakundeak babesten du. Aplikazioak errazago atzitzeko, eraman itzazu hasierako pantailara."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Erakundeak kudeatzen du"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Jakinarazpenak eta aplikazioak desaktibatuta daude"</string>
</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 8051b85..128af90 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -28,9 +28,9 @@
<string name="safemode_widget_error" msgid="4863470563535682004">"ابزارکها در حالت ایمن غیرفعال هستند"</string>
<string name="shortcut_not_available" msgid="2536503539825726397">"میانبر دردسترس نیست"</string>
<string name="home_screen" msgid="806512411299847073">"صفحه اصلی"</string>
- <string name="custom_actions" msgid="3747508247759093328">"عملکردهای سفارشی"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"کنشهای سفارشی"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"برای انتخاب ابزارک لمس کنید و نگه دارید."</string>
- <string name="long_accessible_way_to_add" msgid="4289502106628154155">"برای انتخاب یک ابزارک، دو ضربه سریع بزنید و نگهدارید یا از اقدامات سفارشی استفاده کنید."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"برای انتخاب یک ابزارک، دو ضربه سریع بزنید و نگهدارید یا از کنشهای سفارشی استفاده کنید."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d عرض در %2$d طول"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"برای قرار دادن بهصورت دستی لمس کنید و بکشید"</string>
@@ -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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> میانبر و <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> اعلان برای <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"رد کردن"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"اعلان رد شد"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"شخصی"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"محل کار"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"نمایه کاری"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"اینجا برنامههای کاری را پیدا کنید"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"همه برنامههای کاری نشان نارنجیرنگی دارند و توسط سازمان شما امن نگه داشته میشود. برنامههای کاری را برای دسترسی آسانتر به صفحه اصلی انتقال دهید."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"توسط سازمانتان مدیریت میشود"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"اعلانها و برنامهها خاموش هستند"</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 5e05bbe..f5b0817 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -33,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Valitse widget tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla kohdetta pitkään."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
- <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sijoita manuaalisesti koskettamalla pitkään."</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sijoita manuaalisesti koskettamalla pitkään"</string>
<string name="place_automatically" msgid="8064208734425456485">"Lisää automaattisesti"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Hae sovelluksia"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Ladataan sovelluksia…"</string>
@@ -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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pikakuvaketta ja <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ilmoitusta"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Hylkää"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Ilmoitus hylätty"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Henkilökohtaiset"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Työsovellukset"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Työprofiili"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Etsi työsovelluksia tästä"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Kaikissa työsovelluksissa on oranssi merkki ja ne ovat organisaatiosi suojaamia. Voit siirtää työsovelluksia aloitusnäytölle käytön helpottamiseksi."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Organisaatiosi hallinnoima"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Ilmoitukset ja sovellukset ovat poissa käytöstä"</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index da403a0..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> raccourcis et <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifications pour <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorer"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification ignorée"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnel"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Travail"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Trouvez ici des applications professionnelles"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Chaque application professionnelle comporte un badge orange, ce qui signifie qu\'elle est sécurisée par votre organisation. Vous pouvez déplacer vos applications vers l\'écran d\'accueil afin d\'y accéder plus facilement."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index bc98cd2..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> raccourcis et <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifications pour <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorer"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notification ignorée"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personnelles"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Professionnelles"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil professionnel"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Retrouvez ici vos applications professionnelles"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Les applications professionnelles sont accompagnées d\'un badge orange et sont sécurisées par votre organisation. Vous pouvez les déplacer vers votre écran d\'accueil pour y accéder plus facilement."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Géré par votre organisation"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Les notifications et les applications sont désactivées"</string>
</resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 54f9604..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atallos e <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificacións para a aplicación <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Ignorouse a notificación"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persoal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Traballo"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de traballo"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Buscar aplicacións do traballo aquí"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"As aplicacións do traballo teñen unha insignia laranxa e están protexidas pola túa organización. Traslada as aplicacións á pantalla de inicio para acceder a elas de forma máis fácil."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Perfil xestionado pola túa organización"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"As notificacións e as aplicacións están desactivadas"</string>
</resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 9734781..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> માટે <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> શૉર્ટકટ અને <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> સૂચનાઓ"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"છોડી દો"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"સૂચના છોડી દીધી"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"મનગમતી ઍપ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"કાર્યાલયની પ્રોફાઇલ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"કાર્ય ઍપને અહીંથી મેળવો"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"દરેક કાર્ય ઍપ પાસે એક નારંગી બૅજ હોય છે અને તમારી સંસ્થા દ્વારા તેને સુરક્ષિત રાખવામાં આવે છે. વધુ સરળ ઍક્સેસ માટે ઍપને તમારી હોમ સ્ક્રીન પર ખસેડો."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"તમારી સંસ્થા દ્વારા મેનેજ કરેલ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"નોટિફિકેશન અને ઍપ બંધ છે"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 2387404..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>
@@ -72,7 +76,7 @@
<string name="widget_button_text" msgid="2880537293434387943">"शॉर्टकट"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="settings_button_text" msgid="8873672322605444408">"होम पेज की सेटिंग"</string>
- <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके व्यवस्थापक द्वारा अक्षम"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके एडमिन ने बंद किया हुआ है"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"खास जानकारी"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"होमस्क्रीन घुमाने की अनुमति दें"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"फ़ोन घुुमाए जाने पर"</string>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> के लिए <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट और <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचनाएं हैं"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"खारिज करें"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना को खारिज किया गया"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"काम से जुड़े ऐप"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफ़ाइल"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"काम से जुड़े सभी ऐप्लिकेशन यहां पाएं"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"काम से जुड़े हर ऐप्लिकेशन पर एक नारंगी रंग का बैज (निशान) होता है जिसकी सुरक्षा आपका संगठन करता है. आसानी से इस्तेमाल करने के लिए ऐप्लिकेशन को अपनी होम स्क्रीन पर ले जाएं."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"आपका संगठन प्रबंधित कर रहा है"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"सूचनाएं और ऐप्लिकेशन बंद हैं"</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index f533015..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>
@@ -129,4 +134,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Za aplikaciju <xliff:g id="APP_NAME">%3$s</xliff:g> ima prečaca (ukupno <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) i obavijesti (ukupno <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>)"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odbaci"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obavijest je odbačena"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobno"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Posao"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Radni profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ovdje možete pronaći radne aplikacije"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Svaka radna aplikacija ima narančastu značku i štiti je vaša organizacija. Premjestite aplikacije na početni zaslon radi lakšeg pristupa."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Pod upravljanjem vaše organizacije"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Obavijesti i aplikacije isključeni su"</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index e38da35..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> parancsikon és <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> értesítés a következő alkalmazásnál: <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Elvetés"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Értesítés elvetve"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Személyes"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Munkahelyi"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Munkaprofil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Itt kereshet munkahelyi alkalmazásokat"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Az egyes munkahelyi alkalmazásokon narancs jelvény található, és biztonságukról az Ön szervezete gondoskodik. A könnyebb hozzáférés érdekében helyezze át az alkalmazásokat a kezdőképernyőre."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Az Ön szervezete kezeli"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Az értesítések és az alkalmazások ki vannak kapcsolva"</string>
</resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 4901dc5..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> դյուրացում և <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ծանուցում <xliff:g id="APP_NAME">%3$s</xliff:g>-ի համար"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Անտեսել"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Ծանուցումը մերժված է"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Անձնական"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Աշխատանքային"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Աշխատանքային պրոֆիլ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Գտեք աշխատանքային հավելվածներ այստեղ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Աշխատանքային հավելվածները նշված են նարնջագույն նշանով, նման հավելվածների անվտանգությունը ապահովում է ձեր կազմակերպությունը։ Հարմարության համար աշխատանքային հավելվածները կարող եք տեղափոխել հիմնական էկրան։"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Կառավարվում է ձեր կազմակերպության կողմից"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Ծանուցումներն ու հավելվածներն անջատված են"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 5112223..d115c73 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -60,9 +60,13 @@
<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>
- <string name="default_scroll_format" msgid="7475544710230993317">"Laman %1$d dari %2$d"</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">"Laman layar utama baru"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Halaman layar utama baru"</string>
<string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="4625795376335528256">"Ketuk untuk menutup folder"</string>
<string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketuk untuk menyimpan ganti nama"</string>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan dan <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifikasi untuk <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Tutup"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notifikasi ditutup"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pribadi"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Kantor"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Temukan aplikasi kerja di sini"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Setiap aplikasi kerja memiliki badge oranye dan dibuat tetap aman oleh organisasi. Pindahkan aplikasi ke Layar utama untuk memudahkan akses."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Dikelola oleh organisasi"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notifikasi dan aplikasi nonaktif"</string>
</resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index e1b04e2..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> flýtileiðir og <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> tilkynningar fyrir <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Hunsa"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Tilkynningu lokað"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Persónulegt"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Vinna"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Vinnusnið"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Hér finnurðu vinnuforrit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Öll vinnuforrit eru með appelsínugulu merki og fyrirtækið þitt tryggir öryggi þeirra. Færðu forrit yfir á heimaskjáinn fyrir auðveldari aðgang að þeim."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Stjórnað af fyrirtækinu þínu"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Slökkt er á tilkynningum og forritum"</string>
</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a22a4ce..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> scorciatoie e <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notifiche relative a <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignora"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notifica ignorata"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personali"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Lavoro"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profilo di lavoro"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Qui puoi trovare le tue app di lavoro"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Ogni app di lavoro è contrassegnata da un badge arancione e viene tenuta al sicuro dalla tua organizzazione. Sposta le app nella schermata Home per accedervi più facilmente."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gestito dalla tua organizzazione"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Le notifiche e le app non sono attive"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index d7905b4..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> קיצורי דרך ו-<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> הודעות של <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"סגור"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ההודעה נסגרה"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"אישיות"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"פרופיל עבודה"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ניתן למצוא כאן את אפליקציות העבודה"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"לכל אפליקציית עבודה יש תג כתום ואבטחתה מטופלת בידי הארגון. אפשר להעביר אפליקציות אל מסך דף הבית כדי להקל את הגישה אליהן."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"מנוהל בידי הארגון"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"הודעות ואפליקציות כבויות"</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4b6c3ef..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 個のショートカットと <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> 件の通知"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"表示しない"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"通知を非表示にしました"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人用"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"仕事用"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"仕事用プロファイル"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ここには仕事用アプリが表示されます"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"仕事用アプリにはオレンジのバッジが表示され、組織によって安全に保護されています。仕事用アプリをホーム画面に移動すると、簡単にアクセスできます。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"組織によって管理されています"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"通知とアプリは OFF です"</string>
</resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index ec60583..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>-ის <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> მალსახმობი და <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> შეტყობინება"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"დახურვა"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"შეტყობინება დაიხურა"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"პირადი"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"სამსახური"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"სამსახურის პროფილი"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"აქ თავმოყრილია სამსახურის აპები"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"სამსახურის აპები მონიშნულია სტაფილოსფერი ბეჯით, რაც ნიშნავს, რომ მათ უსაფრთხოებას თქვენი ორგანიზაცია უზრუნველყოფს. მარტივი წვდომისთვის, შეგიძლიათ სამსახურის აპები მთავარი ეკრანზე გადაიტანოთ."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"იმართება თქვენი ორგანიზაციის მიერ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"შეტყობინებები და აპები გამორთულია"</string>
</resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 115b752..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> қолданбасының <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> таңбашасы мен <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> хабарландыруы"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Бас тарту"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Хабарландырудан бас тартылды"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Жұмыс"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жұмыс профилі"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Жұмыс қолданбалары осы жерде берілген"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Әрбір жұмыс қолданбасында қызғылт сары танымбелгі бар. Ол оның қауіпсіздігі ұйым арқылы қамтамасыз етілетінін білдіреді. Жұмыс қолданбаларына оңай кіру үшін, оларды Негізгі экранға жылжытуға болады."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Ұйым арқылы басқарылады"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Хабарландырулар мен қолданбалар өшірулі"</string>
</resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 9040811..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"ផ្លូវកាត់ចំនួន <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> និងការជូនដំណឹងចំនួន <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> សម្រាប់ <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"បដិសេធ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"បានបដិសេធការជូនដំណឹង"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ផ្ទាល់ខ្លួន"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ការងារ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"កម្រងព័ត៌មានការងារ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ស្វែងរកកម្មវិធីការងារនៅទីនេះ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"កម្មវិធីការងារនីមួយៗមានស្លាកពណ៌ទឹកក្រូច និងត្រូវបានរក្សាទុកយ៉ាងមានសុវត្ថិភាពដោយស្ថាប័នរបស់អ្នក។ សូមផ្លាស់ទីកម្មវិធីទៅកាន់អេក្រង់ដើមរបស់អ្នក ដើម្បីងាយស្រួលចូលប្រើជាងមុន។"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"គ្រប់គ្រងដោយស្ថាប័នរបស់អ្នក"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ការជូនដំណឹង និងកម្មវិធីត្រូវបានបិទ"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 4156a21..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ಗಾಗಿ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ಶಾರ್ಟ್ಕಟ್ಗಳು ಮತ್ತು <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ಅಧಿಸೂಚನೆಯನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ವೈಯಕ್ತಿಕ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ಕೆಲಸ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"ಕೆಲಸದ ಪ್ರೊಫೈಲ್"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಇಲ್ಲಿ ಹುಡುಕಿ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"ಕೆಲಸದ ಪ್ರತಿ ಅಪ್ಲಿಕೇಶನ್ ಕಿತ್ತಳೆ ಬ್ಯಾಡ್ಜ್ ಹೊಂದಿದೆ ಮತ್ತು ನಿಮ್ಮ ಸಂಸ್ಥೆಯಿಂದ ಸುರಕ್ಷಿತವಾಗಿ ಇರಿಸಲಾಗುತ್ತದೆ. ಸುಲಭ ಪ್ರವೇಶಕ್ಕಾಗಿ ನಿಮ್ಮ ಹೋಮ್ ಸ್ಕ್ರೀನ್ಗೆ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸರಿಸಿ."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯ ಮೂಲಕ ನಿರ್ವಹಿಸಲಾಗಿದೆ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ಅಧಿಸೂಚನೆಗಳು ಮತ್ತು ಅಪ್ಲಿಕೇಶನ್ಗಳು ಆಫ್ ಆಗಿವೆ"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index d9d8da6..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"바로가기 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>개 및 <xliff:g id="APP_NAME">%3$s</xliff:g> 알림 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>개"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"닫기"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"알림이 해제되었습니다."</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"개인"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"직장"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"직장 프로필"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"여기에서 업무용 앱 찾기"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"각 업무용 앱에는 주황색 배지가 있으며 업무용 앱은 조직에서 안전하게 보호됩니다. 앱을 홈 화면으로 이동하여 더 간편하게 사용하세요."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"조직에서 관리"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"알림 및 앱 사용 중지됨"</string>
</resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 0808214..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> колдонмосу үчүн <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кыска жол жана <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> эскертме бар"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Этибарга албоо"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Эскертме көз жаздымда калтырылды"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Жеке колдонмолор"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Жумуш колдонмолору"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Жумуш профили"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Жумуш колдонмолорун бул жерден таап алыңыз"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Ар бир жумуш колдонмосунун кызгылт сары бейджиги бар жана ал уюмуңуз тарабынан коопсуз сакталат. Колдонмолорго тез өтүү үчүн аларды Башкы экранга кошуп алыңыз."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Уюмуңуз тарабынан башкарылат"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Билдирүүлөр жана колдонмолор өчүрүлгөн"</string>
</resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 92420a2..40ffffe 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -35,19 +35,8 @@
<!-- Folders -->
<dimen name="folder_preview_padding">2dp</dimen>
- <!-- Page indicator -->
- <dimen name="dynamic_grid_page_indicator_land_left_nav_bar_gutter_width">50dp</dimen>
- <dimen name="dynamic_grid_page_indicator_land_right_nav_bar_gutter_width">74dp</dimen>
-
<!-- Hotseat -->
<!-- Will be set to equal the hotseat icon size. -->
<dimen name="dynamic_grid_hotseat_size">0dp</dimen>
-
- <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">50dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_left_nav_bar_left_padding">44dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
-
- <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">56dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_right_nav_bar_left_padding">32dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">6dp</dimen>
+ <dimen name="dynamic_grid_hotseat_side_padding">16dp</dimen>
</resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index c1a1e71..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ທາງລັດ ແລະ <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ການແຈ້ງເຕືອນສຳລັບ <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ປິດໄວ້"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ປິດການແຈ້ງເຕືອນແລ້ວ"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ສ່ວນຕົວ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ວຽກ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ຊອກຫາແອັບວຽກຢູ່ບ່ອນນີ້"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"ແຕ່ລະແອັບວຽກຈະມີປ້າຍສີສົ້ມ ແລະ ຖືກຈັດເກັບໄວ້ຢ່າງປອດໄພໂດຍອົງກອນຂອງທ່ານ. ທ່ານສາມາດຍ້າຍແອັບໄປໃສ່ໜ້າຈໍຫຼັກເພື່ອໃຫ້ເຂົ້າໃຊ້ງ່າຍຂຶ້ນໄດ້."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ຈັດການໂດຍອົງກອນຂອງທ່ານ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ການແຈ້ງເຕືອນ ແລະ ແອັບຖືກປິດໄວ້"</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index ca45583..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"„<xliff:g id="APP_NAME">%3$s</xliff:g>“ spartieji klavišai (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) ir pranešimai (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>)"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Atsisakyti"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Pranešimo atsisakyta"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Asmeninės"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Darbo"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Darbo profilis"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Darbo programas rasite čia"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Kiekvienai darbo programai priskirtas oranžinis ženklelis, o tokių programų sauga rūpinasi jūsų organizacija. Perkelkite darbo programas į pagrindinį ekraną, kad galėtumėte lengviau jas pasiekti."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Tvarko jūsų organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Programos ir pranešimai išjungti"</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7a4903a..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>
@@ -129,4 +134,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> saīsnes un <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> paziņojumi lietotnei <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Nerādīt"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Paziņojums netiek rādīts"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personīgās lietotnes"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Darba lietotnes"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Darba profils"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Meklējiet darba lietotnes šeit"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Katrai darba lietotnei ir oranža emblēma, un jūsu organizācija aizsargā šīs lietotnes. Ērtākai piekļuvei pārvietojiet darba lietotnes uz sākuma ekrānu."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Pārvalda jūsu organizācija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Paziņojumi un lietotnes ir izslēgtas"</string>
</resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 0aaed62..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кратенки и <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> известувања за <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Отфрли"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Известувањето е отфрлено"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Лично"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"За работа"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Работен профил"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Најдете апликации за работа тука"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Секоја апликација за работа има портокалова значка и е обезбедена од вашата организација. За полесен пристап, апликациите за работа преместете ги на почетниот екран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Управувано од вашата организација"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Известувањата и апликациите се исклучени"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 299fc45..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ആപ്പിനായുള്ള <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> കുറുക്കുവഴികളും <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> അറിയിപ്പുകളും"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"നിരസിക്കുക"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"അറിയിപ്പ് നിരസിച്ചു"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"വ്യക്തിപരം"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ജോലി"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ഔദ്യോഗിക ആപ്പുകൾ ഇവിടെ കണ്ടെത്തുക"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"എല്ലാ ഔദ്യോഗിക ആപ്പിനും ഓറഞ്ച് നിറത്തിലുള്ള ഒരു ബാഡ്ജ് ഉണ്ട്, നിങ്ങളുടെ സ്ഥാപനം അത് സുരക്ഷിതമായി സൂക്ഷിക്കുന്നു. എളുപ്പത്തിൽ ആക്സസ് ചെയ്യാൻ ആപ്പുകളെ ഹോം സ്ക്രീനിലേക്ക് നീക്കുക."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"നിങ്ങളുടെ സ്ഥാപനം നിയന്ത്രിക്കുന്നത്"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"അറിയിപ്പുകളും ആപ്പുകളും ഓഫാണ്"</string>
</resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 8921c77..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> товчлол болон <xliff:g id="APP_NAME">%3$s</xliff:g>-н <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> мэдэгдэл"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Хаах"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Мэдэгдлийг хаасан"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Хувийн"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Ажил"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ажлын профайл"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ажлын аппыг эндээс олно уу"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Ажлын апп тус бүр улбар шар тэмдэгтэй ба эдгээрийг танай байгууллагаас аюулгүй байлгадаг. Аппуудад хялбараар хандахын тулд тэдгээрийг Үндсэн Нүүрэнд зөөнө үү."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Танай байгууллагаас удирддаг"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Мэдэгдэл, апп унтраалттай байна"</string>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index ca9a402..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>
@@ -98,7 +102,7 @@
<string name="abandoned_promises_title" msgid="7096178467971716750">"हा अॅप इंस्टॉल केलेला नाही"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
- <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करीत आहे"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"होम स्क्रीनवर जोडा"</string>
<string name="action_move_here" msgid="2170188780612570250">"आयटम येथे हलवा"</string>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>साठी <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट आणि <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचना"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"डिसमिस करा"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना डिसमिस केली"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"वैयक्तिक"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्यालय"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"कामाची अॅप्स येथे मिळवा"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"प्रत्येक कार्य अॅपमध्ये नारिंगी बॅज असतो आणि तो तुमच्या संस्थेकडून सुरक्षित ठेवला जातो. अधिक सहज अॅक्सेससाठी अॅप्स तुमच्या होम स्क्रीनवर हलवा."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"तुमच्या संस्थेकडून व्यवस्थापित"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना आणि अॅप्स बंद आहेत"</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 6aeac8b..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan dan <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> pemberitahuan untuk <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ketepikan"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Pemberitahuan diketepikan"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Peribadi"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Kerja"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil kerja"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Temui apl kerja di sini"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Setiap apl kerja terdapat lencana berwarna oren dan dilindungi oleh organisasi anda. Alihkan apl ke Skrin Utama untuk akses yang lebh mudah."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Diurus oleh organisasi anda"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Pemberitahuan dan apl dimatikan"</string>
</resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 0b2e0d5..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> အတွက် ဖြတ်လမ်းလင့်ခ် <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> နှင့် အကြောင်းကြားချက် <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ခု"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ပယ်ရန်"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"အသိပေးချက်ကို ဖယ်ထုတ်ပြီးပါပြီ"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ကိုယ်ပိုင်"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"အလုပ်"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"အလုပ်ပရိုဖိုင်"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"အလုပ်အက်ပ်များကို ဤနေရာတွင်ရှာဖွေပါ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"အလုပ်အက်ပ်တိုင်းတွင် လိမ္မော်ရောင်တံဆိပ် တစ်ခုစီရှိပြီး သင်၏ အဖွဲ့အစည်းက လုံခြုံအောင် ထားရှိပါသည်။ အသုံးပြုရ ပိုမိုလွယ်ကူစေရန် အက်ပ်များကို သင်၏ ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ။"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"သင်၏ အဖွဲ့အစည်းက စီမံခန့်ခွဲထားပါသည်"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"အကြောင်းကြားချက်များနှင့် အက်ပ်များကို ပိတ်ထားသည်"</string>
</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 0a6276b..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> snarveier og <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> varsler for <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Avvis"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Varselet ble avvist"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personlig"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Jobb"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Finn jobbapper her"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Alle jobbapper har et oransje merke og sikres av organisasjonen din. Flytt apper til startskjermen for å gjøre det enklere å finne dem."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Administreres av organisasjonen din"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Varsler og apper er slått av"</string>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 41e43a7..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> का <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> सर्टकट र <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचनाहरू"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"खारेज गर्नुहोस्"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना खारेज गरियो"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"व्यक्तिगत"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्यसम्बन्धी"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"कार्य प्रोफाइल"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"कार्यसम्बन्धी अनुप्रयोगहरू यहाँ प्राप्त गर्नुहोस्"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"कार्यसम्बन्धी प्रत्येक अनुप्रयोगमा एउटा सुन्तला रङको ब्याज छ र यसलाई तपाईंको संगठनद्वारा सुरक्षित राखिएको छ । अझ सजिलो गरी पहुँच राख्नका लागि कार्यसम्बन्धी अनुप्रयोगहरूलाई तपाईंको गृहस्क्रिनमा सार्नुहोस्।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"तपाईंको सङ्गठनले व्यवस्थापन गरेको"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"सूचना र अनुप्रयोगहरू निष्क्रिय छन्"</string>
</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 16a79ae..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>
@@ -100,7 +104,7 @@
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wordt gedownload, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
<string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wacht op installatie"</string>
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-widgets"</string>
- <string name="action_add_to_workspace" msgid="8902165848117513641">"Toevoegen aan homepage"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Toevoegen aan startscherm"</string>
<string name="action_move_here" msgid="2170188780612570250">"Item hier naartoe verplaatsen"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item toegevoegd aan startscherm"</string>
<string name="item_removed" msgid="851119963877842327">"Item verwijderd"</string>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> snelkoppelingen en <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> meldingen voor <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Sluiten"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Melding gesloten"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privé"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Werk"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Werkprofiel"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Zoek hier naar werk-apps"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Elke werk-app heeft een oranje badge en wordt beveiligd door je organisatie. Verplaats apps naar je startscherm om gemakkelijker toegang te krijgen."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Beheerd door je organisatie"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Meldingen en apps zijn uitgeschakeld"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 525f2f6..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ਲਈ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ ਅਤੇ <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ਖਾਰਜ ਕਰੋ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ਸੂਚਨਾ ਖਾਰਜ ਕੀਤੀ ਗਈ"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ਨਿੱਜੀ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ਕਾਰਜ-ਸਥਾਨ"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ ਇੱਥੇ ਲੱਭੋ"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"ਹਰੇਕ ਕਾਰਜ-ਸਥਾਨ ਐਪ ਦਾ ਇੱਕ ਸੰਤਰੀ ਬੈਜ ਹੁੰਦਾ ਹੈ ਅਤੇ ਉਸਨੂੰ ਤੁਹਾਡੀ ਸੰਸਥਾ ਰਾਹੀਂ ਸੁਰੱਖਿਅਤ ਰੱਖਿਆ ਜਾਂਦਾ ਹੈ। ਵਧੇਰੇ ਆਸਾਨ ਪਹੁੰਚ ਲਈ ਐਪਾਂ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਲਿਜਾਓ।"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ਸੂਚਨਾਵਾਂ ਅਤੇ ਐਪਾਂ ਬੰਦ ਹਨ"</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 2a01d04..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Skróty (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) i powiadomienia (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) aplikacji <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Odrzuć"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Powiadomienie odrzucone"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobiste"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Praca"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil służbowy"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Aplikacje do pracy"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="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>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Powiadomienia i aplikacje są wyłączone"</string>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index ee4bf08..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>
@@ -72,7 +76,7 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
<string name="settings_button_text" msgid="8873672322605444408">"Definições da página inicial"</string>
- <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo administrador"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo gestor"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Vista geral"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação do ecrã principal"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o telemóvel é rodado"</string>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atalhos e <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificações para a aplicação <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificação ignorada"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabalho"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Encontrar as aplicações de trabalho aqui"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Cada aplicação de trabalho apresenta um emblema laranja, pelo que a sua entidade a mantém em segurança. Pode mover as aplicações para o ecrã principal para facilitar o acesso."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gerido pela sua entidade"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"As notificações e as aplicações estão desativadas."</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index b550f6f..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atalhos e <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificações para o app <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Dispensar"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificação dispensada"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Pessoais"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Comerciais"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Perfil de trabalho"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Localizar apps de trabalho aqui"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Cada app de trabalho tem um selo laranja e é mantido em segurança pela sua organização. Mova os apps para sua tela inicial para facilitar o acesso."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gerenciados pela sua organização"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"As notificações e os apps estão desativados"</string>
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 9cf1cc6..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>
@@ -129,4 +134,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> comenzi rapide și <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> notificări pentru <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Închideți"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Notificare închisă"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Profesionale"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profil de serviciu"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Găsiți aplicații de serviciu aici"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Fiecare aplicație de serviciu are o insignă portocalie și este păstrată în siguranță de organizația dvs. Mutați aplicațiile de serviciu pe ecranul de pornire pentru acces mai ușor."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Gestionat de organizația dvs."</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Notificările și aplicațiile sunt dezactivate"</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index bfd014e..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Количество ярлыков для приложения \"<xliff:g id="APP_NAME">%3$s</xliff:g>\": <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>. Количество уведомлений: <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>."</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Закрыть"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Уведомление закрыто"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Личные"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Рабочие"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Рабочий профиль"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Приложения для работы"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Рабочие приложения отмечены оранжевым значком, который подтверждает, что ваша организация гарантирует их безопасность. Для удобства перенесите эти приложения на главный экран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Управляется вашей организацией"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Уведомления и приложения отключены."</string>
</resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 349b7ca..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> සඳහා කෙටි මං <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>ක් සහ දැනුම්දීම් <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>ක්"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ඉවතලන්න"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"දැනුම්දීම ඉවතලන ලදී"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"පුද්ගලික"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"කාර්යාලය"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"කාර්යාල පැතිකඩ"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"මෙහි කාර්යාල යෙදුම් සොයා ගන්න"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"සෑම කාර්යාල යෙදුමකම තැඹිලි ලාංඡනයක් ඇත ඇති අතර එය ඔබේ සංවිධානය විසින් සුරක්ෂිතව තබා ගනී. වඩා පහසුවෙන් පිවිසීමට යෙදුම් ඔබගේ මුල් පිටු තිරය වෙත ගෙන යන්න."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"ඔබේ සංවිධානය විසින් කළමනාකරණය කරනු ලැබේ"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"දැනුම්දීම් සහ යෙදුම් ක්රියාවිරහිතයි"</string>
</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index ba84811..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Odkazy (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) a upozornenia (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) pre aplikáciu <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Zavrieť"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Upozornenie bolo zavreté"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osobné"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Pracovné"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Pracovný profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Tu nájdete pracovné aplikácie"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Všetky pracovné aplikácie majú oranžový štítok a sú bezpečne uchovávané vašou organizáciou. Ak chcete mať k aplikáciám ľahší prístup, presuňte ich na plochu."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Spravované vašou organizáciou"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Upozornenia a aplikácie sú vypnuté"</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index c317c0e..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Bližnjice (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) in obvestila (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) aplikacije <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Opusti"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Obvestilo je bilo opuščeno"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Osebno"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Služba"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Delovni profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Tukaj poiščite delovne aplikacije"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Vsaka delovna aplikacija ima oranžno značko. Za varnost teh aplikacij skrbi vaša organizacija. Za preprostejši dostop premaknite aplikacije na začetni zaslon."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Upravlja vaša organizacija"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Obvestila in aplikacije – izklopljeno"</string>
</resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index ed07912..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shkurtore dhe <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> njoftime për <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Hiqe"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Njoftimi u hoq"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personale"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Punë"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profili i punës"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Gjej këtu aplikacionet e punës"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Secili aplikacion pune ka një distinktiv portokalli dhe mbahet i sigurt nga organizata jote. Zhvendosi aplikacionet e punës në ekranin tënd kryesor për qasje më të lehtë."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Menaxhohet nga organizata jote"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Njoftimet dhe aplikacionet janë joaktive"</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f40366f..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>
@@ -129,4 +134,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Пречице (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) и обавештења (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) за <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Одбаци"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Обавештење је одбачено"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Личне"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Пословне"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Профил за Work"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Пронађите пословне апликације овде"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Свака пословна апликација има наранџасту значку и штити је ваша организација. Преместите апликације на почетни екран да бисте им лакше приступали."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Овим управља организација"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Обавештења и апликације су искључени"</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index ff6b4e8..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> har <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> genvägar och <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> aviseringar"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ignorera"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Aviseringen togs bort"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Privat"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Arbete"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Jobbprofil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Här hittar du jobbappar"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Alla jobbappar har ett orange märke och organisationen ser till att de är skyddade. Flytta apparna till startskärmen så kommer du åt dem lättare."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Hanteras av organisationen"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Aviseringar och appar är inaktiverade"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 402f3a6..8925c89 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -60,14 +60,18 @@
<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) -->
<skip />
<string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
<string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"Gonga ili ufunge folda"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"Gonga ili ubadilishe jina"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Gusa ili ufunge folda"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Gusa ili ubadilishe jina"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folda imefungwa"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -131,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Njia <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> za mkato na arifa <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> za <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ondoa"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Arifa imeondolewa"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Binafsi"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Wasifu wa kazini"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Pata programu za kazi hapa"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Kila programu ya kazi ina beji ya rangi ya machungwa na hulindwa na shirika lako. Hamishia programu kwenye skrini yako ya kwanza ili uzifikie kwa urahisi."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Inasimamiwa na shirika lako"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Vipenge vya arifa na programu vimezimwa"</string>
</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index b211207..1497b5a 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -15,6 +15,9 @@
-->
<resources>
+ <!-- Dynamic Grid -->
+ <dimen name="dynamic_grid_min_page_indicator_size">24dp</dimen>
+
<!-- All Apps -->
<dimen name="all_apps_button_scale_down">8dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 5d72030..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> பயன்பாட்டிற்கான <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> குறுக்குவழிகளும் <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> அறிவிப்புகளும் உள்ளன"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"நிராகரி"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"அறிவிப்பு நிராகரிக்கப்பட்டது"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணி விவரம்"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"பணி ஆப்ஸை இங்கு காணலாம்"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"ஒவ்வொரு பணிப் பயன்பாடும் ஆரஞ்சு நிற பேட்ஜைக் கொண்டிருக்கும். இவை, உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"உங்கள் நிறுவனம் நிர்வகிக்கிறது"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ஆப்ஸும் அறிவிப்புகளும் ஆஃப் செய்யப்பட்டுள்ளன"</string>
</resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 4db5352..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> కోసం <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> సత్వరమార్గాలు మరియు <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> నోటిఫికేషన్లు"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"తీసివేయి"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"నోటిఫికేషన్ తీసివేయబడింది"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"వ్యక్తిగతం"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"కార్యాలయం"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"కార్యాలయ ప్రొఫైల్"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"కార్యాలయ యాప్లను ఇక్కడ కనుగొనండి"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"ప్రతి కార్యాలయ యాప్కు నారింజ బ్యాడ్జ్ ఉంది మరియు మీ సంస్థ ద్వారా సురక్షితంగా ఉంచబడుతుంది. సులభ యాక్సెస్ కోసం యాప్లను మీ హోమ్ స్క్రీన్కి తరలించండి."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"మీ సంస్థ ద్వారా నిర్వహించబడతాయి"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"నోటిఫికేషన్లు మరియు యాప్లు ఆఫ్ చేయబడ్డాయి"</string>
</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index ae5aeb5..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"ทางลัด <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> รายการและการแจ้งเตือน <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> รายการสำหรับ <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ปิด"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ปิดการแจ้งเตือนแล้ว"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ส่วนตัว"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"งาน"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"โปรไฟล์งาน"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"หาแอปงานที่นี่"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"แอปงานแต่ละแอปมีป้ายสีส้มและได้รับการรักษาความปลอดภัยจากองค์กรของคุณ ย้ายแอปไปยังหน้าจอหลักเพื่อให้เข้าถึงได้ง่ายขึ้น"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"จัดการโดยองค์กร"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"ปิดการแจ้งเตือนและแอปอยู่"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 11f13b0..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> (na) shortcut at <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> (na) notification para sa <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"I-dismiss"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Na-dismiss ang notification"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Personal"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Trabaho"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Profile sa trabaho"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Maghanap ng mga app para sa trabaho rito"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Ang bawat app para sa trabaho ay may orange na badge at pinapanatiling ligtas ng iyong organisasyon. Ilipat ang mga app sa iyong Home screen para mas madaling ma-access."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Pinamamahalaan ng iyong organisasyon"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Naka-off ang mga notification at app"</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index f19cd78..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> için <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> kısayol ve <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> bildirim"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Kapat"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Bildirim kapatıldı"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Kişisel"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"İş"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"İş profili"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"İş uygulamalarını burada bulabilirsiniz"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Her iş uygulamasında, uygulama güvenliğinin kuruluşunuz tarafından sağlandığını gösteren turuncu bir rozet bulunur. Uygulamaları daha kolay erişim için Ana ekranınıza taşıyın."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Kuruluşunuz tarafından yönetiliyor"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirimler ve uygulamalar kapalı"</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 08f1575..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>
@@ -129,4 +135,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Ярлики (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) і сповіщення (<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g>) додатка <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Закрити"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Сповіщення закрито"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Особисті додатки"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Робочі додатки"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Робочий профіль"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Робочі додатки містяться тут"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Кожний робочий додаток має оранжевий значок. Його захищає організація. Для швидкого доступу перенесіть додатки на головний екран."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Профілем керує ваша організація"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Сповіщення та додатки вимкнено"</string>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 99b8c80..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> کے <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> شارٹ کٹس اور <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> اطلاعات"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"برخاست کریں"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"اطلاع مسترد ہو گئی"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ذاتی"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"دفتری"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"دفتری پروفائل"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"یہاں دفتری ایپس تلاش کریں"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"ہر دفتری ایپ میں نارنجی بَیج ہوتا ہے اور اسے آپ کی تنظیم محفوظ رکھتی ہے۔ زیادہ آسان رسائی کیلئے ایپس کو اپنی ہوم اسکرین پر منتقل کریں۔"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"آپ کی تنظیم کے زیر انتظام"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"اطلاعات اور ایپس آف ہیں"</string>
</resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 5167670..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ilovasi uchun <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ta yorliq va <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ta bildirishnoma"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Yopish"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Bildirishnoma yopildi"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Shaxsiy"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Ishchi"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Ishchi profil"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Ishga oid ilovalarni shu yerdan topish mumkin"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Apelsinrangli nishonga ega har bir ishga oid ilova tashkilotingiz tomonidan himoyalanadi. Ishga oid ilovalarga osonroq kirish uchun ularni bosh ekranga chiqaring."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Tashkilotingiz tomonidan boshqariladi"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Bildirishnomalar va ilovalar faol emas"</string>
</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index c0e1454..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> phím tắt và <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> thông báo cho <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Loại bỏ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Đã loại bỏ thông báo"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Cá nhân"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Cơ quan"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Hồ sơ công việc"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Tìm ứng dụng công việc tại đây"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Mỗi ứng dụng công việc đều có một huy hiệu màu cam và được tổ chức của bạn bảo mật. Bạn có thể di chuyển ứng dụng đến Màn hình chính để truy cập dễ dàng hơn."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Do tổ chức của bạn quản lý"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Thông báo và ứng dụng đang tắt"</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2342133..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>的 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 个快捷方式和 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> 条通知"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"关闭"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"已关闭通知"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"个人"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"工作"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作资料"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"请在此处查找工作应用"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"每个工作应用均有一个橙色徽标,并由贵单位负责确保其安全。请将工作应用移到主屏幕,以便轻松访问。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"由贵单位管理"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"通知和应用均已关闭"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index f87ff7f..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"「<xliff:g id="APP_NAME">%3$s</xliff:g>」嘅 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 個捷徑同 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> 個通知"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"關閉"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"關閉咗通知"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"商務"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"工作設定檔"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"請在此處尋找工作應用程式"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"每個工作應用程式都有橙色徽章,並由您的機構負責保持安全。您可以將工作應用程式移至主畫面,以便輕鬆存取。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"由您的機構管理"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"通知和應用程式已關閉"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 814a6e4..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 個捷徑和 <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> 則「<xliff:g id="APP_NAME">%3$s</xliff:g>」通知"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"關閉"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"已關閉通知"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"個人"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"公司"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Work 設定檔"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"在這裡尋找辦公應用程式"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"每個辦公應用程式都有橘色徽章,並由貴機構負責管理及確保其安全。請將辦公應用程式移至主螢幕以便輕鬆存取。"</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"由貴機構所管理"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"已關閉通知和應用程式"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 35fa1c7..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>
@@ -129,4 +133,11 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> izinqamuleli nezaziso ezingu-<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ze-<xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Cashisa"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Isaziso sicashisiwe"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Okomuntu siqu"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Umsebenzi"</string>
+ <string name="work_profile_toggle_label" msgid="3081029915775481146">"Iphrofayela yomsebenzi"</string>
+ <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"Thola izinhlelo zokusebenza lapha"</string>
+ <string name="bottom_work_tab_user_education_body" msgid="1485375451542813426">"Uhlo lokusebenza ngalunye lomsebenzi linebheji ewolintshi futhi igcinwa iphephile inhlangano yakho. Hambisa izinhlelo zokusebenza esikrinini sakho sasekhaya ngokufinyelela okulula."</string>
+ <string name="work_mode_on_label" msgid="4781128097185272916">"Kuphethwe inhlangano yakho"</string>
+ <string name="work_mode_off_label" msgid="3194894777601421047">"Izaziso nezinhlelo zokusebenza kuvaliwe"</string>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index ad5f0b8..1351dfa 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -91,10 +91,6 @@
<attr name="layout_ignoreInsets" format="boolean" />
</declare-styleable>
- <declare-styleable name="ButtonDropTarget">
- <attr name="hideParentOnDisable" format="boolean" />
- </declare-styleable>
-
<declare-styleable name="InvariantDeviceProfile">
<attr name="name" format="string" />
<attr name="minWidthDps" format="float" />
@@ -105,8 +101,6 @@
<!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
<attr name="numFolderRows" format="integer" />
<attr name="numFolderColumns" format="integer" />
- <!-- minAllAppsPredictionColumns defaults to numColumns, if not specified -->
- <attr name="minAllAppsPredictionColumns" format="integer" />
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index b44a31e..eb207af 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -32,8 +32,6 @@
<!-- Popup container -->
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
- <color name="badge_color">#1DE9B6</color> <!-- Teal A400 -->
- <color name="folder_badge_color">#1DE9B6</color> <!-- Teal A400 -->
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
<color name="legacy_icon_background">#FFFFFF</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 0ff0d75..065ad36 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -47,10 +47,6 @@
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
- <!-- This constant stores the ratio of the all apps button drawable which
- is used for internal (baked-in) padding -->
- <integer name="config_allAppsButtonPaddingPercent">17</integer>
-
<!-- The duration of the animation from search hint to text entry -->
<integer name="config_searchHintAnimationDuration">50</integer>
@@ -96,15 +92,24 @@
<!-- Name of a user event dispatcher class. -->
<string name="user_event_dispatcher_class" translatable="false"></string>
+ <!-- Name of an app transition manager class. -->
+ <string name="app_transition_manager_class" translatable="false"></string>
+
<!-- Name of a color extraction implementation class. -->
<string name="color_extraction_impl_class" translatable="false"></string>
<!-- 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>
+ <!-- Whitelisted package to retrieve packagename for badge. Can be empty. -->
+ <string name="shortcutinfocompat_badgepkg_whitelist" translatable="false"></string>
+
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index efe5043..1f46844 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,10 +20,8 @@
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">8dp</dimen>
- <dimen name="dynamic_grid_min_page_indicator_size">32dp</dimen>
+ <dimen name="dynamic_grid_min_page_indicator_size">24dp</dimen>
<dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
- <dimen name="dynamic_grid_page_indicator_land_left_nav_bar_gutter_width">0dp</dimen>
- <dimen name="dynamic_grid_page_indicator_land_right_nav_bar_gutter_width">0dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
<dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
<dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
@@ -42,19 +40,16 @@
<dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
<dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
<dimen name="dynamic_grid_hotseat_size">80dp</dimen>
+ <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_left_nav_bar_left_padding">0dp</dimen>
- <dimen name="dynamic_grid_hotseat_land_right_nav_bar_left_padding">0dp</dimen>
-
+ <!-- Hotseat/all-apps scrim -->
+ <dimen name="all_apps_scrim_radius">8dp</dimen>
+ <dimen name="all_apps_scrim_margin">8dp</dimen>
+ <dimen name="all_apps_scrim_blur">4dp</dimen>
<!-- Drop target bar -->
<dimen name="dynamic_grid_drop_target_size">48dp</dimen>
- <dimen name="vert_drop_target_vertical_gap">20dp</dimen>
- <dimen name="vert_drop_target_horizontal_gap">14dp</dimen>
+ <dimen name="drop_target_vertical_gap">20dp</dimen>
<!-- App Widget resize frame -->
<dimen name="widget_handle_margin">13dp</dimen>
@@ -89,9 +84,13 @@
<dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
<dimen name="all_apps_background_canvas_width">700dp</dimen>
<dimen name="all_apps_background_canvas_height">475dp</dimen>
- <dimen name="all_apps_caret_workspace_offset">18dp</dimen>
<dimen name="all_apps_header_tab_height">50dp</dimen>
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
+ <dimen name="all_apps_header_top_padding">36dp</dimen>
+ <dimen name="all_apps_work_profile_tab_footer_top_padding">16dp</dimen>
+ <dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
+ <dimen name="all_apps_tabs_side_padding">12dp</dimen>
+ <dimen name="all_apps_divider_height">1dp</dimen>
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
@@ -189,10 +188,6 @@
<dimen name="popup_arrow_horizontal_center_start">28dp</dimen>
<!-- popup_padding_end + deep_shortcut_drag_handle_size / 2 -->
<dimen name="popup_arrow_horizontal_center_end">24dp</dimen>
- <!-- popup_arrow_center_start - popup_arrow_width / 2-->
- <dimen name="popup_arrow_horizontal_offset_start">23dp</dimen>
- <!-- popup_arrow_center_end - popup_arrow_width / 2-->
- <dimen name="popup_arrow_horizontal_offset_end">19dp</dimen>
<dimen name="popup_arrow_corner_radius">2dp</dimen>
<!-- popup_padding_start + icon_size + 10dp -->
<dimen name="deep_shortcuts_text_padding_start">56dp</dimen>
@@ -206,12 +201,8 @@
<!-- (touch_size - icon_size) / 2 -->
<dimen name="system_shortcut_header_icon_padding">12dp</dimen>
-<!-- Icon badges (with notification counts) -->
- <dimen name="badge_small_padding">0dp</dimen>
- <dimen name="badge_large_padding">3dp</dimen>
-
<!-- Notifications -->
- <dimen name="bg_round_rect_radius">12dp</dimen>
+ <dimen name="bg_round_rect_radius">8dp</dimen>
<dimen name="notification_padding_start">16dp</dimen>
<dimen name="notification_padding_end">12dp</dimen>
<!-- notification_padding_end + (icon_size - footer_icon_size) / 2 -->
@@ -235,6 +226,6 @@
<dimen name="popup_item_divider_height">0.5dp</dimen>
<dimen name="swipe_helper_falsing_threshold">70dp</dimen>
-<!-- Recents -->
- <dimen name="recents_page_spacing">10dp</dimen>
+<!-- Overview -->
+ <dimen name="options_menu_icon_size">48dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fdd4d8d..381830c 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -132,6 +132,11 @@
<!-- Accessibility -->
<!-- The format string for when an app is temporarily disabled. -->
<string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
+ <!-- The format string for when an app has a notification dot (meaning it has associated notifications). -->
+ <plurals name="badged_app_label">
+ <item quantity="one"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="1">%2$d</xliff:g> notification</item>
+ <item quantity="other"><xliff:g id="app_name" example="Messenger">%1$s</xliff:g>, has <xliff:g id="notification_count" example="3">%2$d</xliff:g> notifications</item>
+ </plurals>
<skip />
<!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
@@ -324,4 +329,16 @@
<!-- Label of tab to indicate work apps -->
<string name="all_apps_work_tab">Work</string>
+ <!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
+ <string name="work_profile_toggle_label">Work profile</string>
+ <!-- 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 a badge and is kept secure by your organization. Move apps to your Home screen for easier access.</string>
+ <!-- This string is in the work profile tab when a user has All Apps open on their phone. It describes the label of a toggle, "Work profile," as being managed by the user's employer.
+ "Organization" is used to represent a variety of businesses, non-profits, and educational institutions).-->
+ <string name="work_mode_on_label">Managed by your organization</string>
+ <!-- This string appears under a the label of a toggle in the work profile tab on a user's phone. It describes the status of the toggle, "Work profile," when it's turned off. "Work profile" means a separate profile on a user's phone that's speficially for their work apps and is managed by their company.-->
+ <string name="work_mode_off_label">Notifications and apps are off</string>
+
</resources>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 30c1c54..a34f225 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -25,7 +25,6 @@
launcher:numColumns="3"
launcher:numFolderRows="2"
launcher:numFolderColumns="3"
- launcher:minAllAppsPredictionColumns="3"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="3"
@@ -40,7 +39,6 @@
launcher:numColumns="3"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
- launcher:minAllAppsPredictionColumns="3"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="3"
@@ -55,7 +53,6 @@
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -70,7 +67,6 @@
launcher:numColumns="4"
launcher:numFolderRows="3"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -85,7 +81,6 @@
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -100,7 +95,6 @@
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -115,7 +109,6 @@
launcher:numColumns="4"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
@@ -130,7 +123,6 @@
launcher:numColumns="5"
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="56"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="5"
@@ -145,7 +137,6 @@
launcher:numColumns="6"
launcher:numFolderRows="4"
launcher:numFolderColumns="5"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="64"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="7"
@@ -160,7 +151,6 @@
launcher:numColumns="6"
launcher:numFolderRows="4"
launcher:numFolderColumns="5"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="76"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="7"
@@ -175,7 +165,6 @@
launcher:numColumns="7"
launcher:numFolderRows="6"
launcher:numFolderColumns="6"
- launcher:minAllAppsPredictionColumns="4"
launcher:iconSize="100"
launcher:iconTextSize="20.0"
launcher:numHotseatIcons="7"
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index 26024e5..fc5ce8f 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -42,7 +42,11 @@
TYPE_WIDGETS_BOTTOM_SHEET,
TYPE_WIDGET_RESIZE_FRAME,
TYPE_WIDGETS_FULL_SHEET,
- TYPE_QUICKSTEP_PREVIEW
+ TYPE_ON_BOARD_POPUP,
+
+ TYPE_QUICKSTEP_PREVIEW,
+ TYPE_TASK_MENU,
+ TYPE_OPTIONS_POPUP
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -51,11 +55,20 @@
public static final int TYPE_WIDGETS_BOTTOM_SHEET = 1 << 2;
public static final int TYPE_WIDGET_RESIZE_FRAME = 1 << 3;
public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
- public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 5;
+ public static final int TYPE_ON_BOARD_POPUP = 1 << 5;
+
+ // Popups related to quickstep UI
+ public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 6;
+ public static final int TYPE_TASK_MENU = 1 << 7;
+ public static final int TYPE_OPTIONS_POPUP = 1 << 8;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
- | TYPE_QUICKSTEP_PREVIEW;
+ | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_TASK_MENU | TYPE_OPTIONS_POPUP;
+
+ // Type of popups which should be kept open during launcher rebind
+ public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
+ | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_OPTIONS_POPUP;
protected boolean mIsOpen;
@@ -79,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);
@@ -147,6 +161,7 @@
public static void closeAllOpenViews(Launcher launcher, boolean animate) {
closeOpenViews(launcher, animate, TYPE_ALL);
+ launcher.finishAutoCancelActionMode();
}
public static void closeAllOpenViews(Launcher launcher) {
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 9796d18..4d1bedc 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -110,7 +110,7 @@
info.runtimeStatusFlags |= (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
? FLAG_SYSTEM_NO : FLAG_SYSTEM_YES;
- if (FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO
+ if (Utilities.ATLEAST_OREO
&& appInfo.targetSdkVersion >= Build.VERSION_CODES.O
&& Process.myUserHandle().equals(lai.getUser())) {
// The icon for a non-primary user is badged, hence it's not exactly an adaptive icon.
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 1e95333..7648e30 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -19,6 +19,7 @@
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.util.FocusLogic;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
private static final int SNAP_DURATION = 150;
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 162aa08..469b8bb 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -29,6 +29,7 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
+import android.os.Build.VERSION;
import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
@@ -436,9 +437,11 @@
}
// Auto installs should always support the current platform version.
+ LauncherIcons li = LauncherIcons.obtain(mContext);
mValues.put(LauncherSettings.Favorites.ICON, Utilities.flattenBitmap(
- LauncherIcons.createBadgedIconBitmap(
- icon, Process.myUserHandle(), mContext, Build.VERSION.SDK_INT)));
+ li.createBadgedIconBitmap(icon, Process.myUserHandle(), VERSION.SDK_INT).icon));
+ li.recycle();
+
mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index e496495..12db3b6 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -22,15 +22,22 @@
import android.content.Intent;
import android.view.View.AccessibilityDelegate;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.util.SystemUiController;
+import java.util.ArrayList;
+
public abstract class BaseActivity extends Activity {
+ private final ArrayList<OnDeviceProfileChangeListener> mDPChangeListeners = new ArrayList<>();
+
protected DeviceProfile mDeviceProfile;
protected UserEventDispatcher mUserEventDispatcher;
protected SystemUiController mSystemUiController;
+ private boolean mStarted;
+
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
}
@@ -41,8 +48,7 @@
public final UserEventDispatcher getUserEventDispatcher() {
if (mUserEventDispatcher == null) {
- mUserEventDispatcher = UserEventDispatcher.newInstance(this,
- mDeviceProfile.isLandscape, isInMultiWindowModeCompat());
+ mUserEventDispatcher = UserEventDispatcher.newInstance(this, mDeviceProfile);
}
return mUserEventDispatcher;
}
@@ -69,4 +75,31 @@
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
+
+ @Override
+ protected void onStart() {
+ mStarted = true;
+ super.onStart();
+ }
+
+ @Override
+ protected void onStop() {
+ mStarted = false;
+ super.onStop();
+ }
+
+ public boolean isStarted() {
+ return mStarted;
+ }
+
+ public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
+ mDPChangeListeners.add(listener);
+ }
+
+ protected void dispatchDeviceProfileChanged() {
+ int count = mDPChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ mDPChangeListeners.get(i).onDeviceProfileChanged(mDeviceProfile);
+ }
+ }
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index b315980..cc13263 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -17,13 +17,11 @@
package com.android.launcher3;
import android.content.Context;
-import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.TextView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -68,6 +66,7 @@
ViewGroup parent = (ViewGroup) getParent().getParent();
mScrollbar = parent.findViewById(R.id.fast_scroller);
mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
+ onUpdateScrollbar(0);
}
/**
@@ -90,8 +89,10 @@
*/
private boolean handleTouchEvent(MotionEvent ev) {
// Move to mScrollbar's coordinate system.
- int left = getLeft() - mScrollbar.getLeft();
- int top = getTop() - mScrollbar.getTop();
+ // We need to take parent into account (view pager's location)
+ ViewGroup parent = (ViewGroup) getParent();
+ int left = parent.getLeft() - mScrollbar.getLeft();
+ int top = parent.getTop() + getTop() - mScrollbar.getTop() - getScrollBarTop();
ev.offsetLocation(left, top);
try {
return mScrollbar.handleTouchEvent(ev);
@@ -112,7 +113,7 @@
* Returns the height of the fast scroll bar
*/
public int getScrollbarTrackHeight() {
- return getHeight() - getScrollBarTop() - getPaddingBottom();
+ return mScrollbar.getHeight() - getScrollBarTop() - getPaddingBottom();
}
/**
@@ -130,12 +131,6 @@
return availableScrollBarHeight;
}
- @Override
- protected void dispatchDraw(Canvas canvas) {
- onUpdateScrollbar(0);
- super.dispatchDraw(canvas);
- }
-
/**
* Updates the scrollbar thumb offset to match the visible scroll of the recycler view. It does
* this by mapping the available scroll area of the recycler view to the available space for the
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a590504..dbdb2dc 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -88,7 +88,7 @@
private BadgeInfo mBadgeInfo;
private BadgeRenderer mBadgeRenderer;
- private IconPalette mBadgePalette;
+ private int mBadgeColor;
private float mBadgeScale;
private boolean mForceHideBadge;
private Point mTempSpaceForBadgeOffset = new Point();
@@ -183,7 +183,7 @@
*/
public void reset() {
mBadgeInfo = null;
- mBadgePalette = null;
+ mBadgeColor = Color.TRANSPARENT;
mBadgeScale = 0f;
mForceHideBadge = false;
}
@@ -193,7 +193,7 @@
}
public void applyFromShortcutInfo(ShortcutInfo info, boolean promiseStateChanged) {
- applyIconAndLabel(info.iconBitmap, info);
+ applyIconAndLabel(info);
setTag(info);
if (promiseStateChanged || (info.hasPromiseIconUi())) {
applyPromiseState(promiseStateChanged);
@@ -203,7 +203,7 @@
}
public void applyFromApplicationInfo(AppInfo info) {
- applyIconAndLabel(info.iconBitmap, info);
+ applyIconAndLabel(info);
// We don't need to check the info since it's not a ShortcutInfo
super.setTag(info);
@@ -219,7 +219,7 @@
}
public void applyFromPackageItemInfo(PackageItemInfo info) {
- applyIconAndLabel(info.iconBitmap, info);
+ applyIconAndLabel(info);
// We don't need to check the info since it's not a ShortcutInfo
super.setTag(info);
@@ -227,8 +227,10 @@
verifyHighRes();
}
- private void applyIconAndLabel(Bitmap icon, ItemInfo info) {
- FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(icon, info);
+ private void applyIconAndLabel(ItemInfoWithIcon info) {
+ FastBitmapDrawable iconDrawable = DrawableFactory.get(getContext()).newIcon(info);
+ mBadgeColor = IconPalette.getMutedColor(info.iconColor, 0.54f);
+
iconDrawable.setIsDisabled(info.isDisabled());
setIcon(iconDrawable);
setText(info.title);
@@ -401,7 +403,7 @@
final int scrollX = getScrollX();
final int scrollY = getScrollY();
canvas.translate(scrollX, scrollY);
- mBadgeRenderer.draw(canvas, mBadgePalette, mBadgeInfo, mTempIconBounds, mBadgeScale,
+ mBadgeRenderer.draw(canvas, mBadgeColor, mTempIconBounds, mBadgeScale,
mTempSpaceForBadgeOffset);
canvas.translate(-scrollX, -scrollY);
}
@@ -532,7 +534,7 @@
preloadDrawable.setLevel(progressLevel);
} else {
preloadDrawable = DrawableFactory.get(getContext())
- .newPendingIcon(info.iconBitmap, getContext());
+ .newPendingIcon(info, getContext());
preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
@@ -550,10 +552,6 @@
float newBadgeScale = isBadged ? 1f : 0;
mBadgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
if (wasBadged || isBadged) {
- mBadgePalette = IconPalette.getBadgePalette(getResources());
- if (mBadgePalette == null) {
- mBadgePalette = ((FastBitmapDrawable) mIcon).getIconPalette();
- }
// Animate when a badge is first added or when it is removed.
if (animate && (wasBadged ^ isBadged) && isShown()) {
ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
@@ -562,13 +560,18 @@
invalidate();
}
}
+ if (itemInfo.contentDescription != null) {
+ if (hasBadge()) {
+ int count = mBadgeInfo.getNotificationCount();
+ setContentDescription(getContext().getResources().getQuantityString(
+ R.plurals.badged_app_label, count, itemInfo.contentDescription, count));
+ } else {
+ setContentDescription(itemInfo.contentDescription);
+ }
+ }
}
}
- public IconPalette getBadgePalette() {
- return mBadgePalette;
- }
-
/**
* Sets the icon for this view based on the layout direction.
*/
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index cfb55cc..19ee0b8 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -16,27 +16,28 @@
package com.android.launcher3;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
import static com.android.launcher3.LauncherState.NORMAL;
import android.animation.AnimatorSet;
import android.animation.FloatArrayEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.widget.PopupWindow;
import android.widget.TextView;
import com.android.launcher3.anim.Interpolators;
@@ -56,7 +57,10 @@
private static final int[] sTempCords = new int[2];
private static final int DRAG_VIEW_DROP_DURATION = 285;
- private final boolean mHideParentOnDisable;
+ public static final int TOOLTIP_DEFAULT = 0;
+ public static final int TOOLTIP_LEFT = 1;
+ public static final int TOOLTIP_RIGHT = 2;
+
protected final Launcher mLauncher;
private int mBottomDragPadding;
@@ -75,6 +79,10 @@
protected CharSequence mText;
protected ColorStateList mOriginalTextColor;
protected Drawable mDrawable;
+ private boolean mTextVisible = true;
+
+ private PopupWindow mToolTip;
+ private int mToolTipLocation;
private AnimatorSet mCurrentColorAnim;
@Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
@@ -89,11 +97,6 @@
Resources resources = getResources();
mBottomDragPadding = resources.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
-
- TypedArray a = context.obtainStyledAttributes(attrs,
- R.styleable.ButtonDropTarget, defStyle, 0);
- mHideParentOnDisable = a.getBoolean(R.styleable.ButtonDropTarget_hideParentOnDisable, false);
- a.recycle();
mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
}
@@ -102,21 +105,56 @@
super.onFinishInflate();
mText = getText();
mOriginalTextColor = getTextColors();
+ setContentDescription(mText);
}
protected void setDrawable(int resId) {
// We do not set the drawable in the xml as that inflates two drawables corresponding to
// drawableLeft and drawableStart.
- setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
- mDrawable = getCompoundDrawablesRelative()[0];
+ if (mTextVisible) {
+ setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
+ mDrawable = getCompoundDrawablesRelative()[0];
+ } else {
+ setCompoundDrawablesRelativeWithIntrinsicBounds(0, resId, 0, 0);
+ mDrawable = getCompoundDrawablesRelative()[1];
+ }
}
public void setDropTargetBar(DropTargetBar dropTargetBar) {
mDropTargetBar = dropTargetBar;
}
+ private void hideTooltip() {
+ if (mToolTip != null) {
+ mToolTip.dismiss();
+ mToolTip = null;
+ }
+ }
+
@Override
public final void onDragEnter(DragObject d) {
+ if (!d.accessibleDrag && !mTextVisible) {
+ // Show tooltip
+ hideTooltip();
+
+ TextView message = (TextView) LayoutInflater.from(getContext()).inflate(
+ R.layout.drop_target_tool_tip, null);
+ message.setText(mText);
+
+ mToolTip = new PopupWindow(message, WRAP_CONTENT, WRAP_CONTENT);
+ int x = 0, y = 0;
+ if (mToolTipLocation != TOOLTIP_DEFAULT) {
+ y = -getMeasuredHeight();
+ message.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ if (mToolTipLocation == TOOLTIP_LEFT) {
+ x = - getMeasuredWidth() - message.getMeasuredWidth() / 2;
+ } else {
+ x = getMeasuredWidth() / 2 + message.getMeasuredWidth() / 2;
+ }
+ }
+ mToolTip.showAsDropDown(this, x, y);
+ }
+
d.dragView.setColor(mHoverColor);
animateTextColor(mHoverColor);
if (d.stateAnnouncer != null) {
@@ -153,13 +191,9 @@
ValueAnimator anim1 = ValueAnimator.ofObject(
new FloatArrayEvaluator(mCurrentFilter.getArray()),
mSrcFilter.getArray(), mDstFilter.getArray());
- anim1.addUpdateListener(new AnimatorUpdateListener() {
-
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
- invalidate();
- }
+ anim1.addUpdateListener((anim) -> {
+ mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
+ invalidate();
});
mCurrentColorAnim.play(anim1);
@@ -169,6 +203,8 @@
@Override
public final void onDragExit(DragObject d) {
+ hideTooltip();
+
if (!d.dragComplete) {
d.dragView.setColor(0);
resetHoverColor();
@@ -187,8 +223,7 @@
mCurrentColorAnim = null;
}
setTextColor(mOriginalTextColor);
- (mHideParentOnDisable ? ((ViewGroup) getParent()) : this)
- .setVisibility(mActive ? View.VISIBLE : View.GONE);
+ setVisibility(mActive ? View.VISIBLE : View.GONE);
mAccessibleDrag = options.isAccessibleDrag;
setOnClickListener(mAccessibleDrag ? this : null);
@@ -230,14 +265,12 @@
final float scale = (float) to.width() / from.width();
mDropTargetBar.deferOnDragEnd();
- Runnable onAnimationEndRunnable = new Runnable() {
- @Override
- public void run() {
- completeDrop(d);
- mDropTargetBar.onDragEnd();
- mLauncher.getStateManager().goToState(NORMAL);
- }
+ Runnable onAnimationEndRunnable = () -> {
+ completeDrop(d);
+ mDropTargetBar.onDragEnd();
+ mLauncher.getStateManager().goToState(NORMAL);
};
+
dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
DRAG_VIEW_DROP_DURATION,
Interpolators.DEACCEL_2, Interpolators.LINEAR, onAnimationEndRunnable,
@@ -294,8 +327,8 @@
to.set(left, top, right, bottom);
// Center the destination rect about the trash icon
- final int xOffset = (int) -(viewWidth - width) / 2;
- final int yOffset = (int) -(viewHeight - height) / 2;
+ final int xOffset = -(viewWidth - width) / 2;
+ final int yOffset = -(viewHeight - height) / 2;
to.offset(xOffset, yOffset);
return to;
@@ -310,25 +343,25 @@
return getTextColors().getDefaultColor();
}
- /**
- * Returns True if any update was made.
- */
- public boolean updateText(boolean hide) {
- if ((hide && getText().toString().isEmpty()) || (!hide && mText.equals(getText()))) {
- return false;
+ public void setTextVisible(boolean isVisible) {
+ CharSequence newText = isVisible ? mText : "";
+ if (mTextVisible != isVisible || !TextUtils.equals(newText, getText())) {
+ mTextVisible = isVisible;
+ setText(newText);
+ if (mTextVisible) {
+ setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+ } else {
+ setCompoundDrawablesRelativeWithIntrinsicBounds(null, mDrawable, null, null);
+ }
}
-
- setText(hide ? "" : mText);
- return true;
}
- public boolean isTextTruncated() {
- int availableWidth = getMeasuredWidth();
- if (mHideParentOnDisable) {
- ViewGroup parent = (ViewGroup) getParent();
- availableWidth = parent.getMeasuredWidth() - parent.getPaddingLeft()
- - parent.getPaddingRight();
- }
+ public void setToolTipLocation(int location) {
+ mToolTipLocation = location;
+ hideTooltip();
+ }
+
+ public boolean isTextTruncated(int availableWidth) {
availableWidth -= (getPaddingLeft() + getPaddingRight() + mDrawable.getIntrinsicWidth()
+ getCompoundDrawablePadding());
CharSequence displayedText = TextUtils.ellipsize(mText, getPaint(), availableWidth,
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 79a34a0..5e4f670 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -60,6 +60,7 @@
import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -106,7 +107,7 @@
final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
private static final int[] BACKGROUND_STATE_ACTIVE = new int[] { android.R.attr.state_active };
- private static final int[] BACKGROUND_STATE_DEFAULT = new int[0];
+ private static final int[] BACKGROUND_STATE_DEFAULT = EMPTY_STATE_SET;
private final Drawable mBackground;
// These values allow a fixed measurement to be set on the CellLayout.
@@ -846,10 +847,6 @@
return super.verifyDrawable(who) || (who == mBackground);
}
- public void setShortcutAndWidgetAlpha(float alpha) {
- mShortcutsAndWidgets.setAlpha(alpha);
- }
-
public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
return mShortcutsAndWidgets;
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index c6226f4..ba55b36 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -25,23 +25,12 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup.LayoutParams;
-import android.widget.FrameLayout;
import com.android.launcher3.CellLayout.ContainerType;
-import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.badge.BadgeRenderer;
-import java.util.ArrayList;
-
public class DeviceProfile {
- public interface LauncherLayoutChangeListener {
- void onLauncherLayoutChanged();
- }
-
public final InvariantDeviceProfile inv;
// Device properties
@@ -51,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;
@@ -65,23 +56,19 @@
private static final float TALL_DEVICE_ASPECT_RATIO_THRESHOLD = 2.0f;
-
// Workspace
- private final int desiredWorkspaceLeftRightMarginPx;
+ public final int desiredWorkspaceLeftRightMarginPx;
public final int cellLayoutPaddingLeftRightPx;
public final int cellLayoutBottomPaddingPx;
public final int edgeMarginPx;
public final Rect defaultWidgetPadding;
- private final int defaultPageSpacingPx;
+ public final int defaultPageSpacingPx;
private final int topWorkspacePadding;
public float workspaceSpringLoadShrinkFactor;
public final int workspaceSpringLoadedBottomSpace;
// Page indicator
- private int pageIndicatorSizePx;
- private final int pageIndicatorLandLeftNavBarGutterPx;
- private final int pageIndicatorLandRightNavBarGutterPx;
- private final int pageIndicatorLandWorkspaceOffsetPx;
+ public final int pageIndicatorSizePx;
// Workspace icons
public int iconSizePx;
@@ -111,20 +98,12 @@
public int hotseatCellHeightPx;
// In portrait: size = height, in landscape: size = width
public int hotseatBarSizePx;
- public int hotseatBarTopPaddingPx;
- public int hotseatBarBottomPaddingPx;
-
- public int hotseatBarLeftNavBarLeftPaddingPx;
- public int hotseatBarLeftNavBarRightPaddingPx;
-
- public int hotseatBarRightNavBarLeftPaddingPx;
- public int hotseatBarRightNavBarRightPaddingPx;
+ public final int hotseatBarTopPaddingPx;
+ public final int hotseatBarBottomPaddingPx;
+ public final int hotseatBarSidePaddingPx;
// All apps
public int allAppsCellHeightPx;
- public int allAppsNumCols;
- public int allAppsNumPredictiveCols;
- public int allAppsButtonVisualSize;
public int allAppsIconSizePx;
public int allAppsIconDrawablePaddingPx;
public float allAppsIconTextSizePx;
@@ -136,20 +115,19 @@
public int dropTargetBarSizePx;
// Insets
- private Rect mInsets = new Rect();
-
- // Listeners
- private ArrayList<LauncherLayoutChangeListener> mListeners = new ArrayList<>();
+ private final Rect mInsets = new Rect();
+ public final Rect workspacePadding = new Rect();
// Icon badges
public BadgeRenderer mBadgeRenderer;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
Point minSize, Point maxSize,
- int width, int height, boolean isLandscape) {
+ int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
this.inv = inv;
this.isLandscape = isLandscape;
+ this.isMultiWindowMode = isMultiWindowMode;
Resources res = context.getResources();
DisplayMetrics dm = res.getDisplayMetrics();
@@ -180,12 +158,6 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
pageIndicatorSizePx = res.getDimensionPixelSize(
R.dimen.dynamic_grid_min_page_indicator_size);
- pageIndicatorLandLeftNavBarGutterPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_page_indicator_land_left_nav_bar_gutter_width);
- pageIndicatorLandRightNavBarGutterPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_page_indicator_land_right_nav_bar_gutter_width);
- pageIndicatorLandWorkspaceOffsetPx =
- res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
defaultPageSpacingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
topWorkspacePadding =
@@ -202,14 +174,8 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
hotseatBarBottomPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
- hotseatBarLeftNavBarRightPaddingPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_land_left_nav_bar_right_padding);
- hotseatBarRightNavBarRightPaddingPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_land_right_nav_bar_right_padding);
- hotseatBarLeftNavBarLeftPaddingPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_land_left_nav_bar_left_padding);
- hotseatBarRightNavBarLeftPaddingPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_hotseat_land_right_nav_bar_left_padding);
+ hotseatBarSidePaddingPx =
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
hotseatBarSizePx = isVerticalBarLayout()
? Utilities.pxFromDp(inv.iconSize, dm)
: res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_size)
@@ -243,14 +209,19 @@
// Recalculate the available dimensions using the new hotseat size.
updateAvailableDimensions(dm, res);
}
-
- computeAllAppsButtonSize(context);
+ updateWorkspacePadding();
// This is done last, after iconSizePx is calculated above.
- mBadgeRenderer = new BadgeRenderer(context, iconSizePx);
+ mBadgeRenderer = new BadgeRenderer(iconSizePx);
}
- DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
+ public DeviceProfile copy(Context context) {
+ Point size = new Point(availableWidthPx, availableHeightPx);
+ return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape,
+ isMultiWindowMode);
+ }
+
+ public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
// We take the minimum sizes of this profile and it's multi-window variant to ensure that
// the system decor is always excluded.
mwSize.set(Math.min(availableWidthPx, mwSize.x), Math.min(availableHeightPx, mwSize.y));
@@ -259,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
@@ -273,22 +244,11 @@
float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
float appWidgetScaleY = (float) profile.getCellSize().y / getCellSize().y;
profile.appWidgetScale.set(appWidgetScaleX, appWidgetScaleY);
+ profile.updateWorkspacePadding();
return profile;
}
- public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
- if (!mListeners.contains(listener)) {
- mListeners.add(listener);
- }
- }
-
- public void removeLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
- if (mListeners.contains(listener)) {
- mListeners.remove(listener);
- }
- }
-
/**
* Adjusts the profile so that the labels on the Workspace are hidden.
* It is important to call this method after the All Apps variables have been set.
@@ -307,17 +267,6 @@
+ topBottomPadding * 2;
}
- /**
- * Determine the exact visual footprint of the all apps button, taking into account scaling
- * and internal padding of the drawable.
- */
- private void computeAllAppsButtonSize(Context context) {
- Resources res = context.getResources();
- float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
- allAppsButtonVisualSize = (int) (iconSizePx * (1 - padding)) - context.getResources()
- .getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
- }
-
private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
updateIconSize(1f, res, dm);
@@ -333,7 +282,8 @@
private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
// Workspace
- float invIconSizePx = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+ final boolean isVerticalLayout = isVerticalBarLayout();
+ float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
@@ -341,8 +291,8 @@
cellHeightPx = iconSizePx + iconDrawablePaddingPx
+ Utilities.calculateTextHeight(iconTextSizePx);
int cellYPadding = (getCellSize().y - cellHeightPx) / 2;
- if (iconDrawablePaddingPx > cellYPadding && !isVerticalBarLayout()
- && !inMultiWindowMode()) {
+ if (iconDrawablePaddingPx > cellYPadding && !isVerticalLayout
+ && !isMultiWindowMode) {
// Ensures that the label is closer to its corresponding icon. This is not an issue
// with vertical bar layout or multi-window mode since the issue is handled separately
// with their calls to {@link #adjustToHideWorkspaceLabels}.
@@ -357,18 +307,18 @@
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
allAppsCellHeightPx = getCellSize().y;
- if (isVerticalBarLayout()) {
+ if (isVerticalLayout) {
// Always hide the Workspace text with vertical bar layout.
adjustToHideWorkspaceLabels();
}
// Hotseat
- if (isVerticalBarLayout()) {
+ if (isVerticalLayout) {
hotseatBarSizePx = iconSizePx;
}
hotseatCellHeightPx = iconSizePx;
- if (!isVerticalBarLayout()) {
+ if (!isVerticalLayout) {
int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
- pageIndicatorSizePx - topWorkspacePadding;
float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
@@ -395,15 +345,16 @@
// Don't let the folder get too close to the edges of the screen.
int folderMargin = edgeMarginPx;
+ Point totalWorkspacePadding = getTotalWorkspacePadding();
// Check if the icons fit within the available height.
float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
- int maxHeight = availableHeightPx - getTotalWorkspacePadding().y - folderMargin;
+ int maxHeight = availableHeightPx - totalWorkspacePadding.y - folderMargin;
float scaleY = maxHeight / usedHeight;
// Check if the icons fit within the available width.
float usedWidth = folderCellWidthPx * inv.numFolderColumns;
- int maxWidth = availableWidthPx - getTotalWorkspacePadding().x - folderMargin;
+ int maxWidth = availableWidthPx - totalWorkspacePadding.x - folderMargin;
float scaleX = maxWidth / usedWidth;
float scale = Math.min(scaleX, scaleY);
@@ -429,32 +380,11 @@
public void updateInsets(Rect insets) {
mInsets.set(insets);
+ updateWorkspacePadding();
}
- public void updateAppsViewNumCols() {
- allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns;
- }
-
- /** Returns the width and height of the search bar, ignoring any padding. */
- public Point getSearchBarDimensForWidgetOpts() {
- if (isVerticalBarLayout()) {
- return new Point(dropTargetBarSizePx, availableHeightPx - 2 * edgeMarginPx);
- } else {
- int gap;
- if (isTablet) {
- // Pad the left and right of the workspace to ensure consistent spacing
- // between all icons
- int width = getCurrentWidth();
- // XXX: If the icon size changes across orientations, we will have to take
- // that into account here too.
- gap = ((width - 2 * edgeMarginPx
- - (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)))
- + edgeMarginPx;
- } else {
- gap = desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right;
- }
- return new Point(availableWidthPx - 2 * gap, dropTargetBarSizePx);
- }
+ public Rect getInsets() {
+ return mInsets;
}
public Point getCellSize() {
@@ -470,43 +400,40 @@
}
public Point getTotalWorkspacePadding() {
- Rect padding = getWorkspacePadding(null);
- return new Point(padding.left + padding.right, padding.top + padding.bottom);
+ updateWorkspacePadding();
+ return new Point(workspacePadding.left + workspacePadding.right,
+ workspacePadding.top + workspacePadding.bottom);
}
/**
- * Returns the workspace padding in the specified orientation.
+ * Updates {@link #workspacePadding} as a result of any internal value change to reflect the
+ * new workspace padding
*/
- public Rect getWorkspacePadding(Rect recycle) {
- Rect padding = recycle == null ? new Rect() : recycle;
+ private void updateWorkspacePadding() {
+ Rect padding = workspacePadding;
if (isVerticalBarLayout()) {
- if (mInsets.left > 0) {
- padding.set(mInsets.left + pageIndicatorLandLeftNavBarGutterPx,
- 0,
- hotseatBarSizePx + hotseatBarLeftNavBarRightPaddingPx
- + hotseatBarLeftNavBarLeftPaddingPx
- - mInsets.left,
- edgeMarginPx);
+ padding.top = 0;
+ padding.bottom = edgeMarginPx;
+ padding.left = hotseatBarSidePaddingPx;
+ padding.right = hotseatBarSidePaddingPx;
+ if (isSeascape()) {
+ padding.left += hotseatBarSizePx;
+ padding.right += pageIndicatorSizePx;
} else {
- padding.set(pageIndicatorLandRightNavBarGutterPx,
- 0,
- hotseatBarSizePx + hotseatBarRightNavBarRightPaddingPx
- + hotseatBarRightNavBarLeftPaddingPx,
- edgeMarginPx);
+ padding.left += pageIndicatorSizePx;
+ padding.right += hotseatBarSizePx;
}
} else {
int paddingBottom = hotseatBarSizePx + pageIndicatorSizePx;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
- int width = getCurrentWidth();
- int height = getCurrentHeight();
// The amount of screen space available for left/right padding.
- int availablePaddingX = Math.max(0, width - ((inv.numColumns * cellWidthPx) +
+ int availablePaddingX = Math.max(0, widthPx - ((inv.numColumns * cellWidthPx) +
((inv.numColumns - 1) * cellWidthPx)));
availablePaddingX = (int) Math.min(availablePaddingX,
- width * MAX_HORIZONTAL_PADDING_PERCENT);
- int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
+ widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
+ int availablePaddingY = Math.max(0, heightPx - topWorkspacePadding - paddingBottom
- (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
- hotseatBarBottomPaddingPx);
padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
@@ -519,7 +446,6 @@
paddingBottom);
}
}
- return padding;
}
/**
@@ -534,26 +460,14 @@
mInsets.top + availableHeightPx);
} else {
// Folders should only appear below the drop target bar and above the hotseat
- return new Rect(mInsets.left,
+ return new Rect(mInsets.left + edgeMarginPx,
mInsets.top + dropTargetBarSizePx + edgeMarginPx,
- mInsets.left + availableWidthPx,
+ mInsets.left + availableWidthPx - edgeMarginPx,
mInsets.top + availableHeightPx - hotseatBarSizePx
- pageIndicatorSizePx - edgeMarginPx);
}
}
- private int getWorkspacePageSpacing() {
- if (isVerticalBarLayout() || isLargeTablet) {
- // In landscape mode the page spacing is set to the default.
- return defaultPageSpacingPx;
- } else {
- // In portrait, we want the pages spaced such that there is no
- // overhang of the previous / next page into the current page viewport.
- // We assume symmetrical padding in portrait mode.
- return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left + 1);
- }
- }
-
public static int calculateCellWidth(int width, int countX) {
return width / countX;
}
@@ -570,128 +484,15 @@
return isLandscape && transposeLayoutWithOrientation;
}
- boolean shouldFadeAdjacentWorkspaceScreens() {
+ public boolean isSeascape() {
+ // TODO: This might not hold true for multi window mode, use configuration insead.
+ return isVerticalBarLayout() && mInsets.left > mInsets.right;
+ }
+
+ public boolean shouldFadeAdjacentWorkspaceScreens() {
return isVerticalBarLayout() || isLargeTablet;
}
- public void layout(Launcher launcher, boolean notifyListeners) {
- FrameLayout.LayoutParams lp;
- boolean hasVerticalBarLayout = isVerticalBarLayout();
-
- // Layout the search bar space
- Point searchBarBounds = getSearchBarDimensForWidgetOpts();
- View searchBar = launcher.getDropTargetBar();
- lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
- lp.width = searchBarBounds.x;
- lp.height = searchBarBounds.y;
- lp.topMargin = mInsets.top + edgeMarginPx;
- searchBar.setLayoutParams(lp);
-
- // Layout the workspace
- PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
- Rect workspacePadding = getWorkspacePadding(null);
- workspace.setPadding(workspacePadding.left, workspacePadding.top, workspacePadding.right,
- workspacePadding.bottom);
- workspace.setPageSpacing(getWorkspacePageSpacing());
-
- // Layout the hotseat
- Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
- lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
- // We want the edges of the hotseat to line up with the edges of the workspace, but the
- // icons in the hotseat are a different size, and so don't line up perfectly. To account for
- // this, we pad the left and right of the hotseat with half of the difference of a workspace
- // cell vs a hotseat cell.
- float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns;
- float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons;
- int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
- if (hasVerticalBarLayout) {
- // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
- // screen regardless of RTL
- int paddingRight = mInsets.left > 0
- ? hotseatBarLeftNavBarRightPaddingPx
- : hotseatBarRightNavBarRightPaddingPx;
- int paddingLeft = mInsets.left > 0
- ? hotseatBarLeftNavBarLeftPaddingPx
- : hotseatBarRightNavBarLeftPaddingPx;
-
- lp.gravity = Gravity.RIGHT;
- lp.width = hotseatBarSizePx + mInsets.left + mInsets.right
- + paddingLeft + paddingRight;
- lp.height = LayoutParams.MATCH_PARENT;
-
- hotseat.getLayout().setPadding(mInsets.left + cellLayoutPaddingLeftRightPx
- + paddingLeft,
- mInsets.top,
- mInsets.right + cellLayoutPaddingLeftRightPx + paddingRight,
- workspacePadding.bottom + cellLayoutBottomPaddingPx);
- } else if (isTablet) {
- // Pad the hotseat with the workspace padding calculated above
- lp.gravity = Gravity.BOTTOM;
- lp.width = LayoutParams.MATCH_PARENT;
- lp.height = hotseatBarSizePx + mInsets.bottom;
- hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left
- + cellLayoutPaddingLeftRightPx,
- hotseatBarTopPaddingPx,
- hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
- hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
- } else {
- // For phones, layout the hotseat without any bottom margin
- // to ensure that we have space for the folders
- lp.gravity = Gravity.BOTTOM;
- lp.width = LayoutParams.MATCH_PARENT;
- lp.height = hotseatBarSizePx + mInsets.bottom;
- hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left
- + cellLayoutPaddingLeftRightPx,
- hotseatBarTopPaddingPx,
- hotseatAdjustment + workspacePadding.right + cellLayoutPaddingLeftRightPx,
- hotseatBarBottomPaddingPx + mInsets.bottom + cellLayoutBottomPaddingPx);
- }
- hotseat.setLayoutParams(lp);
-
- // Layout the page indicators
- View pageIndicator = launcher.findViewById(R.id.page_indicator);
- if (pageIndicator != null) {
- lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
- if (isVerticalBarLayout()) {
- if (mInsets.left > 0) {
- lp.leftMargin = mInsets.left;
- } else {
- lp.leftMargin = pageIndicatorLandWorkspaceOffsetPx;
- }
- lp.bottomMargin = workspacePadding.bottom;
- } else {
- // Put the page indicators above the hotseat
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.height = pageIndicatorSizePx;
- lp.bottomMargin = hotseatBarSizePx + mInsets.bottom;
- }
- pageIndicator.setLayoutParams(lp);
- }
-
- // Layout the AllAppsRecyclerView
- AllAppsContainerView appsView = launcher.findViewById(R.id.apps_view);
- int paddingLeftRight = desiredWorkspaceLeftRightMarginPx + cellLayoutPaddingLeftRightPx;
- appsView.setRecyclerViewSidePadding(paddingLeftRight, paddingLeftRight);
-
- if (notifyListeners) {
- for (int i = mListeners.size() - 1; i >= 0; i--) {
- mListeners.get(i).onLauncherLayoutChanged();
- }
- }
- }
-
- public int getCurrentWidth() {
- return isLandscape
- ? Math.max(widthPx, heightPx)
- : Math.min(widthPx, heightPx);
- }
-
- public int getCurrentHeight() {
- return isLandscape
- ? Math.min(widthPx, heightPx)
- : Math.max(widthPx, heightPx);
- }
-
public int getCellHeight(@ContainerType int containerType) {
switch (containerType) {
case CellLayout.WORKSPACE:
@@ -706,34 +507,29 @@
}
}
- /**
- * @return the left/right paddings for all containers.
- */
- public final int[] getContainerPadding() {
- // No paddings for portrait phone
- if (isPhone && !isVerticalBarLayout()) {
- return new int[] {0, 0};
- }
-
- // In landscape, we match the width of the workspace
- Rect padding = getWorkspacePadding(null);
- return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
- }
-
- 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) {
Configuration context = new Configuration(c.getResources().getConfiguration());
context.orientation = orientation;
return c.createConfigurationContext(context);
+ }
+ /**
+ * Callback when a component changes the DeviceProfile associated with it, as a result of
+ * configuration change
+ */
+ public interface OnDeviceProfileChangeListener {
+
+ /**
+ * Called when the device profile is reassigned. Note that for layout and measurements, it
+ * is sufficient to listen for inset changes. Use this callback when you need to perform
+ * a one time operation.
+ */
+ void onDeviceProfileChanged(DeviceProfile dp);
}
}
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 3eca5cd..a3fe89a 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -17,38 +17,37 @@
package com.android.launcher3;
import static com.android.launcher3.AlphaUpdateListener.updateVisibility;
-import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+import static com.android.launcher3.ButtonDropTarget.TOOLTIP_DEFAULT;
+import static com.android.launcher3.ButtonDropTarget.TOOLTIP_LEFT;
+import static com.android.launcher3.ButtonDropTarget.TOOLTIP_RIGHT;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import android.animation.TimeInterpolator;
import android.content.Context;
+import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewDebug;
-import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
-import java.util.ArrayList;
-
/*
* The top bar containing various drop targets: Delete/App Info/Uninstall.
*/
-public class DropTargetBar extends LinearLayout implements DragController.DragListener {
+public class DropTargetBar extends FrameLayout
+ implements DragListener, Insettable {
protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
protected static final TimeInterpolator DEFAULT_INTERPOLATOR = Interpolators.ACCEL;
- private final Runnable mFadeAnimationEndRunnable = new Runnable() {
-
- @Override
- public void run() {
- updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
- }
- };
+ private final Runnable mFadeAnimationEndRunnable =
+ () -> updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
@ViewDebug.ExportedProperty(category = "launcher")
protected boolean mDeferOnDragEnd;
@@ -59,6 +58,8 @@
private ButtonDropTarget[] mDropTargets;
private ViewPropertyAnimator mCurrentAnimation;
+ private boolean mIsVertical = true;
+
public DropTargetBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -70,86 +71,138 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mDropTargets = new ButtonDropTarget[getChildCount()];
+ for (int i = 0; i < mDropTargets.length; i++) {
+ mDropTargets[i] = (ButtonDropTarget) getChildAt(i);
+ mDropTargets[i].setDropTargetBar(this);
+ }
+ }
- // Initialize with hidden state
- setAlpha(0f);
+ @Override
+ public void setInsets(Rect insets) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ mIsVertical = grid.isVerticalBarLayout();
+
+ lp.leftMargin = insets.left;
+ lp.topMargin = insets.top;
+ lp.bottomMargin = insets.bottom;
+ lp.rightMargin = insets.right;
+ int tooltipLocation = TOOLTIP_DEFAULT;
+
+ if (grid.isVerticalBarLayout()) {
+ lp.width = grid.dropTargetBarSizePx;
+ lp.height = grid.availableHeightPx - 2 * grid.edgeMarginPx;
+ lp.gravity = grid.isSeascape() ? Gravity.RIGHT : Gravity.LEFT;
+ tooltipLocation = grid.isSeascape() ? TOOLTIP_LEFT : TOOLTIP_RIGHT;
+ } else {
+ int gap;
+ if (grid.isTablet) {
+ // XXX: If the icon size changes across orientations, we will have to take
+ // that into account here too.
+ gap = ((grid.widthPx - 2 * grid.edgeMarginPx
+ - (grid.inv.numColumns * grid.cellWidthPx))
+ / (2 * (grid.inv.numColumns + 1)))
+ + grid.edgeMarginPx;
+ } else {
+ gap = grid.desiredWorkspaceLeftRightMarginPx - grid.defaultWidgetPadding.right;
+ }
+ lp.width = grid.availableWidthPx - 2 * gap;
+
+ lp.topMargin += grid.edgeMarginPx;
+ lp.height = grid.dropTargetBarSizePx;
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
+ }
+ setLayoutParams(lp);
+ for (ButtonDropTarget button : mDropTargets) {
+ button.setToolTipLocation(tooltipLocation);
+ }
}
public void setup(DragController dragController) {
dragController.addDragListener(this);
- ArrayList<ButtonDropTarget> outList = new ArrayList<>();
- findDropTargets(this, outList);
-
- mDropTargets = new ButtonDropTarget[outList.size()];
for (int i = 0; i < mDropTargets.length; i++) {
- mDropTargets[i] = outList.get(i);
- mDropTargets[i].setDropTargetBar(this);
dragController.addDragListener(mDropTargets[i]);
dragController.addDropTarget(mDropTargets[i]);
}
}
- private static void findDropTargets(View view, ArrayList<ButtonDropTarget> outTargets) {
- if (view instanceof ButtonDropTarget) {
- outTargets.add((ButtonDropTarget) view);
- } else if (view instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) view;
- for (int i = vg.getChildCount() - 1; i >= 0; i--) {
- findDropTargets(vg.getChildAt(i), outTargets);
- }
- }
- }
-
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
- boolean hideText = hideTextHelper(false /* shouldUpdateText */, false /* no-op */);
- if (hideTextHelper(true /* shouldUpdateText */, hideText)) {
- // Text has changed, so we need to re-measure.
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
+ if (mIsVertical) {
+ int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
+ int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
- /**
- * Helper method that iterates through the children and returns whether any of the visible
- * {@link ButtonDropTarget} has truncated text.
- *
- * @param shouldUpdateText If True, updates the text of all children.
- * @param hideText If True and {@param shouldUpdateText} is True, clears the text of all
- * children; otherwise it sets the original text value.
- *
- *
- * @return If shouldUpdateText is True, returns whether any of the children updated their text.
- * Else, returns whether any of the children have truncated their text.
- */
- private boolean hideTextHelper(boolean shouldUpdateText, boolean hideText) {
- boolean result = false;
- View visibleView;
- ButtonDropTarget dropTarget;
- for (int i = getChildCount() - 1; i >= 0; --i) {
- if (getChildAt(i) instanceof ButtonDropTarget) {
- visibleView = dropTarget = (ButtonDropTarget) getChildAt(i);
- } else if (getChildAt(i) instanceof ViewGroup) {
- // The Drop Target is wrapped in a FrameLayout.
- visibleView = getChildAt(i);
- dropTarget = (ButtonDropTarget) ((ViewGroup) visibleView).getChildAt(0);
- } else {
- // Ignore other views.
- continue;
+ for (ButtonDropTarget button : mDropTargets) {
+ if (button.getVisibility() != GONE) {
+ button.setTextVisible(false);
+ button.measure(widthSpec, heightSpec);
+ }
+ }
+ } else {
+ int visibleCount = getVisibleButtonsCount();
+ int availableWidth = width / visibleCount;
+ boolean textVisible = true;
+ for (ButtonDropTarget buttons : mDropTargets) {
+ if (buttons.getVisibility() != GONE) {
+ textVisible = textVisible && !buttons.isTextTruncated(availableWidth);
+ }
}
- if (visibleView.getVisibility() == View.VISIBLE) {
- if (shouldUpdateText) {
- result |= dropTarget.updateText(hideText);
- } else if (dropTarget.isTextTruncated()) {
- result = true;
- break;
+ int widthSpec = MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST);
+ int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ for (ButtonDropTarget button : mDropTargets) {
+ if (button.getVisibility() != GONE) {
+ button.setTextVisible(textVisible);
+ button.measure(widthSpec, heightSpec);
}
}
}
+ setMeasuredDimension(width, height);
+ }
- return result;
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mIsVertical) {
+ int gap = getResources().getDimensionPixelSize(R.dimen.drop_target_vertical_gap);
+ int start = gap;
+ int end;
+
+ for (ButtonDropTarget button : mDropTargets) {
+ if (button.getVisibility() != GONE) {
+ end = start + button.getMeasuredHeight();
+ button.layout(0, start, button.getMeasuredWidth(), end);
+ start = end + gap;
+ }
+ }
+ } else {
+ int visibleCount = getVisibleButtonsCount();
+ int frameSize = (right - left) / visibleCount;
+
+ int start = frameSize / 2;
+ int halfWidth;
+ for (ButtonDropTarget button : mDropTargets) {
+ if (button.getVisibility() != GONE) {
+ halfWidth = button.getMeasuredWidth() / 2;
+ button.layout(start - halfWidth, 0,
+ start + halfWidth, button.getMeasuredHeight());
+ start = start + frameSize;
+ }
+ }
+ }
+ }
+
+ private int getVisibleButtonsCount() {
+ int visibleCount = 0;
+ for (ButtonDropTarget buttons : mDropTargets) {
+ if (buttons.getVisibility() != GONE) {
+ visibleCount++;
+ }
+ }
+ return visibleCount;
}
private void animateToVisibility(boolean isVisible) {
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 1272e0a..c4ec8c9 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -29,10 +29,12 @@
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;
-import com.android.launcher3.graphics.IconPalette;
+import com.android.launcher3.graphics.BitmapInfo;
public class FastBitmapDrawable extends Drawable {
@@ -40,19 +42,9 @@
private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f;
- public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
+ public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = (input) ->
+ (input < 0.05f) ? (input / 0.05f) : ((input < 0.3f) ? 1 : (1 - input) / 0.7f);
- @Override
- public float getInterpolation(float input) {
- if (input < 0.05f) {
- return input / 0.05f;
- } else if (input < 0.3f){
- return 1;
- } else {
- return (1 - input) / 0.7f;
- }
- }
- };
public static final int CLICK_FEEDBACK_DURATION = 2000;
// Since we don't need 256^2 values for combinations of both the brightness and saturation, we
@@ -69,12 +61,11 @@
protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
private final Bitmap mBitmap;
+ protected final int mIconColor;
private boolean mIsPressed;
private boolean mIsDisabled;
- private IconPalette mIconPalette;
-
private static final Property<FastBitmapDrawable, Float> BRIGHTNESS
= new Property<FastBitmapDrawable, Float>(Float.TYPE, "brightness") {
@Override
@@ -99,7 +90,20 @@
private ObjectAnimator mBrightnessAnimator;
public FastBitmapDrawable(Bitmap b) {
+ this(b, Color.TRANSPARENT);
+ }
+
+ public FastBitmapDrawable(BitmapInfo info) {
+ this(info.icon, info.color);
+ }
+
+ public FastBitmapDrawable(ItemInfoWithIcon info) {
+ this(info.iconBitmap, info.iconColor);
+ }
+
+ protected FastBitmapDrawable(Bitmap b, int iconColor) {
mBitmap = b;
+ mIconColor = iconColor;
setFilterBitmap(true);
}
@@ -108,14 +112,6 @@
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
}
- public IconPalette getIconPalette() {
- if (mIconPalette == null) {
- mIconPalette = IconPalette.fromDominantColor(Utilities
- .findDominantColorByHue(mBitmap, 20), true /* desaturateBackground */);
- }
- return mIconPalette;
- }
-
@Override
public void setColorFilter(ColorFilter cf) {
// No op
@@ -310,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/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 09f9e82..03043f2 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,31 +16,35 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-public class Hotseat extends FrameLayout
- implements UserEventDispatcher.LogContainerProvider {
+public class Hotseat extends FrameLayout implements LogContainerProvider, Insettable {
+ private final Launcher mLauncher;
private CellLayout mContent;
- private Launcher mLauncher;
-
@ViewDebug.ExportedProperty(category = "launcher")
- private final boolean mHasVerticalHotseat;
+ private boolean mHasVerticalHotseat;
public Hotseat(Context context) {
this(context, null);
@@ -53,7 +57,6 @@
public Hotseat(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(context);
- mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
}
public CellLayout getLayout() {
@@ -61,13 +64,6 @@
}
/**
- * Returns whether there are other icons than the all apps button in the hotseat.
- */
- public boolean hasIcons() {
- return mContent.getShortcutsAndWidgets().getChildCount() > 1;
- }
-
- /**
* Registers the specified listener on the cell layout of the hotseat.
*/
@Override
@@ -92,13 +88,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- DeviceProfile grid = mLauncher.getDeviceProfile();
- mContent = (CellLayout) findViewById(R.id.layout);
- if (grid.isVerticalBarLayout()) {
- mContent.setGridSize(1, grid.inv.numHotseatIcons);
- } else {
- mContent.setGridSize(grid.inv.numHotseatIcons, 1);
- }
+ mContent = findViewById(R.id.layout);
resetLayout();
}
@@ -127,8 +117,13 @@
allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
if (mLauncher != null) {
- mLauncher.setAllAppsButton(allAppsButton);
- allAppsButton.setOnClickListener(mLauncher);
+ allAppsButton.setOnClickListener((v) -> {
+ if (!mLauncher.isInState(ALL_APPS)) {
+ mLauncher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.ALL_APPS_BUTTON);
+ mLauncher.getStateManager().goToState(ALL_APPS);
+ }
+ });
allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
}
@@ -156,4 +151,52 @@
target.gridY = info.cellY;
targetParent.containerType = ContainerType.HOTSEAT;
}
+
+ @Override
+ public void setInsets(Rect insets) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
+
+ if (mHasVerticalHotseat) {
+ mContent.setGridSize(1, grid.inv.numHotseatIcons);
+
+ lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ if (grid.isSeascape()) {
+ lp.gravity = Gravity.LEFT;
+ lp.width = grid.hotseatBarSizePx + insets.left + grid.hotseatBarSidePaddingPx;
+ getLayout().setPadding(
+ insets.left, insets.top, grid.hotseatBarSidePaddingPx, insets.bottom);
+
+ } else {
+ lp.gravity = Gravity.RIGHT;
+ lp.width = grid.hotseatBarSizePx + insets.right + grid.hotseatBarSidePaddingPx;
+ getLayout().setPadding(
+ grid.hotseatBarSidePaddingPx, insets.top, insets.right, insets.bottom);
+ }
+ } else {
+ mContent.setGridSize(grid.inv.numHotseatIcons, 1);
+
+ lp.gravity = Gravity.BOTTOM;
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ lp.height = grid.hotseatBarSizePx + insets.bottom;
+
+ // We want the edges of the hotseat to line up with the edges of the workspace, but the
+ // icons in the hotseat are a different size, and so don't line up perfectly. To account for
+ // this, we pad the left and right of the hotseat with half of the difference of a workspace
+ // cell vs a hotseat cell.
+ float workspaceCellWidth = (float) grid.widthPx / grid.inv.numColumns;
+ float hotseatCellWidth = (float) grid.widthPx / grid.inv.numHotseatIcons;
+ int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
+ Rect workspacePadding = grid.workspacePadding;
+
+ getLayout().setPadding(
+ hotseatAdjustment + workspacePadding.left + grid.cellLayoutPaddingLeftRightPx,
+ grid.hotseatBarTopPaddingPx,
+ hotseatAdjustment + workspacePadding.right + grid.cellLayoutPaddingLeftRightPx,
+ grid.hotseatBarBottomPaddingPx + insets.bottom + grid.cellLayoutBottomPaddingPx);
+ }
+ setLayoutParams(lp);
+ InsettableFrameLayout.dispatchInsets(this, insets);
+ }
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 573e8a2..d0581a2 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -33,25 +33,31 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
-import android.os.Build;
+import android.os.Build.VERSION;
import android.os.Handler;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
import android.support.annotation.NonNull;
+import android.support.v4.graphics.ColorUtils;
import android.text.TextUtils;
import android.util.Log;
+
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
+import com.android.launcher3.graphics.ColorExtractor;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
import com.android.launcher3.util.Thunk;
+
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -78,14 +84,13 @@
@Thunk static final Object ICON_UPDATE_TOKEN = new Object();
- public static class CacheEntry {
- public Bitmap icon;
+ public static class CacheEntry extends BitmapInfo {
public CharSequence title = "";
public CharSequence contentDescription = "";
public boolean isLowResIcon;
}
- private final HashMap<UserHandle, Bitmap> mDefaultIcons = new HashMap<>();
+ private final HashMap<UserHandle, BitmapInfo> mDefaultIcons = new HashMap<>();
@Thunk final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
private final Context mContext;
@@ -102,6 +107,7 @@
@Thunk final Handler mWorkerHandler;
private final BitmapFactory.Options mLowResOptions;
+ private final BitmapFactory.Options mHighResOptions;
public IconCache(Context context, InvariantDeviceProfile inv) {
mContext = context;
@@ -112,14 +118,20 @@
mIconDpi = inv.fillResIconDpi;
mIconDb = new IconDB(context, inv.iconBitmapSize);
- mIconProvider = Utilities.getOverrideObject(
- IconProvider.class, context, R.string.icon_provider_class);
+ mIconProvider = IconProvider.newInstance(context);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
mLowResOptions = new BitmapFactory.Options();
// Always prefer RGB_565 config for low res. If the bitmap has transparency, it will
// automatically be loaded as ALPHA_8888.
mLowResOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+
+ if (UiFactory.USE_HARDWARE_BITMAP) {
+ mHighResOptions = new BitmapFactory.Options();
+ mHighResOptions.inPreferredConfig = Bitmap.Config.HARDWARE;
+ } else {
+ mHighResOptions = null;
+ }
}
private Drawable getFullResDefaultActivityIcon() {
@@ -179,9 +191,11 @@
return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
- protected Bitmap makeDefaultIcon(UserHandle user) {
- Drawable unbadged = getFullResDefaultActivityIcon();
- return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext, Build.VERSION_CODES.O);
+ protected BitmapInfo makeDefaultIcon(UserHandle user) {
+ try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+ return li.createBadgedIconBitmap(
+ getFullResDefaultActivityIcon(), user, VERSION.SDK_INT);
+ }
}
/**
@@ -239,7 +253,7 @@
// Remove all active icon update tasks.
mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
- mIconProvider.updateSystemStateString();
+ mIconProvider.updateSystemStateString(mContext);
for (UserHandle user : mUserManager.getUserProfiles()) {
// Query for the set of apps
final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
@@ -365,16 +379,18 @@
}
if (entry == null) {
entry = new CacheEntry();
- entry.icon = LauncherIcons.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
- mContext, app.getApplicationInfo().targetSdkVersion);
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ li.createBadgedIconBitmap(getFullResIcon(app), app.getUser(),
+ app.getApplicationInfo().targetSdkVersion).applyTo(entry);
+ li.recycle();
}
entry.title = app.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(key, entry);
Bitmap lowResIcon = generateLowResIcon(entry.icon);
- ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
- app.getApplicationInfo().packageName);
+ ContentValues values = newContentValues(entry.icon, lowResIcon, entry.color,
+ entry.title.toString(), app.getApplicationInfo().packageName);
addIconToDB(values, app.getComponentName(), info, userSerial);
}
@@ -448,7 +464,7 @@
// null info means not installed, but if we have a component from the intent then
// we should still look in the cache for restored app icons.
if (info.getTargetComponent() == null) {
- info.iconBitmap = getDefaultIcon(info.user);
+ getDefaultIcon(info.user).applyTo(info);
info.title = "";
info.contentDescription = "";
info.usingLowResIcon = false;
@@ -483,11 +499,11 @@
private void applyCacheEntry(CacheEntry entry, ItemInfoWithIcon info) {
info.title = Utilities.trim(entry.title);
info.contentDescription = entry.contentDescription;
- info.iconBitmap = entry.icon == null ? getDefaultIcon(info.user) : entry.icon;
info.usingLowResIcon = entry.isLowResIcon;
+ ((entry.icon == null) ? getDefaultIcon(info.user) : entry).applyTo(info);
}
- public synchronized Bitmap getDefaultIcon(UserHandle user) {
+ public synchronized BitmapInfo getDefaultIcon(UserHandle user) {
if (!mDefaultIcons.containsKey(user)) {
mDefaultIcons.put(user, makeDefaultIcon(user));
}
@@ -495,7 +511,7 @@
}
public boolean isDefaultIcon(Bitmap icon, UserHandle user) {
- return mDefaultIcons.get(user) == icon;
+ return getDefaultIcon(user).icon == icon;
}
/**
@@ -522,9 +538,10 @@
providerFetchedOnce = true;
if (info != null) {
- entry.icon = LauncherIcons.createBadgedIconBitmap(
- getFullResIcon(info), info.getUser(), mContext,
- infoProvider.get().getApplicationInfo().targetSdkVersion);
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ li.createBadgedIconBitmap(getFullResIcon(info), info.getUser(),
+ info.getApplicationInfo().targetSdkVersion).applyTo(entry);
+ li.recycle();
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@@ -532,7 +549,7 @@
if (packageEntry != null) {
if (DEBUG) Log.d(TAG, "using package default icon for " +
componentName.toShortString());
- entry.icon = packageEntry.icon;
+ packageEntry.applyTo(entry);
entry.title = packageEntry.title;
entry.contentDescription = packageEntry.contentDescription;
}
@@ -540,7 +557,7 @@
if (entry.icon == null) {
if (DEBUG) Log.d(TAG, "using default icon for " +
componentName.toShortString());
- entry.icon = getDefaultIcon(user);
+ getDefaultIcon(user).applyTo(entry);
}
}
}
@@ -583,7 +600,9 @@
entry.title = title;
}
if (icon != null) {
- entry.icon = LauncherIcons.createIconBitmap(icon, mContext);
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ li.createIconBitmap(icon).applyTo(entry);
+ li.recycle();
}
if (!TextUtils.isEmpty(title) && entry.icon != null) {
mCache.put(cacheKey, entry);
@@ -620,24 +639,28 @@
throw new NameNotFoundException("ApplicationInfo is null");
}
+ LauncherIcons li = LauncherIcons.obtain(mContext);
// Load the full res icon for the application, but if useLowResIcon is set, then
// only keep the low resolution icon instead of the larger full-sized icon
- Bitmap icon = LauncherIcons.createBadgedIconBitmap(
- appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
+ BitmapInfo iconInfo = li.createBadgedIconBitmap(
+ appInfo.loadIcon(mPackageManager), user, appInfo.targetSdkVersion);
if (mInstantAppResolver.isInstantApp(appInfo)) {
- icon = LauncherIcons.badgeWithDrawable(icon,
- mContext.getDrawable(R.drawable.ic_instant_app_badge), mContext);
+ li.badgeWithDrawable(iconInfo.icon,
+ mContext.getDrawable(R.drawable.ic_instant_app_badge));
}
- Bitmap lowResIcon = generateLowResIcon(icon);
+ li.recycle();
+
+ Bitmap lowResIcon = generateLowResIcon(iconInfo.icon);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
- entry.icon = useLowResIcon ? lowResIcon : icon;
+ entry.icon = useLowResIcon ? lowResIcon : iconInfo.icon;
+ entry.color = iconInfo.color;
entry.isLowResIcon = useLowResIcon;
// Add the icon in the DB here, since these do not get written during
// package updates.
- ContentValues values =
- newContentValues(icon, lowResIcon, entry.title.toString(), packageName);
+ ContentValues values = newContentValues(iconInfo.icon, lowResIcon, entry.color,
+ entry.title.toString(), packageName);
addIconToDB(values, cacheKey.componentName, info,
mUserManager.getSerialNumberForUser(user));
@@ -660,14 +683,16 @@
try {
c = mIconDb.query(
new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
- IconDB.COLUMN_LABEL},
+ IconDB.COLUMN_ICON_COLOR, IconDB.COLUMN_LABEL},
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
new String[]{cacheKey.componentName.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
- entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : null);
+ entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : mHighResOptions);
+ // Set the alpha to be 255, so that we never have a wrong color
+ entry.color = ColorUtils.setAlphaComponent(c.getInt(1), 255);
entry.isLowResIcon = lowRes;
- entry.title = c.getString(1);
+ entry.title = c.getString(2);
if (entry.title == null) {
entry.title = "";
entry.contentDescription = "";
@@ -760,10 +785,7 @@
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int DB_VERSION = 17;
-
- private final static int RELEASE_VERSION = DB_VERSION +
- (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
+ private final static int RELEASE_VERSION = 21;
private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid";
@@ -773,6 +795,7 @@
private final static String COLUMN_VERSION = "version";
private final static String COLUMN_ICON = "icon";
private final static String COLUMN_ICON_LOW_RES = "icon_low_res";
+ private final static String COLUMN_ICON_COLOR = "icon_color";
private final static String COLUMN_LABEL = "label";
private final static String COLUMN_SYSTEM_STATE = "system_state";
@@ -791,6 +814,7 @@
COLUMN_VERSION + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_ICON + " BLOB, " +
COLUMN_ICON_LOW_RES + " BLOB, " +
+ COLUMN_ICON_COLOR + " INTEGER NOT NULL DEFAULT 0, " +
COLUMN_LABEL + " TEXT, " +
COLUMN_SYSTEM_STATE + " TEXT, " +
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
@@ -798,11 +822,12 @@
}
}
- private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label,
- String packageName) {
+ private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, int iconColor,
+ String label, String packageName) {
ContentValues values = new ContentValues();
values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
+ values.put(IconDB.COLUMN_ICON_COLOR, iconColor);
values.put(IconDB.COLUMN_LABEL, label);
values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index 4dee2b5..b469a8f 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -1,5 +1,6 @@
package com.android.launcher3;
+import android.content.Context;
import android.content.pm.LauncherActivityInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -8,17 +9,26 @@
public class IconProvider {
- private static final boolean DBG = false;
- private static final String TAG = "IconProvider";
-
protected String mSystemState;
- public IconProvider() {
- updateSystemStateString();
+ public static IconProvider newInstance(Context context) {
+ IconProvider provider = Utilities.getOverrideObject(
+ IconProvider.class, context, R.string.icon_provider_class);
+ provider.updateSystemStateString(context);
+ return provider;
}
- public void updateSystemStateString() {
- mSystemState = Locale.getDefault().toString() + "," + Build.VERSION.SDK_INT;
+ public IconProvider() { }
+
+ public void updateSystemStateString(Context context) {
+ final String locale;
+ if (Utilities.ATLEAST_NOUGAT) {
+ locale = context.getResources().getConfiguration().getLocales().toLanguageTags();
+ } else {
+ locale = Locale.getDefault().toString();
+ }
+
+ mSystemState = locale + "," + Build.VERSION.SDK_INT;
}
public String getIconSystemState(String packageName) {
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 289242f..e52fd76 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -70,7 +70,7 @@
Rect sourceBounds, Bundle opts) {
if (info instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
- context.startActivity(promiseAppInfo.getMarketIntent());
+ context.startActivity(promiseAppInfo.getMarketIntent(context));
return null;
}
ComponentName componentName = null;
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 60f5ca2..1db1fc0 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -38,10 +38,6 @@
@Override
public void setInsets(Rect insets) {
- // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
- // modifying child layout params.
- if (insets.equals(mInsets)) return;
-
final int n = getChildCount();
for (int i = 0; i < n; i++) {
final View child = getChildAt(i);
@@ -97,4 +93,14 @@
super.onViewAdded(child);
setFrameLayoutChildInsets(child, mInsets, new Rect());
}
+
+ public static void dispatchInsets(ViewGroup parent, Rect insets) {
+ final int n = parent.getChildCount();
+ for (int i = 0; i < n; i++) {
+ final View child = parent.getChildAt(i);
+ if (child instanceof Insettable) {
+ ((Insettable) child).setInsets(insets);
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index df1eec6..fe8a841 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -40,6 +40,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -480,7 +481,7 @@
final LauncherAppState app = LauncherAppState.getInstance(mContext);
// Set default values until proper values is loaded.
appInfo.title = "";
- appInfo.iconBitmap = app.getIconCache().getDefaultIcon(user);
+ app.getIconCache().getDefaultIcon(user).applyTo(appInfo);
final ShortcutInfo si = appInfo.makeShortcut();
if (Looper.myLooper() == LauncherModel.getWorkerLooper()) {
app.getIconCache().getTitleAndIcon(si, activityInfo, false /* useLowResIcon */);
@@ -497,7 +498,9 @@
return Pair.create((ItemInfo) si, (Object) activityInfo);
} else if (shortcutInfo != null) {
ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
- si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ li.createShortcutIcon(shortcutInfo).applyTo(si);
+ li.recycle();
return Pair.create((ItemInfo) si, (Object) shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
@@ -641,18 +644,23 @@
// users wouldn't get here without intent forwarding anyway.
info.user = Process.myUserHandle();
+ BitmapInfo iconInfo = null;
+ LauncherIcons li = LauncherIcons.obtain(app.getContext());
if (bitmap instanceof Bitmap) {
- info.iconBitmap = LauncherIcons.createIconBitmap((Bitmap) bitmap, app.getContext());
+ iconInfo = li.createIconBitmap((Bitmap) bitmap);
} else {
Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
if (extra instanceof Intent.ShortcutIconResource) {
info.iconResource = (Intent.ShortcutIconResource) extra;
- info.iconBitmap = LauncherIcons.createIconBitmap(info.iconResource, app.getContext());
+ iconInfo = li.createIconBitmap(info.iconResource);
}
}
- if (info.iconBitmap == null) {
- info.iconBitmap = app.getIconCache().getDefaultIcon(info.user);
+ li.recycle();
+
+ if (iconInfo == null) {
+ iconInfo = app.getIconCache().getDefaultIcon(info.user);
}
+ iconInfo.applyTo(info);
info.title = Utilities.trim(name);
info.contentDescription = UserManagerCompat.getInstance(app.getContext())
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 7a43198..e460911 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -65,12 +65,6 @@
public int numColumns;
/**
- * The minimum number of predicted apps in all apps.
- */
- @Deprecated
- int minAllAppsPredictionColumns;
-
- /**
* Number of icons per row and column in the folder.
*/
public int numFolderRows;
@@ -99,12 +93,12 @@
public InvariantDeviceProfile(InvariantDeviceProfile p) {
this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
- p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
+ p.numFolderRows, p.numFolderColumns,
p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
p.defaultLayoutId, p.demoModeLayoutId);
}
- InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
+ InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc,
float is, float lis, float its, int hs, int dlId, int dmlId) {
name = n;
minWidthDps = w;
@@ -113,7 +107,6 @@
numColumns = c;
numFolderRows = fr;
numFolderColumns = fc;
- minAllAppsPredictionColumns = maapc;
iconSize = is;
landscapeIconSize = lis;
iconTextSize = its;
@@ -150,7 +143,6 @@
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
- minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
iconSize = interpolatedDeviceProfileOut.iconSize;
landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
@@ -170,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
@@ -207,7 +199,6 @@
numColumns,
a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
- a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
iconSize,
a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index fea4dda..bf985c3 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -29,6 +29,11 @@
public Bitmap iconBitmap;
/**
+ * Dominant color in the {@link #iconBitmap}.
+ */
+ public int iconColor;
+
+ /**
* Indicates whether we're using a low res icon
*/
public boolean usingLowResIcon;
@@ -96,6 +101,7 @@
protected ItemInfoWithIcon(ItemInfoWithIcon info) {
super(info);
iconBitmap = info.iconBitmap;
+ iconColor = info.iconColor;
usingLowResIcon = info.usingLowResIcon;
runtimeStatusFlags = info.runtimeStatusFlags;
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b7986da..55bfef6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,9 +16,10 @@
package com.android.launcher3;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
@@ -29,8 +30,6 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_APPS;
-import static com.android.launcher3.util.RunnableWithId.RUNNABLE_ID_BIND_WIDGETS;
import android.Manifest;
import android.animation.Animator;
@@ -42,13 +41,11 @@
import android.annotation.TargetApi;
import android.app.ActivityOptions;
import android.app.AlertDialog;
-import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
@@ -58,11 +55,11 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
+import android.content.res.Configuration;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@@ -71,12 +68,11 @@
import android.os.StrictMode;
import android.os.UserHandle;
import android.support.annotation.Nullable;
-import android.text.Selection;
-import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.Log;
import android.util.SparseArray;
+import android.view.ActionMode;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -90,7 +86,6 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
-import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
@@ -99,10 +94,10 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.LauncherAppsCompatVO;
-import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
@@ -117,17 +112,14 @@
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.notification.NotificationListener;
-import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.states.AllAppsState;
import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ActivityResultInfo;
import com.android.launcher3.util.ComponentKey;
@@ -136,15 +128,16 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
-import com.android.launcher3.util.RunnableWithId;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.widget.WidgetAddFlowHandler;
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetListRowEntry;
@@ -158,7 +151,6 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.Executor;
/**
* Default launcher application.
@@ -207,6 +199,10 @@
// Type: SparseArray<Parcelable>
private static final String RUNTIME_STATE_WIDGET_PANEL = "launcher.widget_panel";
+ // When starting an action mode, setting this tag will cause the action mode to be cancelled
+ // automatically when user interacts with the launcher.
+ public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
+
private LauncherStateManager mStateManager;
private boolean mIsSafeModeEnabled;
@@ -218,6 +214,9 @@
private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
@Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+ private LauncherAppTransitionManager mAppTransitionManager;
+ private Configuration mOldConfig;
+
@Thunk Workspace mWorkspace;
private View mLauncherView;
@Thunk DragLayer mDragLayer;
@@ -230,8 +229,6 @@
@Thunk Hotseat mHotseat;
- private View mAllAppsButton;
-
private DropTargetBar mDropTargetBar;
// Main container view for the all apps screen.
@@ -241,18 +238,9 @@
// UI and state for the overview panel
private ViewGroup mOverviewPanel;
- // We need to store the orientation Launcher was created with, due to a bug (b/64916689)
- // that results in widgets being inflated in the wrong orientation.
- private int mOrientation;
-
- private SpannableStringBuilder mDefaultKeySsb = null;
-
@Thunk boolean mWorkspaceLoading = true;
- private boolean mPaused = true;
- private boolean mOnResumeNeedsLoad;
-
- private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
+ private OnStartCallback mOnStartCallback;
private OnResumeCallback mOnResumeCallback;
private ViewOnDrawExecutor mPendingExecutor;
@@ -261,14 +249,13 @@
private ModelWriter mModelWriter;
private IconCache mIconCache;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
- private boolean mHasFocus = false;
private ObjectAnimator mScrimAnimator;
private boolean mShouldFadeInScrim;
private PopupDataProvider mPopupDataProvider;
- private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
+ private int mSynchronouslyBoundPage = PagedView.INVALID_PAGE;
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -282,13 +269,14 @@
*/
private PendingRequestArgs mPendingRequestArgs;
- private float mLastDispatchTouchEventX = 0.0f;
+ private final PointF mLastDispatchTouchEvent = new PointF();
public ViewGroupFocusHelper mFocusHandler;
private boolean mRotationEnabled = false;
private boolean mAppLaunchSuccess;
private RotationPrefChangeHandler mRotationPrefChangeHandler;
+ private ActionMode mCurrentActionMode;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -308,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());
@@ -320,21 +304,12 @@
TraceHelper.partitionSection("Launcher-onCreate", "super call");
LauncherAppState app = LauncherAppState.getInstance(this);
+ mOldConfig = new Configuration(getResources().getConfiguration());
+ mModel = app.setLauncher(this);
+ initDeviceProfile(app.getInvariantDeviceProfile());
- // Load configuration-specific DeviceProfile
- mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
- if (isInMultiWindowModeCompat()) {
- Display display = getWindowManager().getDefaultDisplay();
- Point mwSize = new Point();
- display.getSize(mwSize);
- mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
- }
-
- mOrientation = getResources().getConfiguration().orientation;
mSharedPrefs = Utilities.getPrefs(this);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
- mModel = app.setLauncher(this);
- mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -347,16 +322,9 @@
mAppWidgetHost = new LauncherAppWidgetHost(this);
mAppWidgetHost.startListening();
- // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
- // this also ensures that any synchronous binding below doesn't re-trigger another
- // LauncherModel load.
- mPaused = false;
-
mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
setupViews();
- mDeviceProfile.layout(this, false /* notifyListeners */);
-
mPopupDataProvider = new PopupDataProvider(this);
mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
@@ -402,15 +370,11 @@
}
// For handling default keys
- mDefaultKeySsb = new SpannableStringBuilder();
- Selection.setSelection(mDefaultKeySsb, 0);
+ 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();
// Listen for broadcasts
IntentFilter filter = new IntentFilter();
@@ -422,6 +386,8 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+ mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
+
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
@@ -429,6 +395,48 @@
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);
+ if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
+ mUserEventDispatcher = null;
+ initDeviceProfile(mDeviceProfile.inv);
+ dispatchDeviceProfileChanged();
+
+ getRootView().dispatchInsets();
+ getStateManager().reapplyState();
+
+ // TODO: We can probably avoid rebind when only screen size changed.
+ int currentPage = mWorkspace.getNextPage();
+ if (mModel.startLoader(currentPage)) {
+ mWorkspace.setCurrentPage(currentPage);
+ setWorkspaceLoading(true);
+ }
+ }
+
+ mOldConfig.setTo(newConfig);
+ super.onConfigurationChanged(newConfig);
+ }
+
+ private void initDeviceProfile(InvariantDeviceProfile idp) {
+ // Load configuration-specific DeviceProfile
+ mDeviceProfile = idp.getDeviceProfile(this);
+ if (isInMultiWindowModeCompat()) {
+ Display display = getWindowManager().getDefaultDisplay();
+ Point mwSize = new Point();
+ display.getSize(mwSize);
+ mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
+ }
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
+ }
+
@Override
public void onThemeChanged() {
recreate();
@@ -438,6 +446,10 @@
return mStateManager;
}
+ public LauncherAppTransitionManager getAppTransitionManager() {
+ return mAppTransitionManager;
+ }
+
protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
if (isDark) {
setTheme(R.style.LauncherThemeDark);
@@ -460,11 +472,6 @@
private LauncherCallbacks mLauncherCallbacks;
- public void onInsetsChanged(Rect insets) {
- mDeviceProfile.updateInsets(insets);
- mDeviceProfile.layout(this, true /* notifyListeners */);
- }
-
/**
* Call this after onCreate to set or clear overlay.
*/
@@ -778,9 +785,10 @@
if (!mAppLaunchSuccess) {
getUserEventDispatcher().logActionCommand(Action.Command.STOP,
- mStateManager.getState().containerType);
+ mStateManager.getState().containerType, -1);
}
NotificationListener.removeNotificationsChangedListener();
+ getStateManager().moveToRestState();
}
@Override
@@ -788,21 +796,22 @@
super.onStart();
FirstFrameAnimatorHelper.setIsVisible(true);
+ if (mOnStartCallback != null) {
+ mOnStartCallback.onLauncherStart(this);
+ mOnStartCallback = null;
+ }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStart();
}
mAppWidgetHost.setListenIfResumed(true);
+ NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
- if (!isWorkspaceLoading()) {
- NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
- }
-
- if (mShouldFadeInScrim && mDragLayer.getBackground() != null) {
+ if (mShouldFadeInScrim && mLauncherView.getBackground() != null) {
if (mScrimAnimator != null) {
mScrimAnimator.cancel();
}
- mDragLayer.getBackground().setAlpha(0);
- mScrimAnimator = ObjectAnimator.ofInt(mDragLayer.getBackground(),
+ mLauncherView.getBackground().setAlpha(0);
+ mScrimAnimator = ObjectAnimator.ofInt(mLauncherView.getBackground(),
LauncherAnimUtils.DRAWABLE_ALPHA, 0, 255);
mScrimAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -825,21 +834,6 @@
mAppLaunchSuccess = false;
getUserEventDispatcher().resetElapsedSessionMillis();
- mPaused = false;
- if (mOnResumeNeedsLoad) {
- setWorkspaceLoading(true);
- mModel.startLoader(getCurrentWorkspaceScreen());
- mOnResumeNeedsLoad = false;
- }
- if (mBindOnResumeCallbacks.size() > 0) {
- // We might have postponed some bind calls until onResume (see waitUntilResume) --
- // execute them here
- for (int i = 0; i < mBindOnResumeCallbacks.size(); i++) {
- mBindOnResumeCallbacks.get(i).run();
- }
- mBindOnResumeCallbacks.clear();
- }
-
setOnResumeCallback(null);
// Process any items that were added while Launcher was away.
InstallShortcutReceiver.disableAndFlushInstallQueue(
@@ -848,15 +842,11 @@
// Refresh shortcuts if the permission changed.
mModel.refreshShortcutsIfRequired();
- if (shouldShowDiscoveryBounce()) {
- mAllAppsController.showDiscoveryBounce();
- }
+ DiscoveryBounce.showIfNeeded(this);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onResume();
}
- clearTypedText();
-
TraceHelper.endSection("ON_RESUME");
}
@@ -866,7 +856,6 @@
InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
super.onPause();
- mPaused = true;
mDragController.cancelDrag();
mDragController.resetLastGestureUpTime();
@@ -875,6 +864,12 @@
}
}
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ mStateManager.onWindowFocusChanged();
+ }
+
public interface LauncherOverlay {
/**
@@ -924,72 +919,6 @@
}
}
- @Override
- public Object onRetainNonConfigurationInstance() {
- // Flag the loader to stop early before switching
- if (mModel.isCurrentCallbacks(this)) {
- mModel.stopLoader();
- }
- //TODO(hyunyoungs): stop the widgets loader when there is a rotation.
-
- return Boolean.TRUE;
- }
-
- // We can't hide the IME if it was forced open. So don't bother
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- mHasFocus = hasFocus;
-
- if (mLauncherCallbacks != null) {
- mLauncherCallbacks.onWindowFocusChanged(hasFocus);
- }
- }
-
- private boolean acceptFilter() {
- final InputMethodManager inputManager = (InputMethodManager)
- getSystemService(Context.INPUT_METHOD_SERVICE);
- return !inputManager.isFullscreenMode();
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- final int uniChar = event.getUnicodeChar();
- final boolean handled = super.onKeyDown(keyCode, event);
- final boolean isKeyNotWhitespace = uniChar > 0 && !Character.isWhitespace(uniChar);
- if (!handled && acceptFilter() && isKeyNotWhitespace) {
- boolean gotKey = TextKeyListener.getInstance().onKeyDown(mWorkspace, mDefaultKeySsb,
- keyCode, event);
- if (gotKey && mDefaultKeySsb != null && mDefaultKeySsb.length() > 0) {
- // something usable has been typed - start a search
- // the typed text will be retrieved and cleared by
- // showSearchDialog()
- // If there are multiple keystrokes before the search dialog takes focus,
- // onSearchRequested() will be called for every keystroke,
- // but it is idempotent, so it's fine.
- return onSearchRequested();
- }
- }
-
- // Eat the long press event so the keyboard doesn't come up.
- if (keyCode == KeyEvent.KEYCODE_MENU && event.isLongPress()) {
- return true;
- }
-
- return handled;
- }
-
- private String getTypedText() {
- return mDefaultKeySsb.toString();
- }
-
- @Override
- public void clearTypedText() {
- mDefaultKeySsb.clear();
- mDefaultKeySsb.clearSpans();
- Selection.setSelection(mDefaultKeySsb, 0);
- }
-
public boolean isInState(LauncherState state) {
return mStateManager.getState() == state;
}
@@ -1007,7 +936,7 @@
int stateOrdinal = savedState.getInt(RUNTIME_STATE, NORMAL.ordinal);
LauncherState[] stateValues = LauncherState.values();
LauncherState state = stateValues[stateOrdinal];
- if (!state.doNotRestore) {
+ if (!state.disableRestore) {
mStateManager.goToState(state, false /* animated */);
}
@@ -1029,7 +958,7 @@
* Finds all the views we need and configure them properly.
*/
private void setupViews() {
- mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
+ mDragLayer = findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
@@ -1040,7 +969,7 @@
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
// Setup the drag layer
- mDragLayer.setup(this, mDragController, mAllAppsController);
+ mDragLayer.setup(this, mDragController);
// Setup the hotseat
mHotseat = (Hotseat) findViewById(R.id.hotseat);
@@ -1072,14 +1001,6 @@
}
/**
- * Sets the all apps button. This method is called from {@link Hotseat}.
- * TODO: Get rid of this.
- */
- public void setAllAppsButton(View allAppsButton) {
- mAllAppsButton = allAppsButton;
- }
-
- /**
* Creates a view representing a shortcut.
*
* @param info The data structure describing the shortcut.
@@ -1254,20 +1175,12 @@
};
public void updateIconBadges(final Set<PackageUserKey> updatedBadges) {
- Runnable r = new Runnable() {
- @Override
- public void run() {
- mWorkspace.updateIconBadges(updatedBadges);
- mAppsView.updateIconBadges(updatedBadges);
+ mWorkspace.updateIconBadges(updatedBadges);
+ mAppsView.getAppsStore().updateIconBadges(updatedBadges);
- PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
- if (popup != null) {
- popup.updateNotificationHeader(updatedBadges);
- }
- }
- };
- if (!waitUntilResume(r)) {
- r.run();
+ PopupContainerWithArrow popup = PopupContainerWithArrow.getOpen(Launcher.this);
+ if (popup != null) {
+ popup.updateNotificationHeader(updatedBadges);
}
}
@@ -1290,10 +1203,20 @@
}
}
+ public void onQuickstepGestureStarted(boolean isVisible) {
+ if (mLauncherCallbacks != null) {
+ mLauncherCallbacks.onQuickstepGestureStarted(isVisible);
+ }
+ }
+
public AllAppsTransitionController getAllAppsController() {
return mAllAppsController;
}
+ public LauncherRootView getRootView() {
+ return (LauncherRootView) mLauncherView;
+ }
+
public DragLayer getDragLayer() {
return mDragLayer;
}
@@ -1334,14 +1257,14 @@
return mSharedPrefs;
}
- public int getOrientation() { return mOrientation; }
+ public int getOrientation() { return mOldConfig.orientation; }
@Override
protected void onNewIntent(Intent intent) {
TraceHelper.beginSection("NEW_INTENT");
super.onNewIntent(intent);
- boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
+ boolean alreadyOnHome = hasWindowFocus() && ((intent.getFlags() &
Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
!= Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
@@ -1350,7 +1273,7 @@
&& AbstractFloatingView.getTopOpenView(this) == null;
boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
boolean internalStateHandled = InternalStateHandler
- .handleNewIntent(this, intent, alreadyOnHome);
+ .handleNewIntent(this, intent, isStarted());
if (isActionMain) {
if (!internalStateHandled) {
@@ -1363,13 +1286,14 @@
} else if (alreadyOnHome) {
Target target = newContainerTarget(mStateManager.getState().containerType);
target.pageIndex = mWorkspace.getCurrentPage();
- ued.logActionCommand(Action.Command.HOME_INTENT, target);
+ ued.logActionCommand(Action.Command.HOME_INTENT, target,
+ newContainerTarget(ContainerType.WORKSPACE));
}
// In all these cases, only animate if we're already on home
- AbstractFloatingView.closeAllOpenViews(this, alreadyOnHome);
+ AbstractFloatingView.closeAllOpenViews(this, isStarted());
- mStateManager.goToState(NORMAL, alreadyOnHome /* animated */);
+ mStateManager.goToState(NORMAL);
// Reset the apps view
if (!alreadyOnHome && mAppsView != null) {
@@ -1397,9 +1321,7 @@
@Override
public void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
- for (int page: mSynchronouslyBoundPages) {
- mWorkspace.restoreInstanceStateForChild(page);
- }
+ mWorkspace.restoreInstanceStateForChild(mSynchronouslyBoundPage);
}
@Override
@@ -1463,7 +1385,6 @@
} catch (NullPointerException ex) {
Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
}
- mAppWidgetHost = null;
TextKeyListener.getInstance().release();
WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
@@ -1508,11 +1429,6 @@
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, boolean globalSearch) {
-
- if (initialQuery == null) {
- // Use any text typed in the launcher as the initial query
- initialQuery = getTypedText();
- }
if (appSearchData == null) {
appSearchData = new Bundle();
appSearchData.putString("source", "launcher-search");
@@ -1521,69 +1437,13 @@
if (mLauncherCallbacks == null ||
!mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) {
// Starting search from the callbacks failed. Start the default global search.
- startGlobalSearch(initialQuery, selectInitialQuery, appSearchData, null);
+ super.startSearch(initialQuery, selectInitialQuery, appSearchData, true);
}
// We need to show the workspace after starting the search
mStateManager.goToState(NORMAL);
}
- /**
- * Starts the global search activity. This code is a copied from SearchManager
- */
- public void startGlobalSearch(String initialQuery,
- boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
- final SearchManager searchManager =
- (SearchManager) getSystemService(Context.SEARCH_SERVICE);
- ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
- if (globalSearchActivity == null) {
- Log.w(TAG, "No global search activity found.");
- return;
- }
- Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setComponent(globalSearchActivity);
- // Make sure that we have a Bundle to put source in
- if (appSearchData == null) {
- appSearchData = new Bundle();
- } else {
- appSearchData = new Bundle(appSearchData);
- }
- // Set source to package name of app that starts global search if not set already.
- if (!appSearchData.containsKey("source")) {
- appSearchData.putString("source", getPackageName());
- }
- intent.putExtra(SearchManager.APP_DATA, appSearchData);
- if (!TextUtils.isEmpty(initialQuery)) {
- intent.putExtra(SearchManager.QUERY, initialQuery);
- }
- if (selectInitialQuery) {
- intent.putExtra(SearchManager.EXTRA_SELECT_QUERY, selectInitialQuery);
- }
- intent.setSourceBounds(sourceBounds);
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException ex) {
- Log.e(TAG, "Global search activity not found: " + globalSearchActivity);
- }
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
- super.onPrepareOptionsMenu(menu);
- if (mLauncherCallbacks != null) {
- return mLauncherCallbacks.onPrepareOptionsMenu(menu);
- }
- return false;
- }
-
- @Override
- public boolean onSearchRequested() {
- startSearch(null, false, null, true);
- // Use a custom animation for launching search
- return true;
- }
-
public boolean isWorkspaceLocked() {
return mWorkspaceLoading || mPendingRequestArgs != null;
}
@@ -1783,6 +1643,9 @@
@Override
public void onBackPressed() {
+ if (finishAutoCancelActionMode()) {
+ return;
+ }
if (mLauncherCallbacks != null && mLauncherCallbacks.handleBackPressed()) {
return;
}
@@ -1799,8 +1662,10 @@
if (topView != null) {
topView.onBackPressed();
} else if (!isInState(NORMAL)) {
- ued.logActionCommand(Action.Command.BACK, mStateManager.getState().containerType);
- mStateManager.goToState(NORMAL);
+ LauncherState lastState = mStateManager.getLastState();
+ 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
mWorkspace.showOutlinesTemporarily();
@@ -1825,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);
@@ -1836,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,9 +1717,6 @@
if (v instanceof FolderIcon) {
onClickFolderIcon(v);
}
- } else if ((v instanceof PageIndicator) ||
- (v == mAllAppsButton && mAllAppsButton != null)) {
- onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
startAppShortcutOrInfoActivity(v);
} else if (tag instanceof LauncherAppWidgetInfo) {
@@ -1903,24 +1765,6 @@
}
}
- /**
- * Event handler for the "grid" button or "caret" that appears on the home screen, which
- * enters all apps mode. In verticalBarLayout the caret can be seen when all apps is open, and
- * so in that case reverses the action.
- *
- * @param v The view that was clicked.
- */
- protected void onClickAllAppsButton(View v) {
- if (LOGD) Log.d(TAG, "onClickAllAppsButton");
- if (!isInState(ALL_APPS)) {
- getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
- ControlType.ALL_APPS_BUTTON);
- mStateManager.goToState(ALL_APPS);
- } else {
- mStateManager.goToState(NORMAL);
- }
- }
-
private void onClickPendingAppItem(final View v, final String packageName,
boolean downloadStarted) {
if (downloadStarted) {
@@ -1949,7 +1793,7 @@
private void startMarketIntentForPackage(View v, String packageName) {
ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = PackageManagerHelper.getMarketIntent(packageName);
+ Intent intent = new PackageManagerHelper(v.getContext()).getMarketIntent(packageName);
startActivitySafely(v, intent, item);
}
@@ -1969,7 +1813,8 @@
final ShortcutInfo shortcut = (ShortcutInfo) tag;
if (shortcut.isDisabled()) {
- if ((shortcut.runtimeStatusFlags &
+ final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
+ if ((disabledFlags &
~FLAG_DISABLED_SUSPENDED &
~FLAG_DISABLED_QUIET_USER) == 0) {
// If the app is only disabled because of the above flags, launch activity anyway.
@@ -2013,13 +1858,25 @@
Intent intent;
if (item instanceof PromiseAppInfo) {
PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
- intent = promiseAppInfo.getMarketIntent();
+ intent = promiseAppInfo.getMarketIntent(this);
} else {
intent = item.getIntent();
}
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
+ if (item instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) item;
+ if (si.hasStatusFlag(ShortcutInfo.FLAG_SUPPORTS_WEB_UI)
+ && intent.getAction() == Intent.ACTION_VIEW) {
+ // make a copy of the intent that has the package set to null
+ // we do this because the platform sometimes disables instant
+ // apps temporarily (triggered by the user) and fallbacks to the
+ // web ui. This only works though if the package isn't set
+ intent = new Intent(intent);
+ intent.setPackage(null);
+ }
+ }
startActivitySafely(v, intent, item);
}
@@ -2067,7 +1924,9 @@
if (v != null) {
intent.setSourceBounds(getViewBounds(v));
// If there is no target package, use the default intent chooser animation
- launchOptions = hasTargetPackage ? getActivityLaunchOptions(v) : null;
+ launchOptions = hasTargetPackage
+ ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
+ : null;
} else {
launchOptions = null;
}
@@ -2121,31 +1980,16 @@
}
}
+ 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) {
- if (Utilities.ATLEAST_MARSHMALLOW) {
- int left = 0, top = 0;
- int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof BubbleTextView) {
- // Launch from center of icon, not entire view
- Drawable icon = ((BubbleTextView) v).getIcon();
- if (icon != null) {
- Rect bounds = icon.getBounds();
- left = (width - bounds.width()) / 2;
- top = v.getPaddingTop();
- width = bounds.width();
- height = bounds.height();
- }
- }
- return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height).toBundle();
- } 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(
- this, R.anim.task_open_enter, R.anim.no_anim).toBundle();
- }
- return null;
+ public ActivityOptions getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) {
+ return useDefaultLaunchOptions
+ ? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v)
+ : mAppTransitionManager.getActivityLaunchOptions(this, v);
}
public Rect getViewBounds(View v) {
@@ -2160,11 +2004,14 @@
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
return mAppLaunchSuccess;
}
+
// 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) : null;
+ Bundle optsBundle = useLaunchAnimation
+ ? getActivityLaunchOptionsAsBundle(v, isInMultiWindowModeCompat())
+ : null;
UserHandle user = item == null ? null : item.user;
@@ -2174,11 +2021,12 @@
intent.setSourceBounds(getViewBounds(v));
}
try {
- if (Utilities.ATLEAST_MARSHMALLOW
+ boolean isShortcut = Utilities.ATLEAST_MARSHMALLOW
&& (item instanceof ShortcutInfo)
&& (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
- || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
- && !((ShortcutInfo) item).isPromise()) {
+ || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ && !((ShortcutInfo) item).isPromise();
+ if (isShortcut) {
// Shortcuts need some special checks due to legacy reasons.
startShortcutIntentSafely(intent, optsBundle, item);
} else if (user == null || user.equals(Process.myUserHandle())) {
@@ -2209,7 +2057,7 @@
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
- mLastDispatchTouchEventX = ev.getX();
+ mLastDispatchTouchEvent.set(ev.getX(), ev.getY());
return super.dispatchTouchEvent(ev);
}
@@ -2220,7 +2068,7 @@
if (!isInState(NORMAL) && !isInState(OVERVIEW)) return false;
boolean ignoreLongPressToOverview =
- mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
+ mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEvent.x);
if (v instanceof Workspace) {
if (!isInState(OVERVIEW)) {
@@ -2228,7 +2076,7 @@
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
- UiFactory.onWorkspaceLongPress(this);
+ UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
return true;
@@ -2265,7 +2113,7 @@
getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
Action.Direction.NONE, ContainerType.WORKSPACE,
mWorkspace.getCurrentPage());
- UiFactory.onWorkspaceLongPress(this);
+ UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
}
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
@@ -2333,33 +2181,6 @@
return result;
}
- /**
- * If the activity is currently paused, signal that we need to run the passed Runnable
- * in onResume.
- *
- * This needs to be called from incoming places where resources might have been loaded
- * while the activity is paused. That is because the Configuration (e.g., rotation) might be
- * wrong when we're not running, and if the activity comes back to what the configuration was
- * when we were paused, activity is not restarted.
- *
- * Implementation of the method from LauncherModel.Callbacks.
- *
- * @return {@code true} if we are currently paused. The caller might be able to skip some work
- */
- @Thunk boolean waitUntilResume(Runnable run) {
- if (mPaused) {
- if (LOGD) Log.d(TAG, "Deferring update until onResume");
- if (run instanceof RunnableWithId) {
- // Remove any runnables which have the same id
- while (mBindOnResumeCallbacks.remove(run)) { }
- }
- mBindOnResumeCallbacks.add(run);
- return true;
- } else {
- return false;
- }
- }
-
public void setOnResumeCallback(OnResumeCallback callback) {
if (mOnResumeCallback != null) {
mOnResumeCallback.onLauncherResume();
@@ -2367,29 +2188,8 @@
mOnResumeCallback = callback;
}
- /**
- * If the activity is currently paused, signal that we need to re-run the loader
- * in onResume.
- *
- * This needs to be called from incoming places where resources might have been loaded
- * while we are paused. That is becaues the Configuration might be wrong
- * when we're not running, and if it comes back to what it was when we
- * were paused, we are not restarted.
- *
- * Implementation of the method from LauncherModel.Callbacks.
- *
- * @return true if we are currently paused. The caller might be able to
- * skip some work in that case since we will come back again.
- */
- @Override
- public boolean setLoadOnResume() {
- if (mPaused) {
- if (LOGD) Log.d(TAG, "setLoadOnResume");
- mOnResumeNeedsLoad = true;
- return true;
- } else {
- return false;
- }
+ public void setOnStartCallback(OnStartCallback callback) {
+ mOnStartCallback = callback;
}
/**
@@ -2410,7 +2210,6 @@
*/
@Override
public void clearPendingBinds() {
- mBindOnResumeCallbacks.clear();
if (mPendingExecutor != null) {
mPendingExecutor.markCompleted();
mPendingExecutor = null;
@@ -2428,7 +2227,7 @@
// we are starting a fresh bind, close all such panels as all the icons are about
// to go away.
AbstractFloatingView.closeOpenViews(this, true,
- AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_WIDGETS_FULL_SHEET);
+ AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
setWorkspaceLoading(true);
@@ -2474,18 +2273,8 @@
}
@Override
- public void bindAppsAdded(final ArrayList<Long> newScreens,
- final ArrayList<ItemInfo> addNotAnimated,
- final ArrayList<ItemInfo> addAnimated) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppsAdded(newScreens, addNotAnimated, addAnimated);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindAppsAdded(ArrayList<Long> newScreens, ArrayList<ItemInfo> addNotAnimated,
+ ArrayList<ItemInfo> addAnimated) {
// Add the new screens
if (newScreens != null) {
bindAddScreens(newScreens);
@@ -2511,15 +2300,6 @@
*/
@Override
public void bindItems(final List<ItemInfo> items, final boolean forceAnimateIcons) {
- Runnable r = new Runnable() {
- public void run() {
- bindItems(items, forceAnimateIcons);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
// Get the list of added items and intersect them with the set of items here
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection<Animator> bounceAnims = new ArrayList<>();
@@ -2762,7 +2542,7 @@
}
if (((PendingAppWidgetHostView) view).isReinflateIfNeeded()) {
- view.reinflate();
+ view.reInflate();
}
getModelWriter().updateItemInDatabase(info);
@@ -2770,7 +2550,7 @@
}
public void onPageBoundSynchronously(int page) {
- mSynchronouslyBoundPages.add(page);
+ mSynchronouslyBoundPage = page;
}
@Override
@@ -2779,6 +2559,11 @@
mPendingExecutor.markCompleted();
}
mPendingExecutor = executor;
+ if (!isInState(ALL_APPS)) {
+ mAppsView.getAppsStore().setDeferUpdates(true);
+ mPendingExecutor.execute(() -> mAppsView.getAppsStore().setDeferUpdates(false));
+ }
+
executor.attachTo(this);
}
@@ -2790,27 +2575,11 @@
@Override
public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
- Runnable r = new Runnable() {
- public void run() {
- finishFirstPageBind(executor);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
- Runnable onComplete = new Runnable() {
- @Override
- public void run() {
- if (executor != null) {
- executor.onLoadAnimationCompleted();
- }
- }
- };
if (mDragLayer.getAlpha() < 1) {
- mDragLayer.animate().alpha(1).withEndAction(onComplete).start();
- } else {
- onComplete.run();
+ mDragLayer.animate().alpha(1).withEndAction(
+ executor == null ? null : executor::onLoadAnimationCompleted).start();
+ } else if (executor != null) {
+ executor.onLoadAnimationCompleted();
}
}
@@ -2820,14 +2589,6 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
public void finishBindingItems() {
- Runnable r = new Runnable() {
- public void run() {
- finishBindingItems();
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
TraceHelper.beginSection("finishBindingItems");
mWorkspace.restoreInstanceStateForRemainingPages();
@@ -2842,7 +2603,6 @@
InstallShortcutReceiver.disableAndFlushInstallQueue(
InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
- NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
TraceHelper.endSection("finishBindingItems");
}
@@ -2859,49 +2619,20 @@
return bounceAnim;
}
- public boolean useVerticalBarLayout() {
- return mDeviceProfile.isVerticalBarLayout();
- }
-
/**
* Add the icons for all apps.
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAllApplications(final ArrayList<AppInfo> apps) {
- Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_APPS) {
- public void run() {
- bindAllApplications(apps);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
+ public void bindAllApplications(ArrayList<AppInfo> apps) {
+ mAppsView.getAppsStore().setApps(apps);
- if (mAppsView != null) {
- Executor pendingExecutor = getPendingExecutor();
- if (pendingExecutor != null && !isInState(ALL_APPS)) {
- // Wait until the fade in animation has finished before setting all apps list.
- pendingExecutor.execute(r);
- return;
- }
-
- mAppsView.setApps(apps);
- }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.bindAllApplications(apps);
}
}
/**
- * Returns an Executor that will run after the launcher is first drawn (including after the
- * initial fade in animation). Returns null if the first draw has already occurred.
- */
- public @Nullable Executor getPendingExecutor() {
- return mPendingExecutor != null && mPendingExecutor.canQueue() ? mPendingExecutor : null;
- }
-
- /**
* Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
* because LauncherModel's map is updated in the background, while Launcher runs on the UI.
*/
@@ -2915,47 +2646,18 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void bindAppsAddedOrUpdated(final ArrayList<AppInfo> apps) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppsAddedOrUpdated(apps);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
- if (mAppsView != null) {
- mAppsView.addOrUpdateApps(apps);
- }
+ @Override
+ public void bindAppsAddedOrUpdated(ArrayList<AppInfo> apps) {
+ mAppsView.getAppsStore().addOrUpdateApps(apps);
}
@Override
- public void bindPromiseAppProgressUpdated(final PromiseAppInfo app) {
- Runnable r = new Runnable() {
- public void run() {
- bindPromiseAppProgressUpdated(app);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
- if (mAppsView != null) {
- mAppsView.updatePromiseAppProgress(app);
- }
+ public void bindPromiseAppProgressUpdated(PromiseAppInfo app) {
+ mAppsView.getAppsStore().updatePromiseAppProgress(app);
}
@Override
- public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
- Runnable r = new Runnable() {
- public void run() {
- bindWidgetsRestored(widgets);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
+ public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) {
mWorkspace.widgetsRestored(widgets);
}
@@ -2966,16 +2668,7 @@
* @param updated list of shortcuts which have changed.
*/
@Override
- public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated, final UserHandle user) {
- Runnable r = new Runnable() {
- public void run() {
- bindShortcutsChanged(updated, user);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated, final UserHandle user) {
if (!updated.isEmpty()) {
mWorkspace.updateShortcuts(updated);
}
@@ -2987,16 +2680,7 @@
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
- public void bindRestoreItemsChange(final HashSet<ItemInfo> updates) {
- Runnable r = new Runnable() {
- public void run() {
- bindRestoreItemsChange(updates);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
+ public void bindRestoreItemsChange(HashSet<ItemInfo> updates) {
mWorkspace.updateRestoreItems(updates);
}
@@ -3009,48 +2693,18 @@
*/
@Override
public void bindWorkspaceComponentsRemoved(final ItemInfoMatcher matcher) {
- Runnable r = new Runnable() {
- public void run() {
- bindWorkspaceComponentsRemoved(matcher);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
mWorkspace.removeItemsByMatcher(matcher);
mDragController.onAppsRemoved(matcher);
}
@Override
public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
- Runnable r = new Runnable() {
- public void run() {
- bindAppInfosRemoved(appInfos);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
- // Update AllApps
- if (mAppsView != null) {
- mAppsView.removeApps(appInfos);
- }
+ mAppsView.getAppsStore().removeApps(appInfos);
}
@Override
public void bindAllWidgets(final ArrayList<WidgetListRowEntry> allWidgets) {
mPopupDataProvider.setAllWidgets(allWidgets);
- Runnable r = new RunnableWithId(RUNNABLE_ID_BIND_WIDGETS) {
- @Override
- public void run() {
- bindAllWidgets(allWidgets);
- }
- };
- if (waitUntilResume(r)) {
- return;
- }
-
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
if (topView != null) {
topView.onWidgetsBound();
@@ -3069,12 +2723,6 @@
return mRotationEnabled;
}
- private boolean shouldShowDiscoveryBounce() {
- return isInState(NORMAL)
- && !mSharedPrefs.getBoolean(AllAppsState.APPS_VIEW_SHOWN, false)
- && !UserManagerCompat.getInstance(this).isDemoUser();
- }
-
/**
* $ adb shell dumpsys activity com.android.launcher3.Launcher [--all]
*/
@@ -3183,6 +2831,26 @@
return super.onKeyShortcut(keyCode, event);
}
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
+ // KEYCODE_MENU is sent by some tests, for example
+ // LauncherJankTests#testWidgetsContainerFling. Don't just remove its handling.
+ if (!mDragController.isDragging() && !mWorkspace.isSwitchingState() &&
+ isInState(NORMAL)) {
+ // Close any open floating views.
+ AbstractFloatingView.closeAllOpenViews(this);
+
+ // Setting the touch point to (-1, -1) will show the options popup in the center of
+ // the screen.
+ mLastDispatchTouchEvent.set(-1, -1);
+ UiFactory.onWorkspaceLongPress(this, mLastDispatchTouchEvent);
+ }
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
public static Launcher getLauncher(Context context) {
if (context instanceof Launcher) {
return (Launcher) context;
@@ -3202,6 +2870,26 @@
}
}
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ mCurrentActionMode = mode;
+ }
+
+ @Override
+ public void onActionModeFinished(ActionMode mode) {
+ super.onActionModeFinished(mode);
+ mCurrentActionMode = null;
+ }
+
+ public boolean finishAutoCancelActionMode() {
+ if (mCurrentActionMode != null && AUTO_CANCEL_ACTION_MODE == mCurrentActionMode.getTag()) {
+ mCurrentActionMode.finish();
+ return true;
+ }
+ return false;
+ }
+
/**
* Callback for listening for onResume
*/
@@ -3209,4 +2897,12 @@
void onLauncherResume();
}
+
+ /**
+ * Callback for listening for onStart
+ */
+ public interface OnStartCallback {
+
+ void onLauncherStart(Launcher launcher);
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 0136c70..a46692b 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -27,6 +27,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
@@ -39,6 +40,8 @@
public class LauncherAppState {
+ public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
+
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
@@ -103,6 +106,10 @@
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ filter.addAction(ACTION_FORCE_ROLOAD);
+ }
+
mContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(mContext).enableAndResetCache();
new ConfigMonitor(mContext).register();
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
new file mode 100644
index 0000000..19fa3d4
--- /dev/null
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+/**
+ * Manages the opening and closing app transitions from Launcher.
+ */
+public class LauncherAppTransitionManager {
+
+ public static LauncherAppTransitionManager newInstance(Context context) {
+ return Utilities.getOverrideObject(LauncherAppTransitionManager.class,
+ context, R.string.app_transition_manager_class);
+ }
+
+ public ActivityOptions getDefaultActivityLaunchOptions(Launcher launcher, View v) {
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ int left = 0, top = 0;
+ int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+ if (v instanceof BubbleTextView) {
+ // Launch from center of icon, not entire view
+ Drawable icon = ((BubbleTextView) v).getIcon();
+ if (icon != null) {
+ Rect bounds = icon.getBounds();
+ left = (width - bounds.width()) / 2;
+ top = v.getPaddingTop();
+ width = bounds.width();
+ height = bounds.height();
+ }
+ }
+ 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);
+ }
+ return null;
+ }
+
+ 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/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 9aa74b3..7bc7139 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -26,11 +26,14 @@
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
+import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.widget.Toast;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.widget.DeferredAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import java.util.ArrayList;
@@ -84,6 +87,14 @@
// have been established by this point, and we will end up populating the
// widgets upon bind anyway. See issue 14255011 for more context.
}
+
+ // We go in reverse order and inflate any deferred widget
+ for (int i = mViews.size() - 1; i >= 0; i--) {
+ LauncherAppWidgetHostView view = mViews.valueAt(i);
+ if (view instanceof DeferredAppWidgetHostView) {
+ view.reInflate();
+ }
+ }
}
@Override
@@ -178,6 +189,11 @@
inflater.inflate(appWidget.initialLayout, lahv);
lahv.setAppWidget(0, appWidget);
return lahv;
+ } else if ((mFlags & FLAG_LISTENING) == 0) {
+ DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+ view.setAppWidget(appWidgetId, appWidget);
+ mViews.put(appWidgetId, view);
+ return view;
} else {
try {
return super.createView(context, appWidgetId, appWidget);
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 928258f..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();
@@ -48,10 +47,8 @@
void onActivityResult(int requestCode, int resultCode, Intent data);
void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults);
- void onWindowFocusChanged(boolean hasFocus);
void onAttachedToWindow();
void onDetachedFromWindow();
- boolean onPrepareOptionsMenu(Menu menu);
void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
void onHomeIntent();
boolean handleBackPressed();
@@ -73,4 +70,12 @@
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
boolean hasSettings();
+
+ /**
+ * Called when launcher integrated quickstep and some quickstep gesture started. It can be
+ * called multiple times for a single gesture an UI or background thread.
+ *
+ * @param isVisible if Launcher was visible when the gesture started.
+ */
+ void onQuickstepGestureStarted(boolean isVisible);
}
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src/com/android/launcher3/LauncherExterns.java
index 887859c..272bbf6 100644
--- a/src/com/android/launcher3/LauncherExterns.java
+++ b/src/com/android/launcher3/LauncherExterns.java
@@ -24,11 +24,9 @@
*/
public interface LauncherExterns {
- public boolean setLauncherCallbacks(LauncherCallbacks callbacks);
+ boolean setLauncherCallbacks(LauncherCallbacks callbacks);
- public SharedPreferences getSharedPrefs();
+ SharedPreferences getSharedPrefs();
- public void setLauncherOverlay(Launcher.LauncherOverlay overlay);
-
- void clearTypedText();
+ void setLauncherOverlay(Launcher.LauncherOverlay overlay);
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 469c5f1..6646b78 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,8 +16,10 @@
package com.android.launcher3;
+import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
+import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -39,18 +41,16 @@
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.model.AddWorkspaceItemsTask;
+import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.CacheDataUpdatedTask;
-import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.LoaderResults;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.PackageInstallStateChangedTask;
-import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.PackageUpdatedTask;
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
@@ -135,7 +135,6 @@
};
public interface Callbacks {
- public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
public void clearPendingBinds();
public void startBinding();
@@ -405,6 +404,8 @@
enqueueModelUpdateTask(new UserLockStateChangedTask(user));
}
}
+ } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+ forceReload();
}
}
@@ -419,25 +420,11 @@
mModelLoaded = false;
}
- // Do this here because if the launcher activity is running it will be restarted.
- // If it's not running startLoaderFromBackground will merely tell it that it needs
- // to reload.
- startLoaderFromBackground();
- }
-
- /**
- * When the launcher is in the background, it's possible for it to miss paired
- * configuration changes. So whenever we trigger the loader from the background
- * tell the launcher that it needs to re-run the loader when it comes back instead
- * of doing it now.
- */
- public void startLoaderFromBackground() {
+ // Start the loader if launcher is already running, otherwise the loader will run,
+ // the next time launcher starts
Callbacks callbacks = getCallback();
if (callbacks != null) {
- // Only actually run the loader if they're not paused.
- if (!callbacks.setLoadOnResume()) {
- startLoader(callbacks.getCurrentWorkspaceScreen());
- }
+ startLoader(callbacks.getCurrentWorkspaceScreen());
}
}
@@ -457,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();
@@ -506,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.
*/
@@ -629,7 +621,9 @@
@Override
public ShortcutInfo get() {
si.updateFromDeepShortcutInfo(info, mApp.getContext());
- si.iconBitmap = LauncherIcons.createShortcutIcon(info, mApp.getContext());
+ LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
+ li.createShortcutIcon(info).applyTo(si);
+ li.recycle();
return si;
}
});
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/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index a814323..fc4de2d 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -11,11 +11,15 @@
import android.view.View;
import android.view.ViewDebug;
+import com.android.launcher3.util.Themes;
+
import static com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ROOT_VIEW;
public class LauncherRootView extends InsettableFrameLayout {
+ private final Launcher mLauncher;
+
private final Paint mOpaquePaint;
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDrawSideInsetBar;
@@ -32,6 +36,8 @@
mOpaquePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOpaquePaint.setColor(Color.BLACK);
mOpaquePaint.setStyle(Paint.Style.FILL);
+
+ mLauncher = Launcher.getLauncher(context);
}
@Override
@@ -57,10 +63,12 @@
} else {
mLeftInsetBarWidth = mRightInsetBarWidth = 0;
}
- Launcher.getLauncher(getContext()).getSystemUiController().updateUiState(
+ mLauncher.getSystemUiController().updateUiState(
UI_STATE_ROOT_VIEW, mDrawSideInsetBar ? FLAG_DARK_NAV : 0);
- boolean rawInsetsChanged = !mInsets.equals(insets);
+ // Update device profile before notifying th children.
+ mLauncher.getDeviceProfile().updateInsets(insets);
+ boolean resetState = !insets.equals(mInsets);
setInsets(insets);
if (mAlignedView != null) {
@@ -72,17 +80,29 @@
mAlignedView.setLayoutParams(lp);
}
}
-
- if (rawInsetsChanged) {
- // Update the grid again
- Launcher launcher = Launcher.getLauncher(getContext());
- launcher.onInsetsChanged(insets);
+ if (resetState) {
+ mLauncher.getStateManager().reapplyState();
}
return true; // I'll take it from here
}
@Override
+ public void setInsets(Rect insets) {
+ // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
+ // modifying child layout params.
+ if (!insets.equals(mInsets)) {
+ super.setInsets(insets);
+ }
+ setBackground(insets.top == 0 ? null
+ : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
+ }
+
+ public void dispatchInsets() {
+ super.setInsets(mInsets);
+ }
+
+ @Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index d6cd8a3..472a5a9 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -19,11 +19,15 @@
import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-import android.view.View;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
-import com.android.launcher3.states.AllAppsState;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.uioverrides.AllAppsState;
import com.android.launcher3.states.SpringLoadedState;
import com.android.launcher3.uioverrides.OverviewState;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import java.util.Arrays;
@@ -37,12 +41,25 @@
protected static final int FLAG_SHOW_SCRIM = 1 << 0;
protected static final int FLAG_MULTI_PAGE = 1 << 1;
protected static final int FLAG_DISABLE_ACCESSIBILITY = 1 << 2;
- protected static final int FLAG_DO_NOT_RESTORE = 1 << 3;
+ protected static final int FLAG_DISABLE_RESTORE = 1 << 3;
+ protected static final int FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED = 1 << 4;
+ protected static final int FLAG_DISABLE_PAGE_CLIPPING = 1 << 5;
+
+ protected static final PageAlphaProvider DEFAULT_ALPHA_PROVIDER =
+ new PageAlphaProvider(ACCEL_2) {
+ @Override
+ public float getPageAlpha(int pageIndex) {
+ return 1;
+ }
+ };
private static final LauncherState[] sAllStates = new LauncherState[4];
+ /**
+ * TODO: Create a separate class for NORMAL state.
+ */
public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
- 0, 1f, FLAG_DO_NOT_RESTORE);
+ 0, FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED);
public static final LauncherState ALL_APPS = new AllAppsState(1);
@@ -60,7 +77,7 @@
/**
* True if the state can be persisted across activity restarts.
*/
- public final boolean doNotRestore;
+ public final boolean disableRestore;
/**
* True if workspace has multiple pages visible.
@@ -82,14 +99,17 @@
public final int transitionDuration;
/**
- * Fraction shift in the vertical translation UI and related properties
- *
- * @see com.android.launcher3.allapps.AllAppsTransitionController
+ * True if the state allows workspace icons to be dragged.
*/
- public final float verticalProgress;
+ public final boolean workspaceIconsCanBeDragged;
- public LauncherState(int id, int containerType, int transitionDuration, float verticalProgress,
- int flags) {
+ /**
+ * True if the workspace pages should not be clipped relative to the workspace bounds
+ * for this state.
+ */
+ public final boolean disablePageClipping;
+
+ public LauncherState(int id, int containerType, int transitionDuration, int flags) {
this.containerType = containerType;
this.transitionDuration = transitionDuration;
@@ -98,9 +118,9 @@
this.workspaceAccessibilityFlag = (flags & FLAG_DISABLE_ACCESSIBILITY) != 0
? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: IMPORTANT_FOR_ACCESSIBILITY_AUTO;
- this.doNotRestore = (flags & FLAG_DO_NOT_RESTORE) != 0;
-
- this.verticalProgress = verticalProgress;
+ this.disableRestore = (flags & FLAG_DISABLE_RESTORE) != 0;
+ this.workspaceIconsCanBeDragged = (flags & FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED) != 0;
+ this.disablePageClipping = (flags & FLAG_DISABLE_PAGE_CLIPPING) != 0;
this.ordinal = id;
sAllStates[id] = this;
@@ -111,7 +131,11 @@
}
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
- return new float[] {1, 0};
+ return new float[] {1, 0, 0};
+ }
+
+ public float getHoseatAlpha(Launcher launcher) {
+ return 1f;
}
public void onStateEnabled(Launcher launcher) {
@@ -124,11 +148,58 @@
return launcher.getWorkspace();
}
+ /**
+ * Fraction shift in the vertical translation UI and related properties
+ *
+ * @see com.android.launcher3.allapps.AllAppsTransitionController
+ */
+ public float getVerticalProgress(Launcher launcher) {
+ return 1f;
+ }
+
public String getDescription(Launcher launcher) {
return launcher.getWorkspace().getCurrentPageDescription();
}
+ public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+ if (this != NORMAL || !launcher.getDeviceProfile().shouldFadeAdjacentWorkspaceScreens()) {
+ return DEFAULT_ALPHA_PROVIDER;
+ }
+ final int centerPage = launcher.getWorkspace().getNextPage();
+ return new PageAlphaProvider(ACCEL_2) {
+ @Override
+ public float getPageAlpha(int pageIndex) {
+ return pageIndex != centerPage ? 0 : 1f;
+ }
+ };
+ }
+
+ public LauncherState getHistoryForState(LauncherState previousState) {
+ // No history is supported
+ return NORMAL;
+ }
+
+ /**
+ * Called when the start transition ends and the user settles on this particular state.
+ */
+ public void onStateTransitionEnd(Launcher launcher) {
+ if (this == NORMAL) {
+ UiFactory.resetOverview(launcher);
+ }
+ }
+
protected static void dispatchWindowStateChanged(Launcher launcher) {
launcher.getWindow().getDecorView().sendAccessibilityEvent(TYPE_WINDOW_STATE_CHANGED);
}
+
+ public static abstract class PageAlphaProvider {
+
+ public final Interpolator interpolator;
+
+ public PageAlphaProvider(Interpolator interpolator) {
+ this.interpolator = interpolator;
+ }
+
+ public abstract float getPageAlpha(int pageIndex);
+ }
}
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 1e6016b..2f8687d 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -25,9 +25,9 @@
import android.os.Looper;
import android.view.View;
-import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.uioverrides.UiFactory;
/**
@@ -66,7 +66,7 @@
* - Go back with back key TODO: make this not go to workspace
* - From all apps
* - From workspace
- * - Enter and exit car mode (becuase it causes an extra configuration changed)
+ * - Enter and exit car mode (becase it causes an extra configuration changed)
* - From all apps
* - From the center workspace
* - From another workspace
@@ -82,6 +82,11 @@
private StateHandler[] mStateHandlers;
private LauncherState mState = NORMAL;
+ private LauncherState mLastStableState = NORMAL;
+ private LauncherState mCurrentStableState = NORMAL;
+
+ private LauncherState mRestState;
+
private StateListener mStateListener;
public LauncherStateManager(Launcher l) {
@@ -93,7 +98,7 @@
return mState;
}
- private StateHandler[] getStateHandlers() {
+ public StateHandler[] getStateHandlers() {
if (mStateHandlers == null) {
mStateHandlers = UiFactory.getStateHandler(mLauncher);
}
@@ -108,7 +113,7 @@
* @see #goToState(LauncherState, boolean, Runnable)
*/
public void goToState(LauncherState state) {
- goToState(state, true, 0, null);
+ goToState(state, mLauncher.isStarted() /* animated */, 0, null);
}
/**
@@ -143,10 +148,22 @@
goToState(state, true, delay, null);
}
+ public void reapplyState() {
+ if (mConfig.mCurrentAnimation == null) {
+ for (StateHandler handler : getStateHandlers()) {
+ handler.setState(mState);
+ }
+ }
+ }
+
private void goToState(LauncherState state, boolean animated, long delay,
Runnable onCompleteRunnable) {
- if (mLauncher.isInState(state) && mConfig.mCurrentAnimation == null) {
+ 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) {
onCompleteRunnable.run();
@@ -158,14 +175,15 @@
mConfig.reset();
if (!animated) {
- setState(state);
+ preOnStateTransitionStart();
+ onStateTransitionStart(state);
for (StateHandler handler : getStateHandlers()) {
handler.setState(state);
}
if (mStateListener != null) {
mStateListener.onStateSetImmediately(state);
}
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ onStateTransitionEnd(state);
// Run any queued runnable
if (onCompleteRunnable != null) {
@@ -177,8 +195,12 @@
// 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, onCompleteRunnable);
+ AnimatorSet animation = createAnimationToNewWorkspaceInternal(
+ state, new AnimatorSetBuilder(), onCompleteRunnable);
Runnable runnable = new StartAnimRunnable(animation, state.getFinalFocus(mLauncher));
if (delay > 0) {
mUiHandler.postDelayed(runnable, delay);
@@ -196,28 +218,34 @@
*/
public AnimatorPlaybackController createAnimationToNewWorkspace(
LauncherState state, long duration) {
+ return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration);
+ }
+
+ public AnimatorPlaybackController createAnimationToNewWorkspace(
+ LauncherState state, AnimatorSetBuilder builder, long duration) {
mConfig.reset();
mConfig.userControlled = true;
mConfig.duration = duration;
return AnimatorPlaybackController.wrap(
- createAnimationToNewWorkspaceInternal(state, null), duration);
+ createAnimationToNewWorkspaceInternal(state, builder, null), duration);
}
protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
- final Runnable onCompleteRunnable) {
- final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
- final AnimationLayerSet layerViews = new AnimationLayerSet();
+ AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
+ preOnStateTransitionStart();
for (StateHandler handler : getStateHandlers()) {
- handler.setStateWithAnimation(state, layerViews, animation, mConfig);
+ builder.startTag(handler);
+ handler.setStateWithAnimation(state, builder, mConfig);
}
- animation.addListener(layerViews);
+
+ final AnimatorSet animation = builder.build();
animation.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationStart(Animator animation) {
// Change the internal state only when the transition actually starts
- setState(state);
+ onStateTransitionStart(state);
if (mStateListener != null) {
mStateListener.onStateTransitionStart(state);
}
@@ -237,19 +265,74 @@
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
-
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ onStateTransitionEnd(state);
}
});
mConfig.setAnimation(animation);
return mConfig.mCurrentAnimation;
}
- private void setState(LauncherState state) {
+ 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;
mState.onStateEnabled(mLauncher);
mLauncher.getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+
+ if (state.disablePageClipping) {
+ // Only disable clipping if needed, otherwise leave it as previous value.
+ mLauncher.getWorkspace().setClipChildren(false);
+ }
+ }
+
+ private void onStateTransitionEnd(LauncherState state) {
+ // Only change the stable states after the transitions have finished
+ if (state != mCurrentStableState) {
+ mLastStableState = state.getHistoryForState(mCurrentStableState);
+ mCurrentStableState = state;
+ }
+
+ state.onStateTransitionEnd(mLauncher);
+ mLauncher.getWorkspace().setClipChildren(!state.disablePageClipping);
+ mLauncher.finishAutoCancelActionMode();
+
+ if (state == NORMAL) {
+ setRestState(null);
+ }
+
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ }
+
+ public void onWindowFocusChanged() {
+ UiFactory.onLauncherStateOrFocusChanged(mLauncher);
+ }
+
+ public LauncherState getLastState() {
+ return mLastStableState;
+ }
+
+ public void moveToRestState() {
+ if (mState.disableRestore) {
+ goToState(getRestState());
+ // Reset history
+ mLastStableState = NORMAL;
+ }
+ }
+
+ public LauncherState getRestState() {
+ return mRestState == null ? NORMAL : mRestState;
+ }
+
+ public void setRestState(LauncherState restState) {
+ mRestState = restState;
}
/**
@@ -321,8 +404,8 @@
/**
* Sets the UI to {@param state} by animating any changes.
*/
- void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
- AnimatorSet anim, AnimationConfig config);
+ void setStateWithAnimation(LauncherState toState,
+ AnimatorSetBuilder builder, AnimationConfig config);
}
public interface StateListener {
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 6c22474..d6e5d18 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -16,6 +16,9 @@
package com.android.launcher3;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isObservedEventType;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
@@ -30,7 +33,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -42,7 +44,6 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.Interpolator;
@@ -58,16 +59,13 @@
* 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;
- protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
+ public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
// OverScroll constants
private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -87,6 +85,7 @@
public static final int INVALID_RESTORE_PAGE = -1001;
private boolean mFreeScroll = false;
+ private boolean mSettleOnPageInFreeScroll = false;
protected int mFlingThresholdVelocity;
protected int mMinFlingVelocity;
@@ -104,7 +103,7 @@
protected LauncherScroller mScroller;
private Interpolator mDefaultInterpolator;
private VelocityTracker mVelocityTracker;
- @Thunk int mPageSpacing = 0;
+ protected int mPageSpacing = 0;
private float mParentDownMotionX;
private float mParentDownMotionY;
@@ -152,11 +151,7 @@
// Page Indicator
@Thunk int mPageIndicatorViewId;
- protected PageIndicator mPageIndicator;
- // The viewport whether the pages are to be contained (the actual view may be larger than the
- // viewport)
- @ViewDebug.ExportedProperty(category = "launcher")
- private Rect mViewport = new Rect();
+ protected T mPageIndicator;
// Reordering
// We use the min scale to determine how much to expand the actually PagedView measured
@@ -186,6 +181,9 @@
protected final Rect mInsets = new Rect();
protected final boolean mIsRtl;
+ // Similar to the platform implementation of isLayoutValid();
+ protected boolean mIsLayoutValid;
+
public PagedView(Context context) {
this(context, null);
}
@@ -223,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);
}
@@ -234,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());
}
}
@@ -281,26 +278,7 @@
}
}
- // Convenience methods to get the actual width/height of the PagedView (since it is measured
- // to be larger to account for the minimum possible scale)
- int getViewportWidth() {
- return mViewport.width();
- }
- public int getViewportHeight() {
- return mViewport.height();
- }
-
- // Convenience methods to get the offset ASSUMING that we are centering the pages in the
- // PagedView both horizontally and vertically
- int getViewportOffsetX() {
- return (getMeasuredWidth() - getViewportWidth()) / 2;
- }
-
- int getViewportOffsetY() {
- return (getMeasuredHeight() - getViewportHeight()) / 2;
- }
-
- public PageIndicator getPageIndicator() {
+ public T getPageIndicator() {
return mPageIndicator;
}
@@ -402,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());
}
@@ -521,9 +499,7 @@
}
private void sendScrollAccessibilityEvent() {
- AccessibilityManager am =
- (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (am.isEnabled()) {
+ if (isObservedEventType(getContext(), AccessibilityEvent.TYPE_VIEW_SCROLLED)) {
if (mCurrentPage != getNextPage()) {
AccessibilityEvent ev =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
@@ -572,9 +548,7 @@
}
onPostReorderingAnimationCompleted();
- AccessibilityManager am = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (am.isEnabled()) {
+ if (isAccessibilityEnabled(getContext())) {
// Notify the user when the page changes
announceForAccessibility(getCurrentPageDescription());
}
@@ -588,11 +562,36 @@
computeScrollHelper();
}
+ public int getExpectedHeight() {
+ return getMeasuredHeight();
+ }
+
public int getNormalChildHeight() {
- return getViewportHeight() - getPaddingTop() - getPaddingBottom()
+ return getExpectedHeight() - getPaddingTop() - getPaddingBottom()
- mInsets.top - mInsets.bottom;
}
+ public int getExpectedWidth() {
+ return getMeasuredWidth();
+ }
+
+ public int getNormalChildWidth() {
+ return getExpectedWidth() - getPaddingLeft() - getPaddingRight()
+ - mInsets.left - mInsets.right;
+ }
+
+ @Override
+ public void requestLayout() {
+ mIsLayoutValid = false;
+ super.requestLayout();
+ }
+
+ @Override
+ public void forceLayout() {
+ mIsLayoutValid = false;
+ super.forceLayout();
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (getChildCount() == 0) {
@@ -607,8 +606,6 @@
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
- mViewport.set(0, 0, widthSize, heightSize);
-
if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
@@ -625,9 +622,9 @@
if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
int myWidthSpec = MeasureSpec.makeMeasureSpec(
- getViewportWidth() - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
+ widthSize - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
int myHeightSpec = MeasureSpec.makeMeasureSpec(
- getViewportHeight() - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
+ heightSize - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
// measureChildren takes accounts for content padding, we only need to care about extra
// space due to insets.
@@ -638,6 +635,7 @@
@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mIsLayoutValid = true;
if (getChildCount() == 0) {
return;
}
@@ -645,28 +643,26 @@
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
final int childCount = getChildCount();
- int offsetX = getViewportOffsetX();
- int offsetY = getViewportOffsetY();
-
- // Update the viewport offsets
- mViewport.offset(offsetX, offsetY);
-
final int startIndex = mIsRtl ? childCount - 1 : 0;
final int endIndex = mIsRtl ? -1 : childCount;
final int delta = mIsRtl ? -1 : 1;
int verticalPadding = getPaddingTop() + getPaddingBottom();
- int childLeft = offsetX + getPaddingLeft();
+ int scrollOffsetLeft = mInsets.left + getPaddingLeft();
+ int childLeft = scrollOffsetLeft;
+
+ boolean pageScrollChanged = false;
if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
mPageScrolls = new int[childCount];
+ pageScrollChanged = true;
}
for (int i = startIndex; i != endIndex; i += delta) {
final View child = getPageAt(i);
if (child.getVisibility() != View.GONE) {
- int childTop = offsetY + getPaddingTop() + mInsets.top;
- childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding
+ int childTop = getPaddingTop() + mInsets.top;
+ childTop += (getMeasuredHeight() - mInsets.top - mInsets.bottom - verticalPadding
- child.getMeasuredHeight()) / 2;
final int childWidth = child.getMeasuredWidth();
@@ -676,8 +672,11 @@
child.layout(childLeft, childTop,
childLeft + child.getMeasuredWidth(), childTop + childHeight);
- int scrollOffsetLeft = getPaddingLeft();
- mPageScrolls[i] = childLeft - scrollOffsetLeft - offsetX;
+ final int pageScroll = childLeft - scrollOffsetLeft;
+ if (mPageScrolls[i] != pageScroll) {
+ pageScrollChanged = true;
+ mPageScrolls[i] = pageScroll;
+ }
childLeft += childWidth + mPageSpacing + getChildGap();
}
@@ -712,7 +711,7 @@
mFirstLayout = false;
}
- if (mScroller.isFinished() && mChildCountOnLastLayout != childCount) {
+ if (mScroller.isFinished() && pageScrollChanged) {
setCurrentPage(getNextPage());
}
mChildCountOnLastLayout = childCount;
@@ -745,71 +744,29 @@
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) {
if (index < 0 || index > getChildCount() - 1) return 0;
-
- int offset = getPageAt(index).getLeft() - getViewportOffsetX();
-
- return offset;
+ return getPageAt(index).getLeft();
}
@Override
@@ -927,32 +884,9 @@
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
- /**
- * Return true if a tap at (x, y) should trigger a flip to the previous page.
- */
- protected boolean hitsPreviousPage(float x, float y) {
- if (mIsRtl) {
- return (x > (getViewportOffsetX() + getViewportWidth() -
- getPaddingRight() - mPageSpacing));
- }
- return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
- }
-
- /**
- * Return true if a tap at (x, y) should trigger a flip to the next page.
- */
- protected boolean hitsNextPage(float x, float y) {
- if (mIsRtl) {
- return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
- }
- return (x > (getViewportOffsetX() + getViewportWidth() -
- getPaddingRight() - mPageSpacing));
- }
-
/** Returns whether x and y originated within the buffered viewport */
private boolean isTouchPointInViewportWithBuffer(int x, int y) {
- sTmpRect.set(mViewport.left - mViewport.width() / 2, mViewport.top,
- mViewport.right + mViewport.width() / 2, mViewport.bottom);
+ sTmpRect.set(-getMeasuredWidth() / 2, 0, 3 * getMeasuredWidth() / 2, getMeasuredHeight());
return sTmpRect.contains(x, y);
}
@@ -1102,7 +1036,7 @@
}
protected float getScrollProgress(int screenCenter, View v, int page) {
- final int halfScreenSize = getViewportWidth() / 2;
+ final int halfScreenSize = getMeasuredWidth() / 2;
int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
int count = getChildCount();
@@ -1142,8 +1076,8 @@
} else {
View child = getChildAt(index);
- int scrollOffset = scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft();
- int baselineX = mPageScrolls[index] + scrollOffset + getViewportOffsetX();
+ int scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft();
+ int baselineX = mPageScrolls[index] + scrollOffset;
return (int) (child.getX() - baselineX);
}
}
@@ -1151,7 +1085,7 @@
protected void dampedOverScroll(float amount) {
if (Float.compare(amount, 0f) == 0) return;
- int overScrollAmount = OverScroll.dampedScroll(amount, getViewportWidth());
+ int overScrollAmount = OverScroll.dampedScroll(amount, getMeasuredWidth());
if (amount < 0) {
mOverScrollX = overScrollAmount;
super.scrollTo(mOverScrollX, getScrollY());
@@ -1170,7 +1104,12 @@
* return true if freescroll has been enabled, false otherwise
*/
protected void enableFreeScroll() {
+ enableFreeScroll(false);
+ }
+
+ protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
setEnableFreeScroll(true);
+ mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
}
protected void disableFreeScroll() {
@@ -1320,8 +1259,8 @@
// dragViewIndex < pageUnderPointIndex, so after we remove the
// drag view all subsequent views to pageUnderPointIndex will
// shift down.
- int oldX = getViewportOffsetX() + getChildOffset(i);
- int newX = getViewportOffsetX() + getChildOffset(i + shiftDelta);
+ int oldX = getChildOffset(i);
+ int newX = getChildOffset(i + shiftDelta);
// Animate the view translation from its old position to its new
// position
@@ -1370,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
@@ -1414,7 +1351,22 @@
mScroller.setInterpolator(mDefaultInterpolator);
mScroller.fling(initialScrollX,
getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
- mNextPage = getPageNearestToCenterOfScreen((int) (mScroller.getFinalX() / scaleX));
+ int unscaledScrollX = (int) (mScroller.getFinalX() / scaleX);
+ mNextPage = getPageNearestToCenterOfScreen(unscaledScrollX);
+ int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
+ int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
+ if (mSettleOnPageInFreeScroll && unscaledScrollX > firstPageScroll
+ && unscaledScrollX < lastPageScroll) {
+ // Make sure we land directly on a page. If flinging past one of the ends,
+ // don't change the velocity as it will get stopped at the end anyway.
+ mScroller.setFinalX((int) (getScrollForPage(mNextPage) * getScaleX()));
+ // Ensure the scroll/snap doesn't happen too fast;
+ int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
+ - mScroller.getDuration();
+ if (extraScrollDuration > 0) {
+ mScroller.extendDuration(extraScrollDuration);
+ }
+ }
invalidate();
}
onScrollInteractionEnd();
@@ -1577,20 +1529,20 @@
}
}
- int getPageNearestToCenterOfScreen() {
+ public int getPageNearestToCenterOfScreen() {
return getPageNearestToCenterOfScreen(getScrollX());
}
private int getPageNearestToCenterOfScreen(int scaledScrollX) {
- int screenCenter = getViewportOffsetX() + scaledScrollX + (getViewportWidth() / 2);
+ int screenCenter = scaledScrollX + (getMeasuredWidth() / 2);
int minDistanceFromScreenCenter = Integer.MAX_VALUE;
int minDistanceFromScreenCenterIndex = -1;
final int childCount = getChildCount();
for (int i = 0; i < childCount; ++i) {
- View layout = (View) getPageAt(i);
+ View layout = getPageAt(i);
int childWidth = layout.getMeasuredWidth();
int halfChildWidth = (childWidth / 2);
- int childCenter = getViewportOffsetX() + getChildOffset(i) + halfChildWidth;
+ int childCenter = getChildOffset(i) + halfChildWidth;
int distanceFromScreenCenter = Math.abs(childCenter - screenCenter);
if (distanceFromScreenCenter < minDistanceFromScreenCenter) {
minDistanceFromScreenCenter = distanceFromScreenCenter;
@@ -1625,9 +1577,9 @@
return (float) Math.sin(f);
}
- protected void snapToPageWithVelocity(int whichPage, int velocity) {
+ protected boolean snapToPageWithVelocity(int whichPage, int velocity) {
whichPage = validateNewPage(whichPage);
- int halfScreenSize = getViewportWidth() / 2;
+ int halfScreenSize = getMeasuredWidth() / 2;
final int newX = getScrollForPage(whichPage);
int delta = newX - getUnboundedScrollX();
@@ -1636,8 +1588,7 @@
if (Math.abs(velocity) < mMinFlingVelocity) {
// If the velocity is low enough, then treat this more as an automatic page advance
// as opposed to an apparent physical response to flinging
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
- return;
+ return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
}
// Here we compute a "distance" that will be used in the computation of the overall
@@ -1656,39 +1607,39 @@
// interpolator at zero, ie. 5. We use 4 to make it a little slower.
duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
- snapToPage(whichPage, delta, duration);
+ return snapToPage(whichPage, delta, duration);
}
- public void snapToPage(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ public boolean snapToPage(int whichPage) {
+ return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
}
- public void snapToPageImmediately(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
+ public boolean snapToPageImmediately(int whichPage) {
+ return snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
}
- protected void snapToPage(int whichPage, int duration) {
- snapToPage(whichPage, duration, false, null);
+ public boolean snapToPage(int whichPage, int duration) {
+ return snapToPage(whichPage, duration, false, null);
}
- protected void snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
- snapToPage(whichPage, duration, false, interpolator);
+ protected boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
+ return snapToPage(whichPage, duration, false, interpolator);
}
- protected void snapToPage(int whichPage, int duration, boolean immediate,
+ protected boolean snapToPage(int whichPage, int duration, boolean immediate,
TimeInterpolator interpolator) {
whichPage = validateNewPage(whichPage);
int newX = getScrollForPage(whichPage);
final int delta = newX - getUnboundedScrollX();
- snapToPage(whichPage, delta, duration, immediate, interpolator);
+ return snapToPage(whichPage, delta, duration, immediate, interpolator);
}
- protected void snapToPage(int whichPage, int delta, int duration) {
- snapToPage(whichPage, delta, duration, false, null);
+ protected boolean snapToPage(int whichPage, int delta, int duration) {
+ return snapToPage(whichPage, delta, duration, false, null);
}
- protected void snapToPage(int whichPage, int delta, int duration, boolean immediate,
+ protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
TimeInterpolator interpolator) {
whichPage = validateNewPage(whichPage);
@@ -1726,6 +1677,7 @@
}
invalidate();
+ return Math.abs(delta) > 0;
}
public void scrollLeft() {
@@ -1935,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/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
index 07515d0..ea9f752 100644
--- a/src/com/android/launcher3/PromiseAppInfo.java
+++ b/src/com/android/launcher3/PromiseAppInfo.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
@@ -46,7 +47,7 @@
return shortcut;
}
- public Intent getMarketIntent() {
- return PackageManagerHelper.getMarketIntent(componentName.getPackageName());
+ public Intent getMarketIntent(Context context) {
+ return new PackageManagerHelper(context).getMarketIntent(componentName.getPackageName());
}
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 841c0cd..1a63326 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,6 +23,7 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout.ContainerType;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
public class ShortcutAndWidgetContainer extends ViewGroup {
static final String TAG = "ShortcutAndWidgetContainer";
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index e6bc770..31dab97 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,15 +28,16 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.Color;
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;
import android.os.PowerManager;
import android.os.TransactionTooLargeException;
+import android.support.v4.os.BuildCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
@@ -44,11 +45,8 @@
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
-import android.util.SparseArray;
import android.util.TypedValue;
import android.view.View;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.config.FeatureFlags;
@@ -83,6 +81,8 @@
private static final Matrix sMatrix = new Matrix();
private static final Matrix sInverseMatrix = new Matrix();
+ public static final boolean ATLEAST_P = BuildCompat.isAtLeastP();
+
public static final boolean ATLEAST_OREO_MR1 =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1;
@@ -233,21 +233,45 @@
return new int[] {sLoc1[0] - sLoc0[0], sLoc1[1] - sLoc0[1]};
}
+ public static void scaleRectFAboutCenter(RectF r, float scale) {
+ if (scale != 1.0f) {
+ float cx = r.centerX();
+ float cy = r.centerY();
+ r.offset(-cx, -cy);
+ r.left = r.left * scale;
+ r.top = r.top * scale ;
+ r.right = r.right * scale;
+ r.bottom = r.bottom * scale;
+ r.offset(cx, cy);
+ }
+ }
+
public static void scaleRectAboutCenter(Rect r, float scale) {
if (scale != 1.0f) {
int cx = r.centerX();
int cy = r.centerY();
r.offset(-cx, -cy);
+ scaleRect(r, scale);
+ r.offset(cx, cy);
+ }
+ }
+ public static void scaleRect(Rect r, float scale) {
+ if (scale != 1.0f) {
r.left = (int) (r.left * scale + 0.5f);
r.top = (int) (r.top * scale + 0.5f);
r.right = (int) (r.right * scale + 0.5f);
r.bottom = (int) (r.bottom * scale + 0.5f);
-
- r.offset(cx, cy);
}
}
+ public static void insetRect(Rect r, Rect insets) {
+ r.left = Math.min(r.right, r.left + insets.left);
+ r.top = Math.min(r.bottom, r.top + insets.top);
+ r.right = Math.max(r.left, r.right - insets.right);
+ r.bottom = Math.max(r.top, r.bottom - insets.bottom);
+ }
+
public static float shrinkRect(Rect r, float scaleX, float scaleY) {
float scale = Math.min(Math.min(scaleX, scaleY), 1.0f);
if (scale < 1.0f) {
@@ -262,6 +286,10 @@
return scale;
}
+ public static float mapRange(float value, float min, float max) {
+ return min + (value * (max - min));
+ }
+
public static boolean isSystemApp(Context context, Intent intent) {
PackageManager pm = context.getPackageManager();
ComponentName cn = intent.getComponent();
@@ -287,89 +315,6 @@
}
}
- /**
- * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
- * @param bitmap The bitmap to scan
- * @param samples The approximate max number of samples to use.
- */
- public static int findDominantColorByHue(Bitmap bitmap, int samples) {
- final int height = bitmap.getHeight();
- final int width = bitmap.getWidth();
- int sampleStride = (int) Math.sqrt((height * width) / samples);
- if (sampleStride < 1) {
- sampleStride = 1;
- }
-
- // This is an out-param, for getting the hsv values for an rgb
- float[] hsv = new float[3];
-
- // First get the best hue, by creating a histogram over 360 hue buckets,
- // where each pixel contributes a score weighted by saturation, value, and alpha.
- float[] hueScoreHistogram = new float[360];
- float highScore = -1;
- int bestHue = -1;
-
- int[] pixels = new int[samples];
- int pixelCount = 0;
-
- for (int y = 0; y < height; y += sampleStride) {
- for (int x = 0; x < width; x += sampleStride) {
- int argb = bitmap.getPixel(x, y);
- int alpha = 0xFF & (argb >> 24);
- if (alpha < 0x80) {
- // Drop mostly-transparent pixels.
- continue;
- }
- // Remove the alpha channel.
- int rgb = argb | 0xFF000000;
- Color.colorToHSV(rgb, hsv);
- // Bucket colors by the 360 integer hues.
- int hue = (int) hsv[0];
- if (hue < 0 || hue >= hueScoreHistogram.length) {
- // Defensively avoid array bounds violations.
- continue;
- }
- if (pixelCount < samples) {
- pixels[pixelCount++] = rgb;
- }
- float score = hsv[1] * hsv[2];
- hueScoreHistogram[hue] += score;
- if (hueScoreHistogram[hue] > highScore) {
- highScore = hueScoreHistogram[hue];
- bestHue = hue;
- }
- }
- }
-
- SparseArray<Float> rgbScores = new SparseArray<>();
- int bestColor = 0xff000000;
- highScore = -1;
- // Go back over the RGB colors that match the winning hue,
- // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
- // The highest-scoring RGB color wins.
- for (int i = 0; i < pixelCount; i++) {
- int rgb = pixels[i];
- Color.colorToHSV(rgb, hsv);
- int hue = (int) hsv[0];
- if (hue == bestHue) {
- float s = hsv[1];
- float v = hsv[2];
- int bucket = (int) (s * 100) + (int) (v * 10000);
- // Score by cumulative saturation * value.
- float score = s * v;
- Float oldTotal = rgbScores.get(bucket);
- float newTotal = oldTotal == null ? score : oldTotal + score;
- rgbScores.put(bucket, newTotal);
- if (newTotal > highScore) {
- highScore = newTotal;
- // All the colors in the winning bucket are very similar. Last in wins.
- bestColor = rgb;
- }
- }
- }
- return bestColor;
- }
-
/*
* Finds a system apk which had a broadcast receiver listening to a particular action.
* @param action intent action used to find the apk
@@ -614,23 +559,6 @@
return c == null || c.isEmpty();
}
- public static boolean isAccessibilityEnabled(Context context) {
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- return accessibilityManager.isEnabled();
- }
-
- public static void sendCustomAccessibilityEvent(View target, int type, String text) {
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- target.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(type);
- target.onInitializeAccessibilityEvent(event);
- event.getText().add(text);
- accessibilityManager.sendAccessibilityEvent(event);
- }
- }
-
public static boolean isBinderSizeError(Exception e) {
return e.getCause() instanceof TransactionTooLargeException
|| e.getCause() instanceof DeadObjectException;
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index bdfeae1..a658d58 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -469,8 +469,11 @@
}
RectF boxRect = drawBoxWithShadow(c, size, size);
- Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow(
- mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, 0);
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ Bitmap icon = li.createScaledBitmapWithoutShadow(
+ mutateOnMainThread(info.getFullResIcon(mIconCache)), 0);
+ li.recycle();
+
Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
boxRect.set(0, 0, iconSize, iconSize);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 0db5a16..1414946 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -22,11 +22,9 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.Utilities.isAccessibilityEnabled;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -49,12 +47,12 @@
import android.os.UserHandle;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.Property;
import android.util.SparseArray;
import android.view.LayoutInflater;
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;
@@ -63,7 +61,7 @@
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
-import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.badge.FolderBadgeInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -78,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;
@@ -89,8 +88,10 @@
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import java.util.ArrayList;
import java.util.HashSet;
@@ -101,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
@@ -178,31 +178,6 @@
private SpringLoadedDragController mSpringLoadedDragController;
- // Direction used for moving the workspace and hotseat UI
- public enum Direction {
- X (TRANSLATION_X),
- Y (TRANSLATION_Y);
-
- private final Property<View, Float> viewProperty;
-
- Direction(Property<View, Float> viewProperty) {
- this.viewProperty = viewProperty;
- }
- }
-
- private static final int HOTSEAT_STATE_ALPHA_INDEX = 2;
-
- /**
- * These values correspond to {@link Direction#X} & {@link Direction#Y}
- */
- private final float[] mPageAlpha = new float[] {1, 1};
- /**
- * Hotseat alpha can be changed when moving horizontally, vertically, changing states.
- * The values correspond to {@link Direction#X}, {@link Direction#Y} &
- * {@link #HOTSEAT_STATE_ALPHA_INDEX} respectively.
- */
- private final float[] mHotseatAlpha = new float[] {1, 1, 1};
-
private boolean mIsSwitchingState = false;
boolean mChildrenLayersEnabled = true;
@@ -210,7 +185,7 @@
private boolean mStripScreensOnPageStopMoving = false;
private DragPreviewProvider mOutlineProvider = null;
- private final boolean mWorkspaceFadeInAdjacentScreens;
+ private boolean mWorkspaceFadeInAdjacentScreens;
final WallpaperOffsetInterpolator mWallpaperOffset;
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
@@ -261,6 +236,7 @@
boolean mStartedSendingScrollEvents;
float mLastOverlayScroll = 0;
boolean mOverlayShown = false;
+ private Runnable mOnOverlayHiddenCallback;
private boolean mForceDrawAdjacentPages = false;
private boolean mPageRearrangeEnabled = false;
@@ -295,15 +271,11 @@
mLauncher = Launcher.getLauncher(context);
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
- DeviceProfile grid = mLauncher.getDeviceProfile();
- mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
mWallpaperManager = WallpaperManager.getInstance(context);
mWallpaperOffset = new WallpaperOffsetInterpolator(this);
- setOnHierarchyChangeListener(this);
setHapticFeedbackEnabled(false);
-
initWorkspace();
// Disable multitouch across the workspace/all apps/customize tray
@@ -313,6 +285,30 @@
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
+
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
+ mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
+
+ Rect padding = grid.workspacePadding;
+ setPadding(padding.left, padding.top, padding.right, padding.bottom);
+
+ if (grid.shouldFadeAdjacentWorkspaceScreens()) {
+ // In landscape mode the page spacing is set to the default.
+ setPageSpacing(grid.defaultPageSpacingPx);
+ } else {
+ // In portrait, we want the pages spaced such that there is no
+ // overhang of the previous / next page into the current page viewport.
+ // We assume symmetrical padding in portrait mode.
+ setPageSpacing(Math.max(grid.defaultPageSpacingPx, padding.left + 1));
+ }
+
+ int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
+ int paddingBottom = grid.cellLayoutBottomPaddingPx;
+ for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
+ mWorkspaceScreens.valueAt(i)
+ .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+ }
}
/**
@@ -425,6 +421,7 @@
}
updateChildrenLayersEnabled();
+ mDragInfo = null;
mOutlineProvider = null;
mDragSourceInternal = null;
}
@@ -434,13 +431,9 @@
*/
protected void initWorkspace() {
mCurrentPage = DEFAULT_PAGE;
- DeviceProfile grid = mLauncher.getDeviceProfile();
- setWillNotDraw(false);
- setClipChildren(false);
setClipToPadding(false);
setupLayoutTransition();
- mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
// Set the wallpaper dimensions when Launcher starts up
setWallpaperDimension();
@@ -470,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.");
}
@@ -478,7 +471,7 @@
cl.setOnInterceptTouchListener(this);
cl.setClickable(true);
cl.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
- super.onChildViewAdded(parent, child);
+ super.onViewAdded(child);
}
boolean isTouchActive() {
@@ -1137,7 +1130,7 @@
mLauncherOverlay.onScrollInteractionBegin();
}
- mLastOverlayScroll = Math.abs(amount / getViewportWidth());
+ mLastOverlayScroll = Math.abs(amount / getMeasuredWidth());
mLauncherOverlay.onScrollChange(mLastOverlayScroll, mIsRtl);
} else {
dampedOverScroll(amount);
@@ -1172,6 +1165,7 @@
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
}
mOverlayShown = false;
+ tryRunOverlayCallback();
}
float offset = 0f;
float slip = 0f;
@@ -1191,93 +1185,61 @@
// TODO(adamcohen): figure out a final effect here. We may need to recommend
// different effects based on device performance. On at least one relatively high-end
// device I've tried, translating the launcher causes things to get quite laggy.
- setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
- setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
+ mLauncher.getDragLayer().setTranslationX(transX);
+ mLauncher.getDragLayer().setAlpha(alpha);
}
/**
- * Moves the workspace UI in the Y direction.
- * @param translation the amount of shift.
- * @param alpha the alpha for the workspace page
+ * @return false if the callback is still pending
*/
- public void setWorkspaceYTranslationAndAlpha(float translation, float alpha) {
- setWorkspaceTranslationAndAlpha(Direction.Y, translation, alpha);
+ 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;
}
/**
- * Moves the workspace UI in the provided direction.
- * @param direction the direction to move the workspace
- * @param translation the amount of shift.
- * @param alpha the alpha for the workspace page
+ * 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.
*/
- private void setWorkspaceTranslationAndAlpha(Direction direction, float translation, float alpha) {
- Property<View, Float> property = direction.viewProperty;
- mPageAlpha[direction.ordinal()] = alpha;
- float finalAlpha = mPageAlpha[0] * mPageAlpha[1];
-
- View currentChild = getChildAt(getCurrentPage());
- if (currentChild != null) {
- property.set(currentChild, translation);
- currentChild.setAlpha(finalAlpha);
- }
-
- // When the animation finishes, reset all pages, just in case we missed a page.
- if (Float.compare(translation, 0) == 0) {
- for (int i = getChildCount() - 1; i >= 0; i--) {
- View child = getChildAt(i);
- property.set(child, translation);
- child.setAlpha(finalAlpha);
- }
- }
- }
-
- /**
- * Moves the Hotseat UI in the provided direction.
- * @param direction the direction to move the workspace
- * @param translation the amount of shift.
- * @param alpha the alpha for the hotseat page
- */
- public void setHotseatTranslationAndAlpha(Direction direction, float translation, float alpha) {
- Property<View, Float> property = direction.viewProperty;
- // Skip the page indicator movement in the vertical bar layout
- if (direction != Direction.Y || !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- property.set(mPageIndicator, translation);
- }
- property.set(mLauncher.getHotseat(), translation);
- setHotseatAlphaAtIndex(alpha, direction.ordinal());
- }
-
- private void setHotseatAlphaAtIndex(float alpha, int index) {
- mHotseatAlpha[index] = alpha;
- final float hotseatAlpha = mHotseatAlpha[0] * mHotseatAlpha[1] * mHotseatAlpha[2];
- final float pageIndicatorAlpha = mHotseatAlpha[0] * mHotseatAlpha[2];
-
- mLauncher.getHotseat().setAlpha(hotseatAlpha);
- mPageIndicator.setAlpha(pageIndicatorAlpha);
- }
-
- public ValueAnimator createHotseatAlphaAnimator(float finalValue) {
- if (Float.compare(finalValue, mHotseatAlpha[HOTSEAT_STATE_ALPHA_INDEX]) == 0) {
- // Return a dummy animator to avoid null checks.
- return ValueAnimator.ofFloat(0, 0);
+ public boolean runOnOverlayHidden(Runnable callback) {
+ if (mOnOverlayHiddenCallback == null) {
+ mOnOverlayHiddenCallback = callback;
} else {
- ValueAnimator animator = ValueAnimator
- .ofFloat(mHotseatAlpha[HOTSEAT_STATE_ALPHA_INDEX], finalValue);
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float value = (Float) valueAnimator.getAnimatedValue();
- setHotseatAlphaAtIndex(value, HOTSEAT_STATE_ALPHA_INDEX);
- }
- });
-
- final boolean accessibilityEnabled = isAccessibilityEnabled(mLauncher);
- animator.addUpdateListener(
- new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled));
- animator.addUpdateListener(
- new AlphaUpdateListener(mPageIndicator, accessibilityEnabled));
- return animator;
+ // 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
@@ -1347,7 +1309,7 @@
private void updatePageAlphaValues() {
if (!workspaceInModalState() && !mIsSwitchingState) {
- int screenCenter = getScrollX() + getViewportWidth() / 2;
+ int screenCenter = getScrollX() + getMeasuredWidth() / 2;
for (int i = 0; i < getChildCount(); i++) {
CellLayout child = (CellLayout) getChildAt(i);
if (child != null) {
@@ -1407,7 +1369,7 @@
/** Returns whether a drag should be allowed to be started from the current workspace state. */
public boolean workspaceIconsCanBeDragged() {
- return mLauncher.isInState(NORMAL) || mLauncher.isInState(SPRING_LOADED);
+ return mLauncher.getStateManager().getState().workspaceIconsCanBeDragged;
}
private void updateChildrenLayersEnabled() {
@@ -1431,8 +1393,8 @@
if (mChildrenLayersEnabled) {
final int screenCount = getChildCount();
- float visibleLeft = getViewportOffsetX();
- float visibleRight = visibleLeft + getViewportWidth();
+ float visibleLeft = 0;
+ float visibleRight = visibleLeft + getMeasuredWidth();
float scaleX = getScaleX();
if (scaleX < 1 && scaleX > 0) {
float mid = getMeasuredWidth() / 2;
@@ -1564,10 +1526,10 @@
* Sets the current workspace {@link LauncherState}, then animates the UI
*/
@Override
- public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
- AnimatorSet anim, AnimationConfig config) {
+ public void setStateWithAnimation(LauncherState toState,
+ AnimatorSetBuilder builder, AnimationConfig config) {
StateTransitionListener listener = new StateTransitionListener(toState);
- mStateTransitionAnimation.setStateWithAnimation(toState, anim, layerViews, config);
+ mStateTransitionAnimation.setStateWithAnimation(toState, builder, config);
// Invalidate the pages now, so that we have the visible pages before the
// animation is started
@@ -1579,8 +1541,8 @@
ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
stepAnimator.addUpdateListener(listener);
stepAnimator.setDuration(config.duration);
- anim.play(stepAnimator);
- anim.addListener(listener);
+ stepAnimator.addListener(listener);
+ builder.play(stepAnimator);
}
public void updateAccessibilityFlags() {
@@ -1717,7 +1679,7 @@
if (popupContainer != null) {
dragOptions.preDragCondition = popupContainer.createPreDragCondition();
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("dragging started");
}
}
@@ -2180,7 +2142,7 @@
// Use the absolute left instead of the child left, as we want the visible area
// irrespective of the visible child. Since the view can only scroll horizontally, the
// top position is not affected.
- mTempXY[0] = getViewportOffsetX() + getPaddingLeft() + boundingLayout.getLeft();
+ mTempXY[0] = getPaddingLeft() + boundingLayout.getLeft();
mTempXY[1] = child.getTop() + boundingLayout.getTop();
float scale = mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY);
@@ -2754,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;
@@ -3444,6 +3412,18 @@
}
@Override
+ public int getExpectedHeight() {
+ return getMeasuredHeight() <= 0 || !mIsLayoutValid
+ ? mLauncher.getDeviceProfile().heightPx : getMeasuredHeight();
+ }
+
+ @Override
+ public int getExpectedWidth() {
+ return getMeasuredWidth() <= 0 || !mIsLayoutValid
+ ? mLauncher.getDeviceProfile().widthPx : getMeasuredWidth();
+ }
+
+ @Override
protected String getPageIndicatorDescription() {
return getResources().getString(R.string.all_apps_button_label);
}
@@ -3519,17 +3499,17 @@
mRefreshPending = false;
- mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) {
- mLauncher.removeItem(view, info, false /* deleteFromDb */);
- mLauncher.bindAppWidget((LauncherAppWidgetInfo) info);
- }
- // process all the shortcuts
- return false;
+ ArrayList<PendingAppWidgetHostView> views = new ArrayList<>(mInfos.size());
+ mapOverItems(MAP_NO_RECURSE, (info, view) -> {
+ if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) {
+ views.add((PendingAppWidgetHostView) view);
}
+ // process all children
+ return false;
});
+ for (PendingAppWidgetHostView view : views) {
+ view.reInflate();
+ }
}
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 8edec40..21f5d67 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,21 +18,19 @@
import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
import android.util.Property;
import android.view.View;
-import android.view.accessibility.AccessibilityManager;
+import com.android.launcher3.LauncherState.PageAlphaProvider;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
/**
@@ -97,28 +95,23 @@
private final Launcher mLauncher;
private final Workspace mWorkspace;
- private final boolean mWorkspaceFadeInAdjacentScreens;
-
private float mNewScale;
public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
mLauncher = launcher;
mWorkspace = workspace;
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
- Resources res = launcher.getResources();
- mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha);
- mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
+ mWorkspaceScrimAlpha = launcher.getResources()
+ .getInteger(R.integer.config_workspaceScrimAlpha);
}
public void setState(LauncherState toState) {
setWorkspaceProperty(toState, NO_ANIM_PROPERTY_SETTER);
}
- public void setStateWithAnimation(LauncherState toState, AnimatorSet anim,
- AnimationLayerSet layerViews, AnimationConfig config) {
+ public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
+ AnimationConfig config) {
AnimatedPropertySetter propertySetter =
- new AnimatedPropertySetter(config.duration, layerViews, anim);
+ new AnimatedPropertySetter(config.duration, builder);
setWorkspaceProperty(toState, propertySetter);
}
@@ -130,20 +123,23 @@
* Starts a transition animation for the workspace.
*/
private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter) {
- float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher);
- mNewScale = scaleAndTranslationY[0];
- final float finalWorkspaceTranslationY = scaleAndTranslationY[1];
-
- int toPage = mWorkspace.getPageNearestToCenterOfScreen();
+ float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
+ mNewScale = scaleAndTranslation[0];
+ PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
for (int i = 0; i < childCount; i++) {
- applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, toPage,
+ applyChildState(state, (CellLayout) mWorkspace.getChildAt(i), i, pageAlphaProvider,
propertySetter);
}
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN);
+ propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
+ scaleAndTranslation[1], Interpolators.ZOOM_IN);
propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
- finalWorkspaceTranslationY, Interpolators.ZOOM_IN);
+ scaleAndTranslation[2], Interpolators.ZOOM_IN);
+
+ propertySetter.setViewAlpha(mLauncher.getHotseat(), state.getHoseatAlpha(mLauncher),
+ pageAlphaProvider.interpolator);
// Set scrim
propertySetter.setInt(mLauncher.getDragLayer().getScrim(), DRAWABLE_ALPHA,
@@ -151,32 +147,26 @@
}
public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
- applyChildState(state, cl, childIndex, mWorkspace.getPageNearestToCenterOfScreen(),
+ applyChildState(state, cl, childIndex, state.getWorkspacePageAlphaProvider(mLauncher),
NO_ANIM_PROPERTY_SETTER);
}
private void applyChildState(LauncherState state, CellLayout cl, int childIndex,
- int centerPage, PropertySetter propertySetter) {
- propertySetter.setInt(cl.getScrimBackground(),
- DRAWABLE_ALPHA, state.hasScrim ? 255 : 0, Interpolators.ZOOM_IN);
+ PageAlphaProvider pageAlphaProvider, PropertySetter propertySetter) {
+ float pageAlpha = pageAlphaProvider.getPageAlpha(childIndex);
+ int drawableAlpha = Math.round(pageAlpha * (state.hasScrim ? 255 : 0));
- // Only animate the page alpha when we actually fade pages
- if (mWorkspaceFadeInAdjacentScreens) {
- float finalAlpha = state == LauncherState.NORMAL && childIndex != centerPage ? 0 : 1f;
- propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
- finalAlpha, Interpolators.ZOOM_IN);
- }
+ propertySetter.setInt(cl.getScrimBackground(),
+ DRAWABLE_ALPHA, drawableAlpha, Interpolators.ZOOM_IN);
+ propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
+ pageAlpha, pageAlphaProvider.interpolator);
}
public static class PropertySetter {
- public void setViewAlpha(Animator anim, View view, float alpha) {
- if (anim != null) {
- anim.end();
- return;
- }
+ public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
view.setAlpha(alpha);
- AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view));
+ AlphaUpdateListener.updateVisibility(view, isAccessibilityEnabled(view.getContext()));
}
public <T> void setFloat(T target, Property<T, Float> property, float value,
@@ -188,39 +178,27 @@
TimeInterpolator interpolator) {
property.set(target, value);
}
-
- protected boolean isAccessibilityEnabled(View v) {
- AccessibilityManager am = (AccessibilityManager)
- v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- return am.isEnabled();
- }
}
public static class AnimatedPropertySetter extends PropertySetter {
private final long mDuration;
- private final AnimationLayerSet mLayerViews;
- private final AnimatorSet mStateAnimator;
+ private final AnimatorSetBuilder mStateAnimator;
- public AnimatedPropertySetter(
- long duration, AnimationLayerSet layerView, AnimatorSet anim) {
+ public AnimatedPropertySetter(long duration, AnimatorSetBuilder builder) {
mDuration = duration;
- mLayerViews = layerView;
- mStateAnimator = anim;
+ mStateAnimator = builder;
}
@Override
- public void setViewAlpha(Animator anim, View view, float alpha) {
- if (anim == null) {
- if (view.getAlpha() == alpha) {
- return;
- }
- anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
- anim.addListener(new AlphaUpdateListener(view, isAccessibilityEnabled(view)));
+ public void setViewAlpha(View view, float alpha, TimeInterpolator interpolator) {
+ if (view.getAlpha() == alpha) {
+ return;
}
-
- anim.setDuration(mDuration).setInterpolator(getFadeInterpolator(alpha));
- mLayerViews.addView(view);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.ALPHA, alpha);
+ anim.addListener(new AlphaUpdateListener(
+ view, isAccessibilityEnabled(view.getContext())));
+ anim.setDuration(mDuration).setInterpolator(interpolator);
mStateAnimator.play(anim);
}
diff --git a/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java b/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
index 99deb7b..fdf2786 100644
--- a/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
+++ b/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
@@ -16,10 +16,10 @@
package com.android.launcher3.accessibility;
-import android.content.Context;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
+
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.Launcher;
@@ -59,11 +59,6 @@
}
public static DragViewStateAnnouncer createFor(View v) {
- if (((AccessibilityManager) v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE))
- .isEnabled()) {
- return new DragViewStateAnnouncer(v);
- } else {
- return null;
- }
+ return isAccessibilityEnabled(v.getContext()) ? new DragViewStateAnnouncer(v) : null;
}
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 512db72..3b6fea9 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -21,20 +21,17 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FolderInfo;
-import com.android.launcher3.InfoDropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.UninstallDropTarget;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index efd7b97..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;
@@ -32,21 +30,24 @@
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.ClickShadowView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Insettable;
+import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
@@ -54,47 +55,31 @@
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.TransformingTouchDelegate;
-import com.android.launcher3.views.SlidingTabStrip;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Set;
+import com.android.launcher3.views.BottomUserEducationView;
/**
* The all apps view container.
*/
public class AllAppsContainerView extends RelativeLayout implements DragSource,
- View.OnLongClickListener, Insettable, DeviceProfile.LauncherLayoutChangeListener,
- BubbleTextView.BubbleTextShadowHandler {
-
- protected final Rect mBasePadding = new Rect();
+ OnLongClickListener, Insettable, BubbleTextShadowHandler, OnDeviceProfileChangeListener {
private final Launcher mLauncher;
private final AdapterHolder[] mAH;
private final ClickShadowView mTouchFeedbackView;
private final ItemInfoMatcher mPersonalMatcher = ItemInfoMatcher.ofUser(Process.myUserHandle());
private final ItemInfoMatcher mWorkMatcher = ItemInfoMatcher.not(mPersonalMatcher);
+ private final AllAppsStore mAllAppsStore = new AllAppsStore();
private SearchUiManager mSearchUiManager;
private View mSearchContainer;
- private InterceptingViewPager mViewPager;
- private ViewGroup mHeader;
- private FloatingHeaderHandler mFloatingHeaderHandler;
+ private AllAppsPagedView mViewPager;
+ private FloatingHeaderView mHeader;
private SpannableStringBuilder mSearchQueryBuilder = null;
- private int mNumAppsPerRow;
- private int mNumPredictedAppsPerRow;
-
- private TransformingTouchDelegate mTouchDelegate;
private boolean mUsingTabs;
-
- private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
+ private boolean mSearchModeWhileUsingTabs = false;
public AllAppsContainerView(Context context) {
this(context, null);
@@ -108,6 +93,7 @@
super(context, attrs, defStyleAttr);
mLauncher = Launcher.getLauncher(context);
+ mLauncher.addOnDeviceProfileChangeListener(this);
mSearchQueryBuilder = new SpannableStringBuilder();
@@ -120,54 +106,24 @@
addView(mTouchFeedbackView, size, size);
mAH = new AdapterHolder[2];
- mAH[AdapterHolder.MAIN] = new AdapterHolder();
- mAH[AdapterHolder.WORK] = new AdapterHolder();
+ mAH[AdapterHolder.MAIN] = new AdapterHolder(false /* isWork */);
+ mAH[AdapterHolder.WORK] = new AdapterHolder(true /* isWork */);
+
+ mAllAppsStore.addUpdateListener(this::onAppsUpdated);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
-
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- grid.addLauncherLayoutChangedListener(this);
-
applyTouchDelegate();
}
private void applyTouchDelegate() {
- RecyclerView rv = getActiveRecyclerView();
- mTouchDelegate = new TransformingTouchDelegate(rv);
- mTouchDelegate.setBounds(
- rv.getLeft() - mBasePadding.left,
- rv.getTop() - mBasePadding.top,
- rv.getRight() + mBasePadding.right,
- rv.getBottom() + mBasePadding.bottom);
- setTouchDelegate(mTouchDelegate);
+ // TODO: Reimplement once fast scroller is fixed.
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
-
- DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
- grid.removeLauncherLayoutChangedListener(this);
- }
-
- /**
- * Calculate the background padding as it can change due to insets/content padding change.
- */
- @Override
- public void onLauncherLayoutChanged() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- if (!grid.isVerticalBarLayout()) {
- return;
- }
-
- int[] padding = grid.getContainerPadding();
- int paddingLeft = padding[0];
- int paddingRight = padding[1];
- mBasePadding.set(paddingLeft, 0, paddingRight, 0);
- setPadding(paddingLeft, 0, paddingRight, 0);
+ public AllAppsStore getAppsStore() {
+ return mAllAppsStore;
}
@Override
@@ -177,77 +133,35 @@
}
@Override
- public void setPressedIcon(BubbleTextView icon, Bitmap background) {
- mTouchFeedbackView.setPressedIcon(icon, background);
- }
-
- /**
- * Sets the current set of apps.
- */
- public void setApps(List<AppInfo> apps) {
- boolean hasWorkProfileApp = hasWorkProfileApp(apps);
- if (mUsingTabs != hasWorkProfileApp) {
- rebindAdapters(hasWorkProfileApp);
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ for (AdapterHolder holder : mAH) {
+ if (holder.recyclerView != null) {
+ // Remove all views and clear the pool, while keeping the data same. After this
+ // call, all the viewHolders will be recreated.
+ holder.recyclerView.swapAdapter(holder.recyclerView.getAdapter(), true);
+ holder.recyclerView.getRecycledViewPool().clear();
+ }
}
- mComponentToAppMap.clear();
- addOrUpdateApps(apps);
- }
-
- /**
- * Adds or updates existing apps in the list
- */
- public void addOrUpdateApps(List<AppInfo> apps) {
- for (AppInfo app : apps) {
- mComponentToAppMap.put(app.toComponentKey(), app);
- }
- onAppsUpdated();
- mSearchUiManager.refreshSearchResult();
- }
-
- /**
- * Removes some apps from the list.
- */
- public void removeApps(List<AppInfo> apps) {
- for (AppInfo app : apps) {
- mComponentToAppMap.remove(app.toComponentKey());
- }
- onAppsUpdated();
- mSearchUiManager.refreshSearchResult();
}
private void onAppsUpdated() {
- for (int i = 0; i < getNumOfAdapters(); i++) {
- mAH[i].appsList.onAppsUpdated();
- }
- }
-
- private int getNumOfAdapters() {
- return mUsingTabs ? mAH.length : 1;
- }
-
- public void updatePromiseAppProgress(PromiseAppInfo app) {
- for (int i = 0; i < mAH.length; i++) {
- updatePromiseAppProgress(app, mAH[i].recyclerView);
- }
- if (mFloatingHeaderHandler != null) {
- updatePromiseAppProgress(app, mFloatingHeaderHandler.getContentView());
- }
- }
-
- private void updatePromiseAppProgress(PromiseAppInfo app, ViewGroup parent) {
- if (parent == null) {
- return;
- }
- int childCount = parent.getChildCount();
- for (int i = 0; i < childCount; i++) {
- View child = parent.getChildAt(i);
- if (child instanceof BubbleTextView && child.getTag() == app) {
- BubbleTextView bubbleTextView = (BubbleTextView) child;
- bubbleTextView.applyProgressLevel(app.level);
+ if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
+ boolean hasWorkApps = false;
+ for (AppInfo app : mAllAppsStore.getApps()) {
+ if (mWorkMatcher.matches(app, null)) {
+ hasWorkApps = true;
+ break;
+ }
}
+ rebindAdapters(hasWorkApps);
}
}
+ @Override
+ public void setPressedIcon(BubbleTextView icon, Bitmap background) {
+ mTouchFeedbackView.setPressedIcon(icon, background);
+ }
+
/**
* Returns whether the view itself will handle the touch event or not.
*/
@@ -257,15 +171,12 @@
if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
return true;
}
- if (mUsingTabs && mLauncher.getDragLayer().isEventOverView(mHeader, ev)) {
- return true;
- }
AllAppsRecyclerView rv = getActiveRecyclerView();
return rv == null || rv.shouldContainerScroll(ev, mLauncher.getDragLayer());
}
public AllAppsRecyclerView getActiveRecyclerView() {
- if (!mUsingTabs || mViewPager.getCurrentItem() == 0) {
+ if (!mUsingTabs || mViewPager.getNextPage() == 0) {
return mAH[AdapterHolder.MAIN].recyclerView;
} else {
return mAH[AdapterHolder.WORK].recyclerView;
@@ -281,6 +192,9 @@
mAH[i].recyclerView.scrollToTop();
}
}
+ if (isHeaderVisible()) {
+ mHeader.reset();
+ }
// Reset the search bar and base recycler view after transitioning home
mSearchUiManager.reset();
}
@@ -291,23 +205,18 @@
// This is a focus listener that proxies focus from a view into the list view. This is to
// work around the search box from getting first focus and showing the cursor.
- setOnFocusChangeListener(new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View v, boolean hasFocus) {
- if (hasFocus && getActiveRecyclerView() != null) {
- getActiveRecyclerView().requestFocus();
- }
+ setOnFocusChangeListener((v, hasFocus) -> {
+ if (hasFocus && getActiveRecyclerView() != null) {
+ getActiveRecyclerView().requestFocus();
}
});
mHeader = findViewById(R.id.all_apps_header);
- rebindAdapters(mUsingTabs);
+ rebindAdapters(mUsingTabs, true /* force */);
mSearchContainer = findViewById(R.id.search_container_all_apps);
mSearchUiManager = (SearchUiManager) mSearchContainer;
mSearchUiManager.initialize(this);
-
- onLauncherLayoutChanged();
}
public SearchUiManager getSearchUiManager() {
@@ -315,23 +224,6 @@
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- // Update the number of items in the grid before we measure the view
- grid.updateAppsViewNumCols();
-
- if (mNumAppsPerRow != grid.inv.numColumns ||
- mNumPredictedAppsPerRow != grid.inv.numColumns) {
- mNumAppsPerRow = grid.inv.numColumns;
- mNumPredictedAppsPerRow = grid.inv.numColumns;
- for (int i = 0; i < mAH.length; i++) {
- mAH[i].applyNumsPerRow();
- }
- }
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
-
- @Override
public boolean dispatchKeyEvent(KeyEvent event) {
mSearchUiManager.preDispatchKeyEvent(event);
return super.dispatchKeyEvent(event);
@@ -379,41 +271,33 @@
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = mLauncher.getDeviceProfile();
+ int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
+ + grid.cellLayoutPaddingLeftRightPx;
+
for (int i = 0; i < mAH.length; i++) {
mAH[i].padding.bottom = insets.bottom;
+ mAH[i].padding.left = mAH[i].padding.right = leftRightPadding;
mAH[i].applyPadding();
}
+
+ ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
if (grid.isVerticalBarLayout()) {
- ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.leftMargin = insets.left;
mlp.topMargin = insets.top;
mlp.rightMargin = insets.right;
- setLayoutParams(mlp);
+ setPadding(grid.workspacePadding.left, 0, grid.workspacePadding.right, 0);
} else {
- View navBarBg = findViewById(R.id.nav_bar_bg);
- ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
- navBarBgLp.height = insets.bottom;
- navBarBg.setLayoutParams(navBarBgLp);
+ mlp.leftMargin = mlp.rightMargin = mlp.topMargin = 0;
+ setPadding(0, 0, 0, 0);
}
- }
+ setLayoutParams(mlp);
- public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
- final PackageUserKey packageUserKey = new PackageUserKey(null, null);
- for (int j = 0; j < mAH.length; j++) {
- if (mAH[j].recyclerView != null) {
- final int n = mAH[j].recyclerView.getChildCount();
- for (int i = 0; i < n; i++) {
- View child = mAH[j].recyclerView.getChildAt(i);
- if (!(child instanceof BubbleTextView) || !(child.getTag() instanceof ItemInfo)) {
- continue;
- }
- ItemInfo info = (ItemInfo) child.getTag();
- if (packageUserKey.updateFromItemInfo(info) && updatedBadges.contains(packageUserKey)) {
- ((BubbleTextView) child).applyBadgeState(info, true /* animate */);
- }
- }
- }
- }
+ View navBarBg = findViewById(R.id.nav_bar_bg);
+ ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
+ navBarBgLp.height = insets.bottom;
+ navBarBg.setLayoutParams(navBarBgLp);
+
+ InsettableFrameLayout.dispatchInsets(this, insets);
}
public SpringAnimationHandler getSpringAnimationHandler() {
@@ -421,36 +305,35 @@
}
private void rebindAdapters(boolean showTabs) {
- if (showTabs != mUsingTabs) {
- replaceRVContainer(showTabs);
+ rebindAdapters(showTabs, false /* force */);
+ }
+
+ private void rebindAdapters(boolean showTabs, boolean force) {
+ if (showTabs == mUsingTabs && !force) {
+ return;
}
+ replaceRVContainer(showTabs);
mUsingTabs = showTabs;
+ mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
+ mAllAppsStore.unregisterIconContainer(mAH[AdapterHolder.WORK].recyclerView);
+
if (mUsingTabs) {
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
- setupWorkProfileTabs();
- setupHeader();
- mHeader.setVisibility(View.VISIBLE);
+ onTabChanged(mViewPager.getNextPage());
} else {
- mHeader.setVisibility(View.GONE);
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
+ mAH[AdapterHolder.WORK].recyclerView = null;
}
+ setupHeader();
+
+ mAllAppsStore.registerIconContainer(mAH[AdapterHolder.MAIN].recyclerView);
+ mAllAppsStore.registerIconContainer(mAH[AdapterHolder.WORK].recyclerView);
applyTouchDelegate();
}
- private boolean hasWorkProfileApp(List<AppInfo> apps) {
- if (FeatureFlags.ALL_APPS_TABS_ENABLED) {
- for (AppInfo app : apps) {
- if (mWorkMatcher.matches(app, null)) {
- return true;
- }
- }
- }
- return false;
- }
-
private void replaceRVContainer(boolean showTabs) {
for (int i = 0; i < mAH.length; i++) {
if (mAH[i].recyclerView != null) {
@@ -463,87 +346,52 @@
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() {
- final SlidingTabStrip tabs = findViewById(R.id.tabs);
- mViewPager.setAdapter(new TabsPagerAdapter());
- mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ public void onTabChanged(int pos) {
+ mHeader.setMainActive(pos == 0);
+ reset();
+ applyTouchDelegate();
+ if (mAH[pos].recyclerView != null) {
+ mAH[pos].recyclerView.bindFastScrollbar();
- boolean mVisible = true;
+ findViewById(R.id.tab_personal)
+ .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
+ findViewById(R.id.tab_work)
+ .setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
- @Override
- public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
- tabs.updateIndicatorPosition(position, positionOffset);
- if (positionOffset == 0 && !mVisible || positionOffset > 0 && mVisible) {
- mVisible = positionOffset == 0;
- for (int i = 0; i < mAH.length; i++) {
- if (mAH[i].recyclerView != null) {
- mAH[i].recyclerView.getScrollbar().setAlpha(mVisible ? 1 : 0);
- }
- }
- }
- }
-
- @Override
- public void onPageSelected(int pos) {
- tabs.updateTabTextColor(pos);
- mFloatingHeaderHandler.setMainActive(pos == 0);
- applyTouchDelegate();
- if (mAH[pos].recyclerView != null) {
- mAH[pos].recyclerView.bindFastScrollbar();
- }
- }
-
- @Override
- public void onPageScrollStateChanged(int state) {
- }
- });
-
- findViewById(R.id.tab_personal)
- .setOnClickListener((View view) -> mViewPager.setCurrentItem(0));
- findViewById(R.id.tab_work)
- .setOnClickListener((View view) -> mViewPager.setCurrentItem(1));
- }
-
- public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
- if (mUsingTabs) {
- mFloatingHeaderHandler.getContentView().setPredictedApps(apps);
}
- mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps);
- }
-
- public AppInfo findApp(ComponentKeyMapper<AppInfo> mapper) {
- return mapper.getItem(mComponentToAppMap);
+ if (pos == AdapterHolder.WORK) {
+ BottomUserEducationView.showIfNeeded(mLauncher);
+ }
}
public AlphabeticalAppsList getApps() {
return mAH[AdapterHolder.MAIN].appsList;
}
- public boolean isUsingTabs() {
- return mUsingTabs;
+ public FloatingHeaderView getFloatingHeaderView() {
+ return mHeader;
}
- public FloatingHeaderHandler getFloatingHeaderHandler() {
- return mFloatingHeaderHandler;
- }
+ public void setupHeader() {
+ mHeader.setVisibility(View.VISIBLE);
+ mHeader.setup(mAH, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView == null);
- private void setupHeader() {
- int contentHeight = mLauncher.getDeviceProfile().allAppsCellHeightPx;
- RecyclerView mainRV = mAH[AdapterHolder.MAIN].recyclerView;
- RecyclerView workRV = mAH[AdapterHolder.WORK] != null
- ? mAH[AdapterHolder.WORK].recyclerView : null;
- mFloatingHeaderHandler = new FloatingHeaderHandler(mHeader, mainRV, workRV, contentHeight);
- mFloatingHeaderHandler.getContentView().setNumAppsPerRow(mNumPredictedAppsPerRow);
- mFloatingHeaderHandler.getContentView().setComponentToAppMap(mComponentToAppMap);
+ int padding = mHeader.getMaxTranslation();
for (int i = 0; i < mAH.length; i++) {
- mAH[i].paddingTopForTabs = contentHeight;
+ mAH[i].padding.top = padding;
mAH[i].applyPadding();
}
}
@@ -552,26 +400,24 @@
for (int i = 0; i < mAH.length; i++) {
mAH[i].adapter.setLastSearchQuery(query);
}
+ if (mUsingTabs) {
+ mSearchModeWhileUsingTabs = true;
+ rebindAdapters(false); // hide tabs
+ }
+ }
+
+ public void onClearSearchResult() {
+ if (mSearchModeWhileUsingTabs) {
+ rebindAdapters(true); // show tabs
+ mSearchModeWhileUsingTabs = false;
+ }
}
public void onSearchResultsChanged() {
for (int i = 0; i < mAH.length; i++) {
- mAH[i].recyclerView.onSearchResultsChanged();
- }
- }
-
- public void setRecyclerViewPaddingTop(int top) {
- for (int i = 0; i < mAH.length; i++) {
- mAH[i].padding.top = top;
- mAH[i].applyPadding();
- }
- }
-
- public void setRecyclerViewSidePadding(int left, int right) {
- for (int i = 0; i < mAH.length; i++) {
- mAH[i].padding.left = left;
- mAH[i].padding.right = right;
- mAH[i].applyPadding();
+ if (mAH[i].recyclerView != null) {
+ mAH[i].recyclerView.onSearchResultsChanged();
+ }
}
}
@@ -587,11 +433,13 @@
}
}
- public List<AppInfo> getPredictedApps() {
+ public boolean isHeaderVisible() {
+ return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
+ }
+
+ public void onScrollUpEnd() {
if (mUsingTabs) {
- return mFloatingHeaderHandler.getContentView().getPredictedApps();
- } else {
- return mAH[AdapterHolder.MAIN].appsList.getPredictedApps();
+ ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs)).highlightWorkTabIfNecessary();
}
}
@@ -599,17 +447,16 @@
public static final int MAIN = 0;
public static final int WORK = 1;
- final AllAppsGridAdapter adapter;
+ public final AllAppsGridAdapter adapter;
final LinearLayoutManager layoutManager;
final SpringAnimationHandler animationHandler;
final AlphabeticalAppsList appsList;
final Rect padding = new Rect();
- int paddingTopForTabs;
AllAppsRecyclerView recyclerView;
boolean verticalFadingEdge;
- AdapterHolder() {
- appsList = new AlphabeticalAppsList(mLauncher, mComponentToAppMap);
+ AdapterHolder(boolean isWork) {
+ appsList = new AlphabeticalAppsList(mLauncher, mAllAppsStore, isWork);
adapter = new AllAppsGridAdapter(mLauncher, appsList, mLauncher,
AllAppsContainerView.this, true);
appsList.setAdapter(adapter);
@@ -631,31 +478,14 @@
}
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView);
recyclerView.addItemDecoration(focusedItemDecorator);
- recyclerView.preMeasureViews(adapter);
adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
applyVerticalFadingEdgeEnabled(verticalFadingEdge);
applyPadding();
- applyNumsPerRow();
}
void applyPadding() {
if (recyclerView != null) {
- int paddingTop = mUsingTabs ? paddingTopForTabs : padding.top;
- recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom);
- }
- }
-
- void applyNumsPerRow() {
- if (mNumAppsPerRow > 0) {
- if (recyclerView != null) {
- recyclerView.setNumAppsPerRow(mLauncher.getDeviceProfile(), mNumAppsPerRow);
- }
- adapter.setNumAppsPerRow(mNumAppsPerRow);
- appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
- if (mUsingTabs && mFloatingHeaderHandler != null) {
- mFloatingHeaderHandler.getContentView()
- .setNumAppsPerRow(mNumPredictedAppsPerRow);
- }
+ recyclerView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
}
}
@@ -665,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/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index ac8d367..a61521c 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -41,9 +41,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.discovery.AppDiscoveryAppInfo;
-import com.android.launcher3.discovery.AppDiscoveryItemView;
import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -57,32 +56,22 @@
// A normal icon
public static final int VIEW_TYPE_ICON = 1 << 1;
- // A prediction icon
- public static final int VIEW_TYPE_PREDICTION_ICON = 1 << 2;
// The message shown when there are no filtered results
- public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 3;
+ public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 2;
// The message to continue to a market search when there are no filtered results
- public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 4;
+ public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 3;
// We use various dividers for various purposes. They share enough attributes to reuse layouts,
// but differ in enough attributes to require different view types
// A divider that separates the apps list and the search market button
- public static final int VIEW_TYPE_SEARCH_MARKET_DIVIDER = 1 << 5;
- // The divider that separates prediction icons from the app list
- public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 6;
- public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 7;
- public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 8;
+ public static final int VIEW_TYPE_ALL_APPS_DIVIDER = 1 << 4;
+ public static final int VIEW_TYPE_WORK_TAB_FOOTER = 1 << 5;
// Common view type masks
- public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_MARKET_DIVIDER
- | VIEW_TYPE_PREDICTION_DIVIDER;
- public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
- | VIEW_TYPE_PREDICTION_ICON;
- public static final int VIEW_TYPE_MASK_CONTENT = VIEW_TYPE_MASK_ICON
- | VIEW_TYPE_DISCOVERY_ITEM;
- public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON
- | VIEW_TYPE_PREDICTION_DIVIDER;
+ public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
+ public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
+ public static final int VIEW_TYPE_MASK_HAS_SPRINGS = VIEW_TYPE_MASK_ICON;
public interface BindViewCallback {
@@ -159,7 +148,7 @@
adapterPosition = Math.max(adapterPosition, mApps.getAdapterItems().size() - 1);
int extraRows = 0;
for (int i = 0; i <= adapterPosition; i++) {
- if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_CONTENT)) {
+ if (!isViewType(items.get(i).viewType, VIEW_TYPE_MASK_ICON)) {
extraRows++;
}
}
@@ -182,8 +171,8 @@
if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
return 1;
} else {
- // Section breaks span the full width
- return mAppsPerRow;
+ // Section breaks span the full width
+ return mAppsPerRow;
}
}
}
@@ -196,7 +185,7 @@
private final View.OnClickListener mIconClickListener;
private final View.OnLongClickListener mIconLongClickListener;
- private int mAppsPerRow;
+ private final int mAppsPerRow;
private BindViewCallback mBindViewCallback;
private OnFocusChangeListener mIconFocusListener;
@@ -226,6 +215,9 @@
} else {
mSpringAnimationHandler = null;
}
+
+ mAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
+ mGridLayoutMgr.setSpanCount(mAppsPerRow);
}
public SpringAnimationHandler getSpringAnimationHandler() {
@@ -244,18 +236,6 @@
return (viewType & viewTypeMask) != 0;
}
- /**
- * Sets the number of apps per row.
- */
- public void setNumAppsPerRow(int appsPerRow) {
- mAppsPerRow = appsPerRow;
- mGridLayoutMgr.setSpanCount(appsPerRow);
- }
-
- public int getNumAppsPerRow() {
- return mAppsPerRow;
- }
-
public void setIconFocusListener(OnFocusChangeListener focusListener) {
mIconFocusListener = focusListener;
}
@@ -288,7 +268,6 @@
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_ICON:
- case VIEW_TYPE_PREDICTION_ICON:
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(mIconClickListener);
@@ -299,12 +278,6 @@
// Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
return new ViewHolder(icon);
- case VIEW_TYPE_DISCOVERY_ITEM:
- AppDiscoveryItemView appDiscoveryItemView = (AppDiscoveryItemView) mLayoutInflater
- .inflate(R.layout.all_apps_discovery_item, parent, false);
- appDiscoveryItemView.init(mIconClickListener, mLauncher.getAccessibilityDelegate(),
- mIconLongClickListener);
- return new ViewHolder(appDiscoveryItemView);
case VIEW_TYPE_EMPTY_SEARCH:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
parent, false));
@@ -318,14 +291,12 @@
}
});
return new ViewHolder(searchMarketView);
- case VIEW_TYPE_APPS_LOADING_DIVIDER:
- View loadingDividerView = mLayoutInflater.inflate(
- R.layout.all_apps_discovery_loading_divider, parent, false);
- return new ViewHolder(loadingDividerView);
- case VIEW_TYPE_PREDICTION_DIVIDER:
- case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
+ case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false));
+ case VIEW_TYPE_WORK_TAB_FOOTER:
+ View footer = mLayoutInflater.inflate(R.layout.work_tab_footer, parent, false);
+ return new ViewHolder(footer);
default:
throw new RuntimeException("Unexpected view type");
}
@@ -335,18 +306,11 @@
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON:
- case VIEW_TYPE_PREDICTION_ICON:
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset();
icon.applyFromApplicationInfo(info);
break;
- case VIEW_TYPE_DISCOVERY_ITEM:
- AppDiscoveryAppInfo appDiscoveryAppInfo = (AppDiscoveryAppInfo)
- mApps.getAdapterItems().get(position).appInfo;
- AppDiscoveryItemView view = (AppDiscoveryItemView) holder.itemView;
- view.apply(appDiscoveryAppInfo);
- break;
case VIEW_TYPE_EMPTY_SEARCH:
TextView emptyViewText = (TextView) holder.itemView;
emptyViewText.setText(mEmptySearchMessage);
@@ -361,15 +325,18 @@
searchView.setVisibility(View.GONE);
}
break;
- case VIEW_TYPE_APPS_LOADING_DIVIDER:
- int visLoading = mApps.isAppDiscoveryRunning() ? View.VISIBLE : View.GONE;
- int visLoaded = !mApps.isAppDiscoveryRunning() ? View.VISIBLE : View.GONE;
- holder.itemView.findViewById(R.id.loadingProgressBar).setVisibility(visLoading);
- holder.itemView.findViewById(R.id.loadedDivider).setVisibility(visLoaded);
- break;
- case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
+ case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do
break;
+ case VIEW_TYPE_WORK_TAB_FOOTER:
+ WorkModeSwitch workModeToggle = holder.itemView.findViewById(R.id.work_mode_toggle);
+ workModeToggle.refresh();
+ TextView managedByLabel = holder.itemView.findViewById(R.id.managed_by_label);
+ boolean anyProfileQuietModeEnabled = UserManagerCompat.getInstance(
+ managedByLabel.getContext()).isAnyProfileQuietModeEnabled();
+ managedByLabel.setText(anyProfileQuietModeEnabled
+ ? R.string.work_mode_off_label : R.string.work_mode_on_label);
+ break;
}
if (mBindViewCallback != null) {
mBindViewCallback.onBindView(holder);
@@ -441,10 +408,7 @@
*/
@Override
public void update(SpringAnimation spring, ViewHolder vh) {
- int numPredictedApps = Math.min(mAppsPerRow, mApps.getPredictedApps().size());
- int appPosition = getAppPosition(vh.getAdapterPosition(), numPredictedApps,
- mAppsPerRow);
-
+ int appPosition = vh.getAdapterPosition();
int col = appPosition % mAppsPerRow;
int row = appPosition / mAppsPerRow;
@@ -487,31 +451,6 @@
}
/**
- * @return The app position is the position of the app in the Adapter if we ignored all
- * other view types.
- *
- * The first app is at position 0, and the first app each following row is at a
- * position that is a multiple of {@param appsPerRow}.
- *
- * ie. If there are 5 apps per row, and there are two rows of apps:
- * 0 1 2 3 4
- * 5 6 7 8 9
- */
- private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
- if (position < numPredictedApps) {
- // Predicted apps are first in the adapter.
- return position;
- }
-
- // There is at most 1 divider view between the predicted apps and the alphabetical apps.
- int numDividerViews = numPredictedApps == 0 ? 0 : 1;
-
- // This offset takes into consideration an incomplete row of predicted apps.
- int numPredictedAppsOffset = appsPerRow - numPredictedApps;
- return position + numPredictedAppsOffset - numDividerViews;
- }
-
- /**
* Increase the column factor as the distance increases between the column and the center
* column(s).
*/
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/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 09357f7..53d19eb 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.allapps;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
@@ -28,10 +30,10 @@
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
@@ -52,8 +54,7 @@
private AlphabeticalAppsList mApps;
private AllAppsFastScrollHelper mFastScrollHelper;
- private int mNumAppsPerRow;
- private int mUserProfileTabContentHeight;
+ private final int mNumAppsPerRow;
// The specific view heights that we use to calculate scroll
private SparseIntArray mViewHeights = new SparseIntArray();
@@ -103,6 +104,8 @@
mOverScrollHelper = new OverScrollHelper();
mPullDetector = new SwipeDetector(getContext(), mOverScrollHelper, SwipeDetector.VERTICAL);
mPullDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, true);
+
+ mNumAppsPerRow = LauncherAppState.getIDP(context).numColumns;
}
public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
@@ -127,65 +130,23 @@
public void setApps(AlphabeticalAppsList apps, boolean usingTabs) {
mApps = apps;
mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
- mUserProfileTabContentHeight = usingTabs
- ? Launcher.getLauncher(getContext()).getDeviceProfile().allAppsCellHeightPx : 0;;
}
public AlphabeticalAppsList getApps() {
return mApps;
}
- /**
- * Sets the number of apps per row in this recycler view.
- */
- public void setNumAppsPerRow(DeviceProfile grid, int numAppsPerRow) {
- mNumAppsPerRow = numAppsPerRow;
+ private void updatePoolSize() {
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
- pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
- pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, mNumAppsPerRow);
- pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, 1);
- }
- /**
- * Ensures that we can present a stable scrollbar for views of varying types by pre-measuring
- * all the different view types.
- */
- public void preMeasureViews(AllAppsGridAdapter adapter) {
- View icon = adapter.onCreateViewHolder(this, AllAppsGridAdapter.VIEW_TYPE_ICON).itemView;
- final int iconHeight = icon.getLayoutParams().height;
- mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, iconHeight);
- mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, iconHeight);
-
- final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST);
- final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST);
-
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER,
- AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH);
- if (FeatureFlags.DISCOVERY_ENABLED) {
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_APPS_LOADING_DIVIDER);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_DISCOVERY_ITEM);
- }
- }
-
- private void putSameHeightFor(AllAppsGridAdapter adapter, int w, int h, int... viewTypes) {
- View view = adapter.onCreateViewHolder(this, viewTypes[0]).itemView;
- view.measure(w, h);
- for (int viewType : viewTypes) {
- mViewHeights.put(viewType, view.getMeasuredHeight());
- }
+ mViewHeights.clear();
+ mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, grid.allAppsCellHeightPx);
}
/**
@@ -237,6 +198,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateEmptySearchBackgroundBounds();
+ updatePoolSize();
}
@Override
@@ -244,19 +206,6 @@
if (mApps.hasFilter()) {
targetParent.containerType = ContainerType.SEARCHRESULT;
} else {
- if (v instanceof BubbleTextView) {
- BubbleTextView icon = (BubbleTextView) v;
- int position = getChildPosition(icon);
- if (position != NO_POSITION) {
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- AlphabeticalAppsList.AdapterItem item = items.get(position);
- if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
- targetParent.containerType = ContainerType.PREDICTION;
- target.predictedRank = item.rowAppIndex;
- return;
- }
- }
- }
targetParent.containerType = ContainerType.ALLAPPS;
}
}
@@ -265,7 +214,7 @@
// Always scroll the view to the top so the user can see the changed results
scrollToTop();
- if (mApps.shouldShowEmptySearch()) {
+ if (mApps.hasNoFilteredResults()) {
if (mEmptySearchBackground == null) {
mEmptySearchBackground = DrawableFactory.get(getContext())
.getAllAppsBackground(getContext());
@@ -362,6 +311,9 @@
*/
@Override
public void onUpdateScrollbar(int dy) {
+ if (mApps == null) {
+ return;
+ }
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
@@ -473,7 +425,21 @@
}
} else {
// Rest of the views span the full width
- y += mViewHeights.get(item.viewType, 0);
+ int elHeight = mViewHeights.get(item.viewType);
+ if (elHeight == 0) {
+ ViewHolder holder = findViewHolderForAdapterPosition(i);
+ if (holder == null) {
+ holder = getAdapter().createViewHolder(this, item.viewType);
+ getAdapter().onBindViewHolder(holder, i);
+ holder.itemView.measure(UNSPECIFIED, UNSPECIFIED);
+ elHeight = holder.itemView.getMeasuredHeight();
+
+ getRecycledViewPool().putRecycledView(holder);
+ } else {
+ elHeight = holder.itemView.getMeasuredHeight();
+ }
+ }
+ y += elHeight;
}
}
mCachedScrollPositions.put(position, y);
@@ -488,18 +454,11 @@
@Override
protected int getAvailableScrollHeight() {
return getPaddingTop() + getCurrentScrollY(getAdapter().getItemCount(), 0)
- - getHeight() + getPaddingBottom() + mUserProfileTabContentHeight;
+ - getHeight() + getPaddingBottom();
}
public int getScrollBarTop() {
- return super.getScrollBarTop() + mUserProfileTabContentHeight;
- }
-
- /**
- * Returns the height of the fast scroll bar
- */
- public int getScrollbarTrackHeight() {
- return super.getScrollbarTrackHeight() + mUserProfileTabContentHeight;
+ return getResources().getDimensionPixelOffset(R.dimen.all_apps_header_top_padding);
}
public RecyclerViewFastScroller getScrollbar() {
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
new file mode 100644
index 0000000..dc34892
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.PromiseAppInfo;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageUserKey;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A utility class to maintain the collection of all apps.
+ */
+public class AllAppsStore {
+
+ private PackageUserKey mTempKey = new PackageUserKey(null, null);
+ private final HashMap<ComponentKey, AppInfo> mComponentToAppMap = new HashMap<>();
+ private final List<OnUpdateListener> mUpdateListeners = new ArrayList<>();
+ private final ArrayList<ViewGroup> mIconContainers = new ArrayList<>();
+
+ private boolean mDeferUpdates = false;
+ private boolean mUpdatePending = false;
+
+ public Collection<AppInfo> getApps() {
+ return mComponentToAppMap.values();
+ }
+
+ /**
+ * Sets the current set of apps.
+ */
+ public void setApps(List<AppInfo> apps) {
+ mComponentToAppMap.clear();
+ addOrUpdateApps(apps);
+ }
+
+ public AppInfo getApp(ComponentKey key) {
+ return mComponentToAppMap.get(key);
+ }
+
+ public void setDeferUpdates(boolean deferUpdates) {
+ if (mDeferUpdates != deferUpdates) {
+ mDeferUpdates = deferUpdates;
+
+ if (!mDeferUpdates && mUpdatePending) {
+ notifyUpdate();
+ mUpdatePending = false;
+ }
+ }
+ }
+
+ /**
+ * Adds or updates existing apps in the list
+ */
+ public void addOrUpdateApps(List<AppInfo> apps) {
+ for (AppInfo app : apps) {
+ mComponentToAppMap.put(app.toComponentKey(), app);
+ }
+ notifyUpdate();
+ }
+
+ /**
+ * Removes some apps from the list.
+ */
+ public void removeApps(List<AppInfo> apps) {
+ for (AppInfo app : apps) {
+ mComponentToAppMap.remove(app.toComponentKey());
+ }
+ notifyUpdate();
+ }
+
+
+ private void notifyUpdate() {
+ if (mDeferUpdates) {
+ mUpdatePending = true;
+ return;
+ }
+ int count = mUpdateListeners.size();
+ for (int i = 0; i < count; i++) {
+ mUpdateListeners.get(i).onAppsUpdated();
+ }
+ }
+
+ public void addUpdateListener(OnUpdateListener listener) {
+ mUpdateListeners.add(listener);
+ }
+
+ public void removeUpdateListener(OnUpdateListener listener) {
+ mUpdateListeners.remove(listener);
+ }
+
+ public void registerIconContainer(ViewGroup container) {
+ if (container != null) {
+ mIconContainers.add(container);
+ }
+ }
+
+ public void unregisterIconContainer(ViewGroup container) {
+ mIconContainers.remove(container);
+ }
+
+ public void updateIconBadges(Set<PackageUserKey> updatedBadges) {
+ updateAllIcons((child) -> {
+ if (child.getTag() instanceof ItemInfo) {
+ ItemInfo info = (ItemInfo) child.getTag();
+ if (mTempKey.updateFromItemInfo(info) && updatedBadges.contains(mTempKey)) {
+ child.applyBadgeState(info, true /* animate */);
+ }
+ }
+ });
+ }
+
+ public void updatePromiseAppProgress(PromiseAppInfo app) {
+ updateAllIcons((child) -> {
+ if (child.getTag() == app) {
+ child.applyProgressLevel(app.level);
+ }
+ });
+ }
+
+ private void updateAllIcons(IconAction action) {
+ for (int i = mIconContainers.size() - 1; i >= 0; i--) {
+ ViewGroup parent = mIconContainers.get(i);
+ int childCount = parent.getChildCount();
+
+ for (int j = 0; j < childCount; j++) {
+ View child = parent.getChildAt(j);
+ if (child instanceof BubbleTextView) {
+ action.apply((BubbleTextView) child);
+ }
+ }
+ }
+ }
+
+ public interface OnUpdateListener {
+ void onAppsUpdated();
+ }
+
+ public interface IconAction {
+ void apply(BubbleTextView icon);
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index eb26704..c859347 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -2,30 +2,31 @@
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
-import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.util.Property;
import android.view.View;
import android.view.animation.Interpolator;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
+import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
-import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.allapps.SearchUiManager.OnScrollRangeChangeListener;
import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.graphics.GradientView;
-import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.AllAppsScrim;
/**
* Handles AllApps view transition.
@@ -38,10 +39,10 @@
* closer to top or closer to the page indicator.
*/
public class AllAppsTransitionController
- implements SearchUiManager.OnScrollRangeChangeListener, LauncherStateManager.StateHandler {
+ implements OnScrollRangeChangeListener, StateHandler, OnDeviceProfileChangeListener {
- private static final Property<AllAppsTransitionController, Float> PROGRESS =
- new Property<AllAppsTransitionController, Float>(Float.class, "progress") {
+ public static final Property<AllAppsTransitionController, Float> ALL_APPS_PROGRESS =
+ new Property<AllAppsTransitionController, Float>(Float.class, "allAppsProgress") {
@Override
public Float get(AllAppsTransitionController controller) {
@@ -54,10 +55,7 @@
}
};
- private final Interpolator mWorkspaceAccelnterpolator = Interpolators.ACCEL_2;
- private final Interpolator mHotseatAccelInterpolator = Interpolators.ACCEL_1_5;
-
- private static final float PARALLAX_COEFFICIENT = .125f;
+ public static final float PARALLAX_COEFFICIENT = .125f;
private AllAppsContainerView mAppsView;
private Workspace mWorkspace;
@@ -65,6 +63,7 @@
private final Launcher mLauncher;
private final boolean mIsDarkTheme;
+ private boolean mIsVerticalLayout;
// Animation in this class is controlled by a single variable {@link mProgress}.
// Visually, it represents top y coordinate of the all apps container if multiplied with
@@ -77,9 +76,7 @@
private static final float DEFAULT_SHIFT_RANGE = 10;
- private boolean mIsTranslateWithoutWorkspace = false;
- private Animator mDiscoBounceAnimation;
- private GradientView mGradientView;
+ private AllAppsScrim mAllAppsScrim;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
@@ -87,6 +84,8 @@
mProgress = 1f;
mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
+ mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
+ mLauncher.addOnDeviceProfileChangeListener(this);
}
public float getShiftRange() {
@@ -99,24 +98,16 @@
mAppsView.setVisibility(View.VISIBLE);
}
- private void updateLightStatusBar(float shift) {
- // Use a light system UI (dark icons) if all apps is behind at least half of the status bar.
- boolean forceChange = shift <= mShiftRange / 4;
- if (forceChange) {
- mLauncher.getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
- } else {
- mLauncher.getSystemUiController().updateUiState(
- SystemUiController.UI_STATE_ALL_APPS, 0);
- }
- }
+ @Override
+ public void onDeviceProfileChanged(DeviceProfile dp) {
+ mIsVerticalLayout = dp.isVerticalBarLayout();
- private void updateAllAppsBg(float progress) {
- // gradient
- if (mGradientView == null) {
- mGradientView = mLauncher.findViewById(R.id.gradient_bg);
+ if (mIsVerticalLayout) {
+ mAppsView.setAlpha(1);
+ mLauncher.getHotseat().setTranslationY(0);
+ mLauncher.getWorkspace().getPageIndicator().setTranslationY(0);
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
}
- mGradientView.setProgress(progress);
}
/**
@@ -126,7 +117,7 @@
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
*
* @see #setState(LauncherState)
- * @see #setStateWithAnimation(LauncherState, AnimationLayerSet, AnimatorSet, AnimationConfig)
+ * @see #setStateWithAnimation(LauncherState, AnimatorSetBuilder, AnimationConfig)
*/
public void setProgress(float progress) {
mProgress = progress;
@@ -134,29 +125,28 @@
float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f);
float alpha = 1 - workspaceHotseatAlpha;
- float workspaceAlpha = mWorkspaceAccelnterpolator.getInterpolation(workspaceHotseatAlpha);
- float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
- updateAllAppsBg(alpha);
- mAppsView.setAlpha(alpha);
mAppsView.setTranslationY(shiftCurrent);
-
- if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y, -mShiftRange + shiftCurrent,
- hotseatAlpha);
- } else {
- mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y,
- PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent),
- hotseatAlpha);
+ if (mAllAppsScrim == null) {
+ mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
}
+ float hotseatTranslation = -mShiftRange + shiftCurrent;
+ mAllAppsScrim.setProgress(hotseatTranslation, alpha);
- if (mIsTranslateWithoutWorkspace) {
- return;
+ if (!mIsVerticalLayout) {
+ mAppsView.setAlpha(alpha);
+ mLauncher.getHotseat().setTranslationY(hotseatTranslation);
+ mLauncher.getWorkspace().getPageIndicator().setTranslationY(hotseatTranslation);
+
+ // Use a light system UI (dark icons) if all apps is behind at least half of the
+ // status bar.
+ boolean forceChange = shiftCurrent <= mShiftRange / 4;
+ if (forceChange) {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
+ } else {
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
+ }
}
- mWorkspace.setWorkspaceYTranslationAndAlpha(
- PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), workspaceAlpha);
-
- updateLightStatusBar(shiftCurrent);
}
public float getProgress() {
@@ -169,7 +159,7 @@
*/
@Override
public void setState(LauncherState state) {
- setProgress(state.verticalProgress);
+ setProgress(state.getVerticalProgress(mLauncher));
onProgressAnimationEnd();
}
@@ -178,20 +168,27 @@
* dependent UI using various animation events
*/
@Override
- public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
- AnimatorSet animationOut, AnimationConfig config) {
- if (Float.compare(mProgress, toState.verticalProgress) == 0) {
+ public void setStateWithAnimation(LauncherState toState,
+ AnimatorSetBuilder builder, AnimationConfig config) {
+ float targetProgress = toState.getVerticalProgress(mLauncher);
+ if (Float.compare(mProgress, targetProgress) == 0) {
// Fail fast
onProgressAnimationEnd();
return;
}
Interpolator interpolator = config.userControlled ? LINEAR : FAST_OUT_SLOW_IN;
- ObjectAnimator anim = ObjectAnimator.ofFloat(
- this, PROGRESS, mProgress, toState.verticalProgress);
+ ObjectAnimator anim =
+ ObjectAnimator.ofFloat(this, ALL_APPS_PROGRESS, mProgress, targetProgress);
anim.setDuration(config.duration);
anim.setInterpolator(interpolator);
- anim.addListener(new AnimationSuccessListener() {
+ anim.addListener(getProgressAnimatorListener());
+
+ builder.play(anim);
+ }
+
+ public AnimatorListenerAdapter getProgressAnimatorListener() {
+ return new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
onProgressAnimationEnd();
@@ -201,50 +198,7 @@
public void onAnimationStart(Animator animation) {
onProgressAnimationStart();
}
- });
-
- animationOut.play(anim);
- }
-
- public void showDiscoveryBounce() {
- // cancel existing animation in case user locked and unlocked at a super human speed.
- cancelDiscoveryAnimation();
-
- // assumption is that this variable is always null
- mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher,
- R.animator.discovery_bounce);
- mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- mIsTranslateWithoutWorkspace = true;
- onProgressAnimationStart();
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- onProgressAnimationEnd();
- mDiscoBounceAnimation = null;
- mIsTranslateWithoutWorkspace = false;
- }
- });
- mDiscoBounceAnimation.setTarget(this);
- mAppsView.post(new Runnable() {
- @Override
- public void run() {
- if (mDiscoBounceAnimation == null) {
- return;
- }
- mDiscoBounceAnimation.start();
- }
- });
- }
-
- public void cancelDiscoveryAnimation() {
- if (mDiscoBounceAnimation == null) {
- return;
- }
- mDiscoBounceAnimation.cancel();
- mDiscoBounceAnimation = null;
+ };
}
public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) {
@@ -273,6 +227,10 @@
} else if (Float.compare(mProgress, 0f) == 0) {
mHotseat.setVisibility(View.INVISIBLE);
mAppsView.setVisibility(View.VISIBLE);
+ mAppsView.onScrollUpEnd();
+ } else {
+ mAppsView.setVisibility(View.VISIBLE);
+ mHotseat.setVisibility(View.VISIBLE);
}
}
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index f9dde2f..434918d 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -16,20 +16,14 @@
package com.android.launcher3.allapps;
import android.content.Context;
-import android.os.Process;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.Log;
+import android.content.pm.PackageManager;
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.discovery.AppDiscoveryAppInfo;
-import com.android.launcher3.discovery.AppDiscoveryItem;
-import com.android.launcher3.discovery.AppDiscoveryUpdateState;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LabelComparator;
@@ -44,19 +38,15 @@
/**
* The alphabetically sorted list of applications.
*/
-public class AlphabeticalAppsList {
+public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
public static final String TAG = "AlphabeticalAppsList";
- private static final boolean DEBUG = false;
- private static final boolean DEBUG_PREDICTIONS = false;
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
- private AppDiscoveryUpdateState mAppDiscoveryUpdateState;
-
/**
* Info about a fast scroller section, depending if sections are merged, the fast scroller
* sections will not be the same set as the section headers.
@@ -97,13 +87,6 @@
// The index of this app not including sections
public int appIndex = -1;
- public static AdapterItem asPredictedApp(int pos, String sectionName, AppInfo appInfo,
- int appIndex) {
- AdapterItem item = asApp(pos, sectionName, appInfo, appIndex);
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON;
- return item;
- }
-
public static AdapterItem asApp(int pos, String sectionName, AppInfo appInfo,
int appIndex) {
AdapterItem item = new AdapterItem();
@@ -115,17 +98,6 @@
return item;
}
- public static AdapterItem asDiscoveryItem(int pos, String sectionName, AppInfo appInfo,
- int appIndex) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_DISCOVERY_ITEM;
- item.position = pos;
- item.sectionName = sectionName;
- item.appInfo = appInfo;
- item.appIndex = appIndex;
- return item;
- }
-
public static AdapterItem asEmptySearch(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
@@ -133,23 +105,9 @@
return item;
}
- public static AdapterItem asPredictionDivider(int pos) {
+ public static AdapterItem asAllAppsDivider(int pos) {
AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER;
- item.position = pos;
- return item;
- }
-
- public static AdapterItem asMarketDivider(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
- item.position = pos;
- return item;
- }
-
- public static AdapterItem asLoadingDivider(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_APPS_LOADING_DIVIDER;
+ item.viewType = AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
item.position = pos;
return item;
}
@@ -160,13 +118,20 @@
item.position = pos;
return item;
}
+
+ public static AdapterItem asWorkTabFooter(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.VIEW_TYPE_WORK_TAB_FOOTER;
+ item.position = pos;
+ return item;
+ }
}
private final Launcher mLauncher;
- // The set of apps from the system not including predictions
+ // The set of apps from the system
private final List<AppInfo> mApps = new ArrayList<>();
- private final HashMap<ComponentKey, AppInfo> mComponentToAppMap;
+ private final AllAppsStore mAllAppsStore;
// The set of filtered apps with the current filter
private final List<AppInfo> mFilteredApps = new ArrayList<>();
@@ -174,11 +139,8 @@
private final ArrayList<AdapterItem> mAdapterItems = new ArrayList<>();
// The set of sections that we allow fast-scrolling to (includes non-merged sections)
private final List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
- // The set of predicted app component names
- private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
- // The set of predicted apps resolved from the component names and the current set of apps
- private final List<AppInfo> mPredictedApps = new ArrayList<>();
- private final List<AppDiscoveryAppInfo> mDiscoveredApps = new ArrayList<>();
+ // Is it the work profile app list.
+ private final boolean mIsWork;
// The of ordered component names as a result of a search query
private ArrayList<ComponentKey> mSearchResults;
@@ -186,16 +148,18 @@
private AllAppsGridAdapter mAdapter;
private AlphabeticIndexCompat mIndexer;
private AppInfoComparator mAppNameComparator;
- private int mNumAppsPerRow;
- private int mNumPredictedAppsPerRow;
+ private final int mNumAppsPerRow;
private int mNumAppRowsInAdapter;
private ItemInfoMatcher mItemFilter;
- public AlphabeticalAppsList(Context context, HashMap<ComponentKey, AppInfo> componentToAppMap) {
- mComponentToAppMap = componentToAppMap;
+ public AlphabeticalAppsList(Context context, AllAppsStore appsStore, boolean isWork) {
+ mAllAppsStore = appsStore;
mLauncher = Launcher.getLauncher(context);
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppInfoComparator(context);
+ mIsWork = isWork;
+ mNumAppsPerRow = mLauncher.getDeviceProfile().inv.numColumns;
+ mAllAppsStore.addUpdateListener(this);
}
public void updateItemFilter(ItemInfoMatcher itemFilter) {
@@ -204,16 +168,6 @@
}
/**
- * Sets the number of apps per row.
- */
- public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
- mNumAppsPerRow = numAppsPerRow;
- mNumPredictedAppsPerRow = numPredictedAppsPerRow;
-
- updateAdapterItems();
- }
-
- /**
* Sets the adapter to notify when this dataset changes.
*/
public void setAdapter(AllAppsGridAdapter adapter) {
@@ -228,13 +182,6 @@
}
/**
- * Returns the predicted apps.
- */
- public List<AppInfo> getPredictedApps() {
- return mPredictedApps;
- }
-
- /**
* Returns fast scroller sections of all the current filtered applications.
*/
public List<FastScrollSectionInfo> getFastScrollerSections() {
@@ -249,7 +196,7 @@
}
/**
- * Returns the number of rows of applications (not including predictions)
+ * Returns the number of rows of applications
*/
public int getNumAppRows() {
return mNumAppRowsInAdapter;
@@ -276,10 +223,6 @@
return (mSearchResults != null) && mFilteredApps.isEmpty();
}
- boolean shouldShowEmptySearch() {
- return hasNoFilteredResults() && !isAppDiscoveryRunning() && mDiscoveredApps.isEmpty();
- }
-
/**
* Sets the sorted list of filtered components.
*/
@@ -287,109 +230,22 @@
if (mSearchResults != f) {
boolean same = mSearchResults != null && mSearchResults.equals(f);
mSearchResults = f;
- updateAdapterItems();
+ onAppsUpdated();
return !same;
}
return false;
}
- public void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
- @NonNull AppDiscoveryUpdateState state) {
- mAppDiscoveryUpdateState = state;
- switch (state) {
- case START:
- mDiscoveredApps.clear();
- break;
- case UPDATE:
- mDiscoveredApps.add(new AppDiscoveryAppInfo(app));
- break;
- }
- updateAdapterItems();
- }
-
- private List<AppInfo> processPredictedAppComponents(List<ComponentKeyMapper<AppInfo>> components) {
- if (mComponentToAppMap.isEmpty()) {
- // Apps have not been bound yet.
- return Collections.emptyList();
- }
-
- List<AppInfo> predictedApps = new ArrayList<>();
- for (ComponentKeyMapper<AppInfo> mapper : components) {
- AppInfo info = mapper.getItem(mComponentToAppMap);
- if (info != null) {
- predictedApps.add(info);
- } else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Predicted app not found: " + mapper);
- }
- }
- // Stop at the number of predicted apps
- if (predictedApps.size() == mNumPredictedAppsPerRow) {
- break;
- }
- }
- return predictedApps;
- }
-
- /**
- * Sets the current set of predicted apps.
- *
- * This can be called before we get the full set of applications, we should merge the results
- * only in onAppsUpdated() which is idempotent.
- *
- * If the number of predicted apps is the same as the previous list of predicted apps,
- * we can optimize by swapping them in place.
- */
- public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
- mPredictedAppComponents.clear();
- mPredictedAppComponents.addAll(apps);
-
- List<AppInfo> newPredictedApps = processPredictedAppComponents(apps);
- // We only need to do work if any of the visible predicted apps have changed.
- if (!newPredictedApps.equals(mPredictedApps)) {
- if (newPredictedApps.size() == mPredictedApps.size()) {
- swapInNewPredictedApps(newPredictedApps);
- } else {
- // We need to update the appIndex of all the items.
- onAppsUpdated();
- }
- }
- }
-
- /**
- * Swaps out the old predicted apps with the new predicted apps, in place. This optimization
- * allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged.
- *
- * Note: This should only be called if the # of predicted apps is the same.
- * This method assumes that predicted apps are the first items in the adapter.
- */
- private void swapInNewPredictedApps(List<AppInfo> apps) {
- mPredictedApps.clear();
- mPredictedApps.addAll(apps);
-
- int size = apps.size();
- for (int i = 0; i < size; ++i) {
- AppInfo info = apps.get(i);
- AdapterItem orgItem = mAdapterItems.get(i);
- AdapterItem newItem = AdapterItem.asPredictedApp(orgItem.position, "", info,
- orgItem.appIndex);
- newItem.rowAppIndex = orgItem.rowAppIndex;
-
- mAdapterItems.set(i, newItem);
- mFilteredApps.set(i, info);
- mAdapter.notifyItemChanged(i);
- }
- }
-
/**
* Updates internals when the set of apps are updated.
*/
- void onAppsUpdated() {
+ @Override
+ public void onAppsUpdated() {
// Sort the list of apps
mApps.clear();
- for (AppInfo app : mComponentToAppMap.values()) {
- if (mItemFilter == null || mItemFilter.matches(app, null)) {
+ for (AppInfo app : mAllAppsStore.getApps()) {
+ if (mItemFilter == null || mItemFilter.matches(app, null) || hasFilter()) {
mApps.add(app);
}
}
@@ -460,47 +316,6 @@
mFastScrollerSections.clear();
mAdapterItems.clear();
- if (!FeatureFlags.ALL_APPS_TABS_ENABLED) {
- if (DEBUG_PREDICTIONS) {
- if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
- Process.myUserHandle())));
- }
- }
-
- // Process the predicted app components
- mPredictedApps.clear();
- if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
- mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
-
- if (!mPredictedApps.isEmpty()) {
- // Add a section for the predictions
- lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
- mFastScrollerSections.add(lastFastScrollerSectionInfo);
-
- // Add the predicted app items
- for (AppInfo info : mPredictedApps) {
- AdapterItem appItem = AdapterItem.asPredictedApp(position++, "", info,
- appIndex++);
- if (lastFastScrollerSectionInfo.fastScrollToItem == null) {
- lastFastScrollerSectionInfo.fastScrollToItem = appItem;
- }
- mAdapterItems.add(appItem);
- mFilteredApps.add(info);
- }
-
- mAdapterItems.add(AdapterItem.asPredictionDivider(position++));
- }
- }
-
- }
-
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
for (AppInfo info : getFiltersAppInfos()) {
@@ -523,32 +338,13 @@
}
if (hasFilter()) {
- if (isAppDiscoveryRunning() || mDiscoveredApps.size() > 0) {
- mAdapterItems.add(AdapterItem.asLoadingDivider(position++));
- // Append all app discovery results
- for (int i = 0; i < mDiscoveredApps.size(); i++) {
- AppDiscoveryAppInfo appDiscoveryAppInfo = mDiscoveredApps.get(i);
- if (appDiscoveryAppInfo.isRecent) {
- // already handled in getFilteredAppInfos()
- continue;
- }
- AdapterItem item = AdapterItem.asDiscoveryItem(position++,
- "", appDiscoveryAppInfo, appIndex++);
- mAdapterItems.add(item);
- }
-
- if (!isAppDiscoveryRunning()) {
- mAdapterItems.add(AdapterItem.asMarketSearch(position++));
- }
+ // Append the search market item
+ if (hasNoFilteredResults()) {
+ mAdapterItems.add(AdapterItem.asEmptySearch(position++));
} else {
- // Append the search market item
- if (hasNoFilteredResults()) {
- mAdapterItems.add(AdapterItem.asEmptySearch(position++));
- } else {
- mAdapterItems.add(AdapterItem.asMarketDivider(position++));
- }
- mAdapterItems.add(AdapterItem.asMarketSearch(position++));
+ mAdapterItems.add(AdapterItem.asAllAppsDivider(position++));
}
+ mAdapterItems.add(AdapterItem.asMarketSearch(position++));
}
if (mNumAppsPerRow != 0) {
@@ -604,11 +400,18 @@
break;
}
}
+
+ // Add the work profile footer if required.
+ if (shouldShowWorkFooter()) {
+ mAdapterItems.add(AdapterItem.asWorkTabFooter(position++));
+ }
}
- public boolean isAppDiscoveryRunning() {
- return mAppDiscoveryUpdateState == AppDiscoveryUpdateState.START
- || mAppDiscoveryUpdateState == AppDiscoveryUpdateState.UPDATE;
+ private boolean shouldShowWorkFooter() {
+ return mIsWork && Utilities.ATLEAST_P &&
+ (DeepShortcutManager.getInstance(mLauncher).hasHostPermission()
+ || mLauncher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ == PackageManager.PERMISSION_GRANTED);
}
private List<AppInfo> getFiltersAppInfos() {
@@ -617,22 +420,11 @@
}
ArrayList<AppInfo> result = new ArrayList<>();
for (ComponentKey key : mSearchResults) {
- AppInfo match = mComponentToAppMap.get(key);
+ AppInfo match = mAllAppsStore.getApp(key);
if (match != null) {
result.add(match);
}
}
-
- // adding recently used instant apps
- if (mDiscoveredApps.size() > 0) {
- for (int i = 0; i < mDiscoveredApps.size(); i++) {
- AppDiscoveryAppInfo discoveryAppInfo = mDiscoveredApps.get(i);
- if (discoveryAppInfo.isRecent) {
- result.add(discoveryAppInfo);
- }
- }
- Collections.sort(result, mAppNameComparator);
- }
return result;
}
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
new file mode 100644
index 0000000..fddafb2
--- /dev/null
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -0,0 +1,112 @@
+/*
+ * 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 static com.android.launcher3.LauncherState.NORMAL;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.compat.UserManagerCompat;
+
+/**
+ * Floating view responsible for showing discovery bounce animation
+ */
+public class DiscoveryBounce extends AbstractFloatingView {
+
+ public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
+
+ private final Launcher mLauncher;
+ private final Animator mDiscoBounceAnimation;
+
+ public DiscoveryBounce(Launcher launcher) {
+ super(launcher, null);
+ mLauncher = launcher;
+
+ mDiscoBounceAnimation = AnimatorInflater.loadAnimator(mLauncher,
+ R.animator.discovery_bounce);
+ AllAppsTransitionController controller = mLauncher.getAllAppsController();
+ mDiscoBounceAnimation.setTarget(controller);
+ mDiscoBounceAnimation.addListener(controller.getProgressAnimatorListener());
+
+ mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ handleClose(false);
+ }
+ });
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mDiscoBounceAnimation.start();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mDiscoBounceAnimation.isRunning()) {
+ mDiscoBounceAnimation.end();
+ }
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ handleClose(false);
+ return false;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (mIsOpen) {
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // Since this is on-boarding popup, it is not a user controlled action.
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ON_BOARD_POPUP) != 0;
+ }
+
+ public static void showIfNeeded(Launcher launcher) {
+ if (!launcher.isInState(NORMAL)
+ || launcher.getSharedPrefs().getBoolean(APPS_VIEW_SHOWN, false)
+ || AbstractFloatingView.getTopOpenView(launcher) != null
+ || UserManagerCompat.getInstance(launcher).isDemoUser()
+ || ActivityManager.isRunningInTestHarness()) {
+ return;
+ }
+
+ DiscoveryBounce view = new DiscoveryBounce(launcher);
+ view.mIsOpen = true;
+ launcher.getDragLayer().addView(view);
+ }
+}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderHandler.java b/src/com/android/launcher3/allapps/FloatingHeaderHandler.java
deleted file mode 100644
index 984966b..0000000
--- a/src/com/android/launcher3/allapps/FloatingHeaderHandler.java
+++ /dev/null
@@ -1,139 +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.graphics.Rect;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-
-import com.android.launcher3.R;
-
-public class FloatingHeaderHandler extends RecyclerView.OnScrollListener {
-
- private final int mMaxTranslation;
- private final View mHeaderView;
- private final PredictionRowView mContentView;
- private final RecyclerView mMainRV;
- private final RecyclerView mWorkRV;
- private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
-
- private boolean mHeaderHidden;
- private int mSnappedScrolledY;
- private int mTranslationY;
- private int mMainScrolledY;
- private int mWorkScrolledY;
- private boolean mMainRVActive;
-
- public FloatingHeaderHandler(@NonNull View header, @NonNull RecyclerView personalRV,
- @Nullable RecyclerView workRV, int contentHeight) {
- mHeaderView = header;
- mContentView = mHeaderView.findViewById(R.id.header_content);
- mContentView.getLayoutParams().height = contentHeight;
- mMaxTranslation = contentHeight;
- mMainRV = personalRV;
- mMainRV.addOnScrollListener(this);
- mWorkRV = workRV;
- if (workRV != null) {
- workRV.addOnScrollListener(this);
- }
- setMainActive(true);
- }
-
- public void setMainActive(boolean active) {
- mMainRVActive = active;
- mSnappedScrolledY = getCurrentScroll() - mMaxTranslation;
- setExpanded(true);
- }
-
- public View getHeaderView() {
- return mHeaderView;
- }
-
- public PredictionRowView getContentView() {
- return mContentView;
- }
-
- @Override
- public void onScrolled(RecyclerView rv, int dx, int dy) {
- boolean isMainRV = rv == mMainRV;
- if (isMainRV != mMainRVActive) {
- return;
- }
-
- int current = isMainRV
- ? (mMainScrolledY -= dy)
- : (mWorkScrolledY -= dy);
-
- if (dy == 0) {
- setExpanded(true);
- } else {
- moved(current);
- apply();
- }
- }
-
- private void moved(final int currentScrollY) {
- if (mHeaderHidden) {
- if (currentScrollY <= mSnappedScrolledY) {
- mSnappedScrolledY = currentScrollY;
- } else {
- mHeaderHidden = false;
- }
- mTranslationY = currentScrollY;
- } else {
- mTranslationY = currentScrollY - mSnappedScrolledY - mMaxTranslation;
-
- // update state vars
- if (mTranslationY >= 0) { // expanded: must not move down further
- mTranslationY = 0;
- mSnappedScrolledY = currentScrollY - mMaxTranslation;
- } else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden
- mHeaderHidden = true;
- mSnappedScrolledY = currentScrollY;
- }
- }
- }
-
- private void apply() {
- mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
- mHeaderView.setTranslationY(mTranslationY);
- mClip.top = mMaxTranslation + mTranslationY;
- mMainRV.setClipBounds(mClip);
- if (mWorkRV != null) {
- mWorkRV.setClipBounds(mClip);
- }
- }
-
- private void setExpanded(boolean expand) {
- int translateTo = expand ? 0 : -mMaxTranslation;
- mTranslationY = translateTo;
- apply();
-
- mHeaderHidden = !expand;
- mSnappedScrolledY = expand ? getCurrentScroll() - mMaxTranslation : getCurrentScroll();
- }
-
- public boolean isExpanded() {
- return !mHeaderHidden;
- }
-
- private int getCurrentScroll() {
- return mMainRVActive ? mMainScrolledY : mWorkScrolledY;
- }
-
-}
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
new file mode 100644
index 0000000..d8a9f63
--- /dev/null
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -0,0 +1,206 @@
+/*
+ * 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.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import com.android.launcher3.R;
+
+public class FloatingHeaderView extends LinearLayout implements
+ ValueAnimator.AnimatorUpdateListener {
+
+ private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
+ private final Point mTempOffset = new Point();
+ private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ }
+
+ @Override
+ public void onScrolled(RecyclerView rv, int dx, int dy) {
+ if (rv != mCurrentRV) {
+ return;
+ }
+
+ if (mAnimator.isStarted()) {
+ mAnimator.cancel();
+ }
+
+ int current = -mCurrentRV.getCurrentScrollY();
+ moved(current);
+ apply();
+ }
+ };
+
+ private ViewGroup mTabLayout;
+ private AllAppsRecyclerView mMainRV;
+ private AllAppsRecyclerView mWorkRV;
+ private AllAppsRecyclerView mCurrentRV;
+ private ViewGroup mParent;
+ private boolean mHeaderCollapsed;
+ private int mSnappedScrolledY;
+ private int mTranslationY;
+ private boolean mForwardToRecyclerView;
+
+ protected int mMaxTranslation;
+
+ public FloatingHeaderView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTabLayout = findViewById(R.id.tabs);
+ }
+
+ public void setup(AllAppsContainerView.AdapterHolder[] mAH, boolean tabsHidden) {
+ mTabLayout.setVisibility(tabsHidden ? View.GONE : View.VISIBLE);
+ mMainRV = setupRV(mMainRV, mAH[AllAppsContainerView.AdapterHolder.MAIN].recyclerView);
+ mWorkRV = setupRV(mWorkRV, mAH[AllAppsContainerView.AdapterHolder.WORK].recyclerView);
+ mParent = (ViewGroup) mMainRV.getParent();
+ setMainActive(true);
+ reset();
+ }
+
+ private AllAppsRecyclerView setupRV(AllAppsRecyclerView old, AllAppsRecyclerView updated) {
+ if (old != updated && updated != null ) {
+ updated.addOnScrollListener(mOnScrollListener);
+ }
+ return updated;
+ }
+
+ public void setMainActive(boolean active) {
+ mCurrentRV = active ? mMainRV : mWorkRV;
+ }
+
+ public int getMaxTranslation() {
+ return mMaxTranslation;
+ }
+
+ private boolean canSnapAt(int currentScrollY) {
+ return Math.abs(currentScrollY) <= mMaxTranslation;
+ }
+
+ private void moved(final int currentScrollY) {
+ if (mHeaderCollapsed) {
+ if (currentScrollY <= mSnappedScrolledY) {
+ if (canSnapAt(currentScrollY)) {
+ mSnappedScrolledY = currentScrollY;
+ }
+ } else {
+ mHeaderCollapsed = false;
+ }
+ mTranslationY = currentScrollY;
+ } else if (!mHeaderCollapsed) {
+ mTranslationY = currentScrollY - mSnappedScrolledY - mMaxTranslation;
+
+ // update state vars
+ if (mTranslationY >= 0) { // expanded: must not move down further
+ mTranslationY = 0;
+ mSnappedScrolledY = currentScrollY - mMaxTranslation;
+ } else if (mTranslationY <= -mMaxTranslation) { // hide or stay hidden
+ mHeaderCollapsed = true;
+ mSnappedScrolledY = currentScrollY;
+ }
+ }
+ }
+
+ protected void applyScroll(int uncappedY, int currentY) { }
+
+ protected void apply() {
+ int uncappedTranslationY = mTranslationY;
+ mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
+ applyScroll(uncappedTranslationY, mTranslationY);
+ mTabLayout.setTranslationY(mTranslationY);
+ mClip.top = mMaxTranslation + mTranslationY;
+ // clipping on a draw might cause additional redraw
+ mMainRV.setClipBounds(mClip);
+ if (mWorkRV != null) {
+ mWorkRV.setClipBounds(mClip);
+ }
+ }
+
+ public void reset() {
+ int translateTo = 0;
+ mAnimator.setIntValues(mTranslationY, translateTo);
+ mAnimator.addUpdateListener(this);
+ mAnimator.setDuration(150);
+ mAnimator.start();
+ mHeaderCollapsed = false;
+ mSnappedScrolledY = -mMaxTranslation;
+ mCurrentRV.scrollToTop();
+ }
+
+ public boolean isExpanded() {
+ return !mHeaderCollapsed;
+ }
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslationY = (Integer) animation.getAnimatedValue();
+ apply();
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ calcOffset(mTempOffset);
+ ev.offsetLocation(mTempOffset.x, mTempOffset.y);
+ mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev);
+ ev.offsetLocation(-mTempOffset.x, -mTempOffset.y);
+ return mForwardToRecyclerView || super.onInterceptTouchEvent(ev);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mForwardToRecyclerView) {
+ // take this view's and parent view's (view pager) location into account
+ calcOffset(mTempOffset);
+ event.offsetLocation(mTempOffset.x, mTempOffset.y);
+ try {
+ return mCurrentRV.onTouchEvent(event);
+ } finally {
+ event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
+ }
+ } else {
+ return super.onTouchEvent(event);
+ }
+ }
+
+ private void calcOffset(Point p) {
+ p.x = getLeft() - mCurrentRV.getLeft() - mParent.getLeft();
+ p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
+ }
+}
+
+
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/LandscapeFastScroller.java b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
deleted file mode 100644
index cdde657..0000000
--- a/src/com/android/launcher3/allapps/LandscapeFastScroller.java
+++ /dev/null
@@ -1,63 +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.util.AttributeSet;
-import android.view.MotionEvent;
-
-import com.android.launcher3.views.RecyclerViewFastScroller;
-
-/**
- * Extension of {@link RecyclerViewFastScroller} to be used in landscape layout.
- */
-public class LandscapeFastScroller extends RecyclerViewFastScroller {
-
- public LandscapeFastScroller(Context context) {
- super(context);
- }
-
- public LandscapeFastScroller(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public LandscapeFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public boolean handleTouchEvent(MotionEvent ev) {
- // We handle our own touch event, no need to handle recycler view touch delegates.
- return false;
- }
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- event.offsetLocation(0, -mRv.getPaddingTop());
- if (super.handleTouchEvent(event)) {
- getParent().requestDisallowInterceptTouchEvent(true);
- }
- event.offsetLocation(0, mRv.getPaddingTop());
- return true;
- }
-
- @Override
- public boolean shouldBlockIntercept(int x, int y) {
- // If the user touched the scroll bar area, block swipe
- return x >= 0 && x < getWidth() && y >= 0 && y < getHeight();
- }
-}
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
new file mode 100644
index 0000000..a069d5d
--- /dev/null
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -0,0 +1,176 @@
+/*
+ * 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.content.SharedPreferences;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+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 implements PageIndicator {
+ private static final int POSITION_PERSONAL = 0;
+ private static final int POSITION_WORK = 1;
+
+ private static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab";
+
+ private final Paint mSelectedIndicatorPaint;
+ private final Paint mDividerPaint;
+ private final SharedPreferences mSharedPreferences;
+
+ private int mSelectedIndicatorHeight;
+ private int mIndicatorLeft = -1;
+ private int mIndicatorRight = -1;
+ 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);
+ setWillNotDraw(false);
+
+ mSelectedIndicatorHeight =
+ getResources().getDimensionPixelSize(R.dimen.all_apps_tabs_indicator_height);
+
+ mSelectedIndicatorPaint = new Paint();
+ mSelectedIndicatorPaint.setColor(
+ Themes.getAttrColor(context, android.R.attr.colorAccent));
+
+ mDividerPaint = new Paint();
+ mDividerPaint.setColor(Themes.getAttrColor(context, android.R.attr.colorControlHighlight));
+ mDividerPaint.setStrokeWidth(
+ getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
+
+ mSharedPreferences = Launcher.getLauncher(getContext()).getSharedPrefs();
+ mIsRtl = Utilities.isRtl(getResources());
+ }
+
+ private void updateIndicatorPosition(float scrollOffset) {
+ mScrollOffset = scrollOffset;
+ updateIndicatorPosition();
+ }
+
+ private void updateTabTextColor(int pos) {
+ mSelectedPosition = pos;
+ for (int i = 0; i < getChildCount(); i++) {
+ Button tab = (Button) getChildAt(i);
+ tab.setSelected(i == pos);
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ updateTabTextColor(mSelectedPosition);
+ updateIndicatorPosition(mScrollOffset);
+ }
+
+ private void updateIndicatorPosition() {
+ 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;
+ mIndicatorRight = right;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ float y = getHeight() - mDividerPaint.getStrokeWidth();
+ canvas.drawLine(getPaddingLeft(), y, getWidth() - getPaddingRight(), y, mDividerPaint);
+ canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
+ mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
+ }
+
+ public void highlightWorkTabIfNecessary() {
+ if (mSharedPreferences.getBoolean(KEY_SHOWED_PEEK_WORK_TAB, false)) {
+ return;
+ }
+ if (mLastActivePage != POSITION_PERSONAL) {
+ return;
+ }
+ highlightWorkTab();
+ mSharedPreferences.edit().putBoolean(KEY_SHOWED_PEEK_WORK_TAB, true).apply();
+ }
+
+ private void highlightWorkTab() {
+ View v = getChildAt(POSITION_WORK);
+ v.post(() -> {
+ v.setPressed(true);
+ v.setPressed(false);
+ });
+ }
+
+ @Override
+ public void setScroll(int currentScroll, int totalScroll) {
+ float scrollOffset = ((float) currentScroll) / totalScroll;
+ updateIndicatorPosition(scrollOffset);
+ }
+
+ @Override
+ public void setActiveMarker(int activePage) {
+ updateTabTextColor(activePage);
+ if (mContainerView != null && mLastActivePage != activePage) {
+ mContainerView.onTabChanged(activePage);
+ }
+ mLastActivePage = activePage;
+ }
+
+ public void setContainerView(AllAppsContainerView containerView) {
+ mContainerView = containerView;
+ }
+
+ @Override
+ public void setMarkersCount(int numMarkers) { }
+
+ @Override
+ public void setPageDescription(CharSequence description) {
+ // We don't want custom page description as the tab-bar already has two tabs with their
+ // own descriptions.
+ }
+}
diff --git a/src/com/android/launcher3/allapps/PredictionRowView.java b/src/com/android/launcher3/allapps/PredictionRowView.java
deleted file mode 100644
index 5551f07..0000000
--- a/src/com/android/launcher3/allapps/PredictionRowView.java
+++ /dev/null
@@ -1,139 +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.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ComponentKeyMapper;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-public class PredictionRowView extends LinearLayout {
-
- private static final String TAG = "PredictionRowView";
-
- private HashMap<ComponentKey, AppInfo> mComponentToAppMap;
- private int mNumPredictedAppsPerRow;
- // The set of predicted app component names
- private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
- // The set of predicted apps resolved from the component names and the current set of apps
- private final List<AppInfo> mPredictedApps = new ArrayList<>();
-
- public PredictionRowView(@NonNull Context context) {
- this(context, null);
- }
-
- public PredictionRowView(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- setOrientation(LinearLayout.HORIZONTAL);
- }
-
- public void setComponentToAppMap(HashMap<ComponentKey, AppInfo> componentToAppMap) {
- this.mComponentToAppMap = componentToAppMap;
- }
-
- /**
- * Sets the number of apps per row.
- */
- public void setNumAppsPerRow(int numPredictedAppsPerRow) {
- mNumPredictedAppsPerRow = numPredictedAppsPerRow;
- }
-
- public void onAppsUpdated() {
- // TODO
- }
-
- /**
- * Returns the predicted apps.
- */
- public List<AppInfo> getPredictedApps() {
- return mPredictedApps;
- }
-
- /**
- * Sets the current set of predicted apps.
- *
- * This can be called before we get the full set of applications, we should merge the results
- * only in onAppsUpdated() which is idempotent.
- *
- * If the number of predicted apps is the same as the previous list of predicted apps,
- * we can optimize by swapping them in place.
- */
- public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
- mPredictedAppComponents.clear();
- mPredictedAppComponents.addAll(apps);
-
- List<AppInfo> newPredictedApps = processPredictedAppComponents(apps);
- // We only need to do work if any of the visible predicted apps have changed.
- if (!newPredictedApps.equals(mPredictedApps)) {
- if (newPredictedApps.size() == mPredictedApps.size()) {
- swapInNewPredictedApps(newPredictedApps);
- } else {
- // We need to update the appIndex of all the items.
- onAppsUpdated();
- }
- }
- }
-
- private List<AppInfo> processPredictedAppComponents(
- List<ComponentKeyMapper<AppInfo>> components) {
- if (mComponentToAppMap.isEmpty()) {
- // Apps have not been bound yet.
- return Collections.emptyList();
- }
-
- List<AppInfo> predictedApps = new ArrayList<>();
- for (ComponentKeyMapper<AppInfo> mapper : components) {
- AppInfo info = mapper.getItem(mComponentToAppMap);
- if (info != null) {
- predictedApps.add(info);
- } else {
- if (FeatureFlags.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Predicted app not found: " + mapper);
- }
- }
- // Stop at the number of predicted apps
- if (predictedApps.size() == mNumPredictedAppsPerRow) {
- break;
- }
- }
- return predictedApps;
- }
-
- /**
- * Swaps out the old predicted apps with the new predicted apps, in place. This optimization
- * allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged.
- *
- * Note: This should only be called if the # of predicted apps is the same.
- * This method assumes that predicted apps are the first items in the adapter.
- */
- private void swapInNewPredictedApps(List<AppInfo> apps) {
- // TODO
- }
-
-}
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index f562b6a..bb17ed5 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -35,12 +35,6 @@
@NonNull SpringAnimation getSpringForFling();
/**
- * Notifies the search manager that the apps-list has changed and the search UI should be
- * updated accordingly.
- */
- void refreshSearchResult();
-
- /**
* Notifies the search manager to close any active search session.
*/
void reset();
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
new file mode 100644
index 0000000..e7cf092
--- /dev/null
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -0,0 +1,94 @@
+/*
+ * 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.os.AsyncTask;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+import android.widget.Switch;
+
+import com.android.launcher3.compat.UserManagerCompat;
+
+import java.util.List;
+
+public class WorkModeSwitch extends Switch {
+
+ public WorkModeSwitch(Context context) {
+ super(context);
+ }
+
+ public WorkModeSwitch(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ // No-op, do not change the checked state until broadcast is received.
+ }
+
+ @Override
+ public void toggle() {
+ trySetQuietModeEnabledToAllProfilesAsync(isChecked());
+ }
+
+ private void setCheckedInternal(boolean checked) {
+ super.setChecked(checked);
+ }
+
+ public void refresh() {
+ UserManagerCompat userManager = UserManagerCompat.getInstance(getContext());
+ setCheckedInternal(!userManager.isAnyProfileQuietModeEnabled());
+ setEnabled(true);
+ }
+
+ private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
+ new AsyncTask<Void, Void, Boolean>() {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ setEnabled(false);
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... voids) {
+ UserManagerCompat userManager = UserManagerCompat.getInstance(getContext());
+ List<UserHandle> userProfiles = userManager.getUserProfiles();
+ boolean showConfirm = false;
+ for (UserHandle userProfile : userProfiles) {
+ if (Process.myUserHandle().equals(userProfile)) {
+ continue;
+ }
+ showConfirm |= !userManager.requestQuietModeEnabled(enabled, userProfile);
+ }
+ return showConfirm;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean showConfirm) {
+ if (showConfirm) {
+ setEnabled(true);
+ }
+ }
+ }.execute();
+ }
+}
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index bf03a0e..e83904f 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -28,8 +28,6 @@
import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
-import com.android.launcher3.discovery.AppDiscoveryItem;
-import com.android.launcher3.discovery.AppDiscoveryUpdateState;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
@@ -164,18 +162,6 @@
* Called when the search results should be cleared.
*/
void clearSearchResult();
-
- /**
- * Called when the app discovery is providing an update of search, which can either be
- * START for starting a new discovery,
- * UPDATE for providing a new search result, can be called multiple times,
- * END for indicating the end of results.
- *
- * @param app result item if UPDATE, else null
- * @param app the update state, START, UPDATE or END
- */
- void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
- @NonNull AppDiscoveryUpdateState state);
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index e65a2c4..a56c8b8 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -21,7 +21,6 @@
import android.support.animation.SpringAnimation;
import android.support.animation.SpringForce;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableString;
@@ -37,10 +36,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager;
-import com.android.launcher3.discovery.AppDiscoveryItem;
-import com.android.launcher3.discovery.AppDiscoveryUpdateState;
import com.android.launcher3.graphics.TintedDrawableSpan;
import com.android.launcher3.util.ComponentKey;
@@ -50,7 +48,8 @@
* Layout to contain the All-apps search UI.
*/
public class AppsSearchContainerLayout extends FrameLayout
- implements SearchUiManager, AllAppsSearchBarController.Callbacks {
+ implements SearchUiManager, AllAppsSearchBarController.Callbacks,
+ AllAppsStore.OnUpdateListener {
private final Launcher mLauncher;
private final int mMinHeight;
@@ -113,9 +112,22 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mLauncher.getAppsView().getAppsStore().addUpdateListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mLauncher.getAppsView().getAppsStore().removeUpdateListener(this);
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ if (!dp.isVerticalBarLayout()) {
+ getLayoutParams().height = dp.getInsets().top + mMinHeight;
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@@ -136,7 +148,7 @@
}
@Override
- public void refreshSearchResult() {
+ public void onAppsUpdated() {
mSearchBarController.refreshSearchResult();
}
@@ -184,15 +196,7 @@
mSearchQueryBuilder.clear();
mSearchQueryBuilder.clearSpans();
Selection.setSelection(mSearchQueryBuilder, 0);
- }
-
- @Override
- public void onAppDiscoverySearchUpdate(
- @Nullable AppDiscoveryItem app, @NonNull AppDiscoveryUpdateState state) {
- if (!mLauncher.isDestroyed()) {
- mApps.onAppDiscoverySearchUpdate(app, state);
- notifyResultChanged();
- }
+ mAppsView.onClearSearchResult();
}
private void notifyResultChanged() {
@@ -208,7 +212,7 @@
int oldLeft, int oldTop, int oldRight, int oldBottom) {
DeviceProfile dp = mLauncher.getDeviceProfile();
if (!dp.isVerticalBarLayout()) {
- Rect insets = mLauncher.getDragLayer().getInsets();
+ Rect insets = dp.getInsets();
int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight)
+ ((MarginLayoutParams) getLayoutParams()).bottomMargin;
diff --git a/src/com/android/launcher3/anim/AnimationLayerSet.java b/src/com/android/launcher3/anim/AnimationLayerSet.java
deleted file mode 100644
index f0b3458..0000000
--- a/src/com/android/launcher3/anim/AnimationLayerSet.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.anim;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.util.ArrayMap;
-import android.view.View;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * Helper class to automatically build view hardware layers for the duration of an animation.
- */
-public class AnimationLayerSet extends AnimatorListenerAdapter {
-
- private final ArrayMap<View, Integer> mViewsToLayerTypeMap;
-
- public AnimationLayerSet() {
- mViewsToLayerTypeMap = new ArrayMap<>();
- }
-
- public AnimationLayerSet(View v) {
- mViewsToLayerTypeMap = new ArrayMap<>(1);
- addView(v);
- }
-
- public void addView(View v) {
- mViewsToLayerTypeMap.put(v, v.getLayerType());
- }
-
- @Override
- public void onAnimationStart(Animator animation) {
- // Enable all necessary layers
- Iterator<Map.Entry<View, Integer>> itr = mViewsToLayerTypeMap.entrySet().iterator();
- while (itr.hasNext()) {
- Map.Entry<View, Integer> entry = itr.next();
- View v = entry.getKey();
- entry.setValue(v.getLayerType());
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- if (v.isAttachedToWindow() && v.getVisibility() == View.VISIBLE) {
- v.buildLayer();
- }
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- Iterator<Map.Entry<View, Integer>> itr = mViewsToLayerTypeMap.entrySet().iterator();
- while (itr.hasNext()) {
- Map.Entry<View, Integer> entry = itr.next();
- entry.getKey().setLayerType(entry.getValue(), null);
- }
- }
-}
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 826a20e..68e9847 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -33,6 +33,12 @@
*/
public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
+ /**
+ * Creates an animation controller for the provided animation.
+ * The actual duration does not matter as the animation is manually controlled. It just
+ * needs to be larger than the total number of pixels so that we don't have jittering due
+ * to float (animation-fraction * total duration) to int conversion.
+ */
public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
/**
@@ -45,12 +51,14 @@
private final long mDuration;
protected final AnimatorSet mAnim;
+ private AnimatorSet mOriginalTarget;
protected float mCurrentFraction;
private Runnable mEndAction;
protected AnimatorPlaybackController(AnimatorSet anim, long duration) {
mAnim = anim;
+ mOriginalTarget = mAnim;
mDuration = duration;
mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
@@ -63,6 +71,25 @@
return mAnim;
}
+ public void setOriginalTarget(AnimatorSet anim) {
+ mOriginalTarget = anim;
+ }
+
+ public AnimatorSet getOriginalTarget() {
+ return mOriginalTarget;
+ }
+
+ public long getDuration() {
+ return mDuration;
+ }
+
+ public AnimatorPlaybackController cloneFor(AnimatorSet anim) {
+ AnimatorPlaybackController controller = AnimatorPlaybackController.wrap(anim, mDuration);
+ controller.setOriginalTarget(mOriginalTarget);
+ controller.setPlayFraction(mCurrentFraction);
+ return controller;
+ }
+
/**
* Starts playing the animation forward from current position.
*/
@@ -211,6 +238,6 @@
}
private static <T> List<T> nonNullList(ArrayList<T> list) {
- return list == null ? Collections.<T>emptyList() : list;
+ return list == null ? Collections.emptyList() : list;
}
}
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
new file mode 100644
index 0000000..7cd9651
--- /dev/null
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.anim;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+
+import com.android.launcher3.LauncherAnimUtils;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class for building animator set
+ */
+public class AnimatorSetBuilder {
+
+ protected final ArrayList<Animator> mAnims = new ArrayList<>();
+ private long mStartDelay = 0;
+
+ /**
+ * Associates a tag with all the animations added after this call.
+ */
+ public void startTag(Object obj) { }
+
+ public void play(Animator anim) {
+ mAnims.add(anim);
+ }
+
+ public void setStartDelay(long startDelay) {
+ mStartDelay = startDelay;
+ }
+
+ public AnimatorSet build() {
+ AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+ anim.playTogether(mAnims);
+ anim.setStartDelay(mStartDelay);
+ return anim;
+ }
+}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 8826e64..6078776 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -16,10 +16,12 @@
package com.android.launcher3.anim;
+import android.graphics.Path;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
+import android.view.animation.OvershootInterpolator;
import android.view.animation.PathInterpolator;
@@ -42,6 +44,26 @@
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.6f,0, 0.4f, 1);
+
+ public static final Interpolator EXAGGERATED_EASE;
+
+ static {
+ Path exaggeratedEase = new Path();
+ exaggeratedEase.moveTo(0, 0);
+ exaggeratedEase.cubicTo(0.05f, 0f, 0.133333f, 0.08f, 0.166666f, 0.4f);
+ exaggeratedEase.cubicTo(0.225f, 0.94f, 0.5f, 1f, 1f, 1f);
+ EXAGGERATED_EASE = new PathInterpolator(exaggeratedEase);
+ }
+
+ public static final Interpolator APP_CLOSE_ALPHA = new PathInterpolator(0.4f, 0, 1f, 1f);
+
+ public static final Interpolator OVERSHOOT_0 = new OvershootInterpolator(0);
+
+ public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
+ new PathInterpolator(0.3f, 0f, 0.1f, 1f);
+
/**
* Inversion of zInterpolate, compounded with an ease-out.
*/
diff --git a/src/com/android/launcher3/anim/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
index 1312da9..afb8875 100644
--- a/src/com/android/launcher3/anim/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -34,7 +34,6 @@
final float elevation = revealView.getElevation();
va.addListener(new AnimatorListenerAdapter() {
- private boolean mWasCanceled = false;
private boolean mIsClippedToOutline;
private ViewOutlineProvider mOldOutlineProvider;
@@ -49,18 +48,11 @@
}
}
- @Override
- public void onAnimationCancel(Animator animation) {
- mWasCanceled = true;
- }
-
public void onAnimationEnd(Animator animation) {
- if (!mWasCanceled) {
- revealView.setOutlineProvider(mOldOutlineProvider);
- revealView.setClipToOutline(mIsClippedToOutline);
- if (shouldRemoveElevationDuringAnimation()) {
- revealView.setTranslationZ(0);
- }
+ revealView.setOutlineProvider(mOldOutlineProvider);
+ revealView.setClipToOutline(mIsClippedToOutline);
+ if (shouldRemoveElevationDuringAnimation()) {
+ revealView.setTranslationZ(0);
}
}
@@ -88,4 +80,8 @@
public float getRadius() {
return mOutlineRadius;
}
+
+ public void getOutline(Rect out) {
+ out.set(mOutline);
+ }
}
diff --git a/src/com/android/launcher3/badge/BadgeInfo.java b/src/com/android/launcher3/badge/BadgeInfo.java
index 08d8ad4..f03544f 100644
--- a/src/com/android/launcher3/badge/BadgeInfo.java
+++ b/src/com/android/launcher3/badge/BadgeInfo.java
@@ -16,14 +16,6 @@
package com.android.launcher3.badge;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Shader;
-import android.graphics.drawable.Drawable;
-import android.support.annotation.Nullable;
-
import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.util.PackageUserKey;
@@ -53,15 +45,6 @@
*/
private int mTotalCount;
- /** This will only be initialized if the badge should display the notification icon. */
- private NotificationInfo mNotificationInfo;
-
- /**
- * When retrieving the notification icon, we draw it into this shader, which can be clipped
- * as necessary when drawn in a badge.
- */
- private Shader mNotificationIcon;
-
public BadgeInfo(PackageUserKey packageUserKey) {
mPackageUserKey = packageUserKey;
mNotificationKeys = new ArrayList<>();
@@ -110,44 +93,6 @@
return Math.min(mTotalCount, MAX_COUNT);
}
- public void setNotificationToShow(@Nullable NotificationInfo notificationInfo) {
- mNotificationInfo = notificationInfo;
- mNotificationIcon = null;
- }
-
- public boolean hasNotificationToShow() {
- return mNotificationInfo != null;
- }
-
- /**
- * Returns a shader to set on a Paint that will draw the notification icon in a badge.
- *
- * The shader is cached until {@link #setNotificationToShow(NotificationInfo)} is called.
- */
- public @Nullable Shader getNotificationIconForBadge(Context context, int badgeColor,
- int badgeSize, int badgePadding) {
- if (mNotificationInfo == null) {
- return null;
- }
- if (mNotificationIcon == null) {
- Drawable icon = mNotificationInfo.getIconForBackground(context, badgeColor)
- .getConstantState().newDrawable();
- int iconSize = badgeSize - badgePadding * 2;
- icon.setBounds(0, 0, iconSize, iconSize);
- Bitmap iconBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(iconBitmap);
- canvas.translate(badgePadding, badgePadding);
- icon.draw(canvas);
- mNotificationIcon = new BitmapShader(iconBitmap, Shader.TileMode.CLAMP,
- Shader.TileMode.CLAMP);
- }
- return mNotificationIcon;
- }
-
- public boolean isIconLarge() {
- return mNotificationInfo != null && mNotificationInfo.isIconLarge();
- }
-
/**
* Whether newBadge represents the same PackageUserKey as this badge, and icons with
* this badge should be invalidated. So, for instance, if a badge has 3 notifications
@@ -158,7 +103,6 @@
*/
public boolean shouldBeInvalidated(BadgeInfo newBadge) {
return mPackageUserKey.equals(newBadge.mPackageUserKey)
- && (getNotificationCount() != newBadge.getNotificationCount()
- || hasNotificationToShow());
+ && (getNotificationCount() != newBadge.getNotificationCount());
}
}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index 6ce334e..72d49f0 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -16,21 +16,17 @@
package com.android.launcher3.badge;
-import android.content.Context;
-import android.content.res.Resources;
+import static android.graphics.Paint.ANTI_ALIAS_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.Shader;
-import android.support.annotation.Nullable;
import android.util.Log;
-import android.util.SparseArray;
-import com.android.launcher3.R;
-import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.ShadowGenerator;
/**
@@ -41,147 +37,63 @@
private static final String TAG = "BadgeRenderer";
- private static final boolean DOTS_ONLY = true;
-
// The badge sizes are defined as percentages of the app icon size.
private static final float SIZE_PERCENTAGE = 0.38f;
- // Used to expand the width of the badge for each additional digit.
- private static final float CHAR_SIZE_PERCENTAGE = 0.12f;
- private static final float TEXT_SIZE_PERCENTAGE = 0.26f;
- private static final float OFFSET_PERCENTAGE = 0.02f;
- private static final float STACK_OFFSET_PERCENTAGE_X = 0.05f;
- private static final float STACK_OFFSET_PERCENTAGE_Y = 0.06f;
+
+ // Extra scale down of the dot
private static final float DOT_SCALE = 0.6f;
- private final Context mContext;
- private final int mSize;
- private final int mCharSize;
- private final int mTextHeight;
+ // Used to expand the width of the badge for each additional digit.
+ private static final float OFFSET_PERCENTAGE = 0.02f;
+
+ private final float mDotCenterOffset;
private final int mOffset;
- private final int mStackOffsetX;
- private final int mStackOffsetY;
- private final IconDrawer mLargeIconDrawer;
- private final IconDrawer mSmallIconDrawer;
- private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG
- | Paint.FILTER_BITMAP_FLAG);
- private final SparseArray<Bitmap> mBackgroundsWithShadow;
+ private final float mCircleRadius;
+ private final Paint mCirclePaint = new Paint(ANTI_ALIAS_FLAG | FILTER_BITMAP_FLAG);
- public BadgeRenderer(Context context, int iconSizePx) {
- mContext = context;
- Resources res = context.getResources();
- mSize = (int) (SIZE_PERCENTAGE * iconSizePx);
- mCharSize = (int) (CHAR_SIZE_PERCENTAGE * iconSizePx);
+ private final Bitmap mBackgroundWithShadow;
+ private final float mBitmapOffset;
+
+ public BadgeRenderer(int iconSizePx) {
+ mDotCenterOffset = SIZE_PERCENTAGE * iconSizePx;
mOffset = (int) (OFFSET_PERCENTAGE * iconSizePx);
- mStackOffsetX = (int) (STACK_OFFSET_PERCENTAGE_X * iconSizePx);
- mStackOffsetY = (int) (STACK_OFFSET_PERCENTAGE_Y * iconSizePx);
- mTextPaint.setTextSize(iconSizePx * TEXT_SIZE_PERCENTAGE);
- mTextPaint.setTextAlign(Paint.Align.CENTER);
- mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
- mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
- // Measure the text height.
- Rect tempTextHeight = new Rect();
- mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
- mTextHeight = tempTextHeight.height();
- mBackgroundsWithShadow = new SparseArray<>(3);
+ int size = (int) (DOT_SCALE * mDotCenterOffset);
+ ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
+ mBackgroundWithShadow = builder.setupBlurForSize(size).createPill(size, size);
+ mCircleRadius = builder.radius;
+
+ mBitmapOffset = -mBackgroundWithShadow.getHeight() * 0.5f; // Same as width.
}
/**
* Draw a circle in the top right corner of the given bounds, and draw
* {@link BadgeInfo#getNotificationCount()} on top of the circle.
- * @param palette The colors (based on the icon) to use for the badge.
- * @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
+ * @param color The color (based on the icon) to use for the badge.
* @param iconBounds The bounds of the icon being badged.
* @param badgeScale The progress of the animation, from 0 to 1.
* @param spaceForOffset How much space is available to offset the badge up and to the right.
*/
- public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
- Rect iconBounds, float badgeScale, Point spaceForOffset) {
- if (palette == null || iconBounds == null || spaceForOffset == null) {
+ public void draw(
+ Canvas canvas, int color, Rect iconBounds, float badgeScale, Point spaceForOffset) {
+ if (iconBounds == null || spaceForOffset == null) {
Log.e(TAG, "Invalid null argument(s) passed in call to draw.");
return;
}
- mTextPaint.setColor(palette.textColor);
- IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
- ? mLargeIconDrawer : mSmallIconDrawer;
- Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
- mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
- String notificationCount = badgeInfo == null ? "0"
- : String.valueOf(badgeInfo.getNotificationCount());
- int numChars = notificationCount.length();
- int width = DOTS_ONLY ? mSize : mSize + mCharSize * (numChars - 1);
- // Lazily load the background with shadow.
- Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
- if (backgroundWithShadow == null) {
- backgroundWithShadow = new ShadowGenerator.Builder(Color.WHITE)
- .setupBlurForSize(mSize).createPill(width, mSize);
- mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
- }
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// We draw the badge relative to its center.
- int badgeCenterX = iconBounds.right - width / 2;
- int badgeCenterY = iconBounds.top + mSize / 2;
- boolean isText = !DOTS_ONLY && badgeInfo != null && badgeInfo.getNotificationCount() != 0;
- boolean isIcon = !DOTS_ONLY && icon != null;
- boolean isDot = !(isText || isIcon);
- if (isDot) {
- badgeScale *= DOT_SCALE;
- }
+ float badgeCenterX = iconBounds.right - mDotCenterOffset / 2;
+ float badgeCenterY = iconBounds.top + mDotCenterOffset / 2;
+
int offsetX = Math.min(mOffset, spaceForOffset.x);
int offsetY = Math.min(mOffset, spaceForOffset.y);
canvas.translate(badgeCenterX + offsetX, badgeCenterY - offsetY);
canvas.scale(badgeScale, badgeScale);
- // Prepare the background and shadow and possible stacking effect.
- mBackgroundPaint.setColorFilter(palette.backgroundColorMatrixFilter);
- int backgroundWithShadowSize = backgroundWithShadow.getHeight(); // Same as width.
- boolean shouldStack = !isDot && badgeInfo != null
- && badgeInfo.getNotificationKeys().size() > 1;
- if (shouldStack) {
- int offsetDiffX = mStackOffsetX - mOffset;
- int offsetDiffY = mStackOffsetY - mOffset;
- canvas.translate(offsetDiffX, offsetDiffY);
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- canvas.translate(-offsetDiffX, -offsetDiffY);
- }
- if (isText) {
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
- } else if (isIcon) {
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- iconDrawer.drawIcon(icon, canvas);
- } else if (isDot) {
- mBackgroundPaint.setColorFilter(palette.saturatedBackgroundColorMatrixFilter);
- canvas.drawBitmap(backgroundWithShadow, -backgroundWithShadowSize / 2,
- -backgroundWithShadowSize / 2, mBackgroundPaint);
- }
+ mCirclePaint.setColor(Color.BLACK);
+ canvas.drawBitmap(mBackgroundWithShadow, mBitmapOffset, mBitmapOffset, mCirclePaint);
+ mCirclePaint.setColor(color);
+ canvas.drawCircle(0, 0, mCircleRadius, mCirclePaint);
canvas.restore();
}
-
- /** Draws the notification icon with padding of a given size. */
- private class IconDrawer {
-
- private final int mPadding;
- private final Bitmap mCircleClipBitmap;
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
- Paint.FILTER_BITMAP_FLAG);
-
- public IconDrawer(int padding) {
- mPadding = padding;
- mCircleClipBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ALPHA_8);
- Canvas canvas = new Canvas();
- canvas.setBitmap(mCircleClipBitmap);
- canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2 - padding, mPaint);
- }
-
- public void drawIcon(Shader icon, Canvas canvas) {
- mPaint.setShader(icon);
- canvas.drawBitmap(mCircleClipBitmap, -mSize / 2, -mSize / 2, mPaint);
- mPaint.setShader(null);
- }
- }
}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
new file mode 100644
index 0000000..0c78381
--- /dev/null
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -0,0 +1,47 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+public class AccessibilityManagerCompat {
+
+ public static boolean isAccessibilityEnabled(Context context) {
+ return getManager(context).isEnabled();
+ }
+
+ public static boolean isObservedEventType(Context context, int eventType) {
+ // TODO: Use new API once available
+ return isAccessibilityEnabled(context);
+ }
+
+ public static void sendCustomAccessibilityEvent(View target, int type, String text) {
+ if (isObservedEventType(target.getContext(), type)) {
+ AccessibilityEvent event = AccessibilityEvent.obtain(type);
+ target.onInitializeAccessibilityEvent(event);
+ event.getText().add(text);
+ getManager(target.getContext()).sendAccessibilityEvent(event);
+ }
+ }
+
+ private static AccessibilityManager getManager(Context context) {
+ return (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index 3214b46..173d0d1 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -137,8 +137,9 @@
ShortcutInfoCompat compat = new ShortcutInfoCompat(request.getShortcutInfo());
ShortcutInfo info = new ShortcutInfo(compat, context);
// Apply the unbadged icon and fetch the actual icon asynchronously.
- info.iconBitmap = LauncherIcons
- .createShortcutIcon(compat, context, false /* badged */);
+ LauncherIcons li = LauncherIcons.obtain(context);
+ li.createShortcutIcon(compat, false /* badged */).applyTo(info);
+ li.recycle();
LauncherAppState.getInstance(context).getModel()
.updateAndBindShortcutInfo(info, compat);
return info;
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 25808d2..62055dc 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -33,7 +33,9 @@
public static UserManagerCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
- if (Utilities.ATLEAST_NOUGAT_MR1) {
+ if (Utilities.ATLEAST_P) {
+ sInstance = new UserManagerCompatVP(context.getApplicationContext());
+ } else if (Utilities.ATLEAST_NOUGAT_MR1) {
sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
} else if (Utilities.ATLEAST_NOUGAT) {
sInstance = new UserManagerCompatVN(context.getApplicationContext());
@@ -61,4 +63,6 @@
public abstract boolean isUserUnlocked(UserHandle user);
public abstract boolean isDemoUser();
+ public abstract boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user);
+ public abstract boolean isAnyProfileQuietModeEnabled();
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index bb42573..e57786d 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -83,6 +83,16 @@
}
@Override
+ public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
+ return false;
+ }
+
+ @Override
+ public boolean isAnyProfileQuietModeEnabled() {
+ return false;
+ }
+
+ @Override
public void enableAndResetCache() {
synchronized (this) {
mUsers = new LongArrayMap<>();
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
index 50a0217..3733565 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -19,8 +19,11 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
+import android.os.Process;
import android.os.UserHandle;
+import java.util.List;
+
@TargetApi(Build.VERSION_CODES.N)
public class UserManagerCompatVN extends UserManagerCompatVM {
@@ -37,5 +40,19 @@
public boolean isUserUnlocked(UserHandle user) {
return mUserManager.isUserUnlocked(user);
}
+
+ @Override
+ public boolean isAnyProfileQuietModeEnabled() {
+ List<UserHandle> userProfiles = getUserProfiles();
+ for (UserHandle userProfile : userProfiles) {
+ if (Process.myUserHandle().equals(userProfile)) {
+ continue;
+ }
+ if (isQuietModeEnabled(userProfile)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVP.java b/src/com/android/launcher3/compat/UserManagerCompatVP.java
new file mode 100644
index 0000000..2e8a8eb
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVP.java
@@ -0,0 +1,55 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class UserManagerCompatVP extends UserManagerCompatVNMr1 {
+ private static final String TAG = "UserManagerCompatVP";
+
+ private Method mRequestQuietModeEnabled;
+
+ UserManagerCompatVP(Context context) {
+ super(context);
+ // TODO: Replace it with proper API call once SDK is ready.
+ try {
+ mRequestQuietModeEnabled = UserManager.class.getDeclaredMethod(
+ "requestQuietModeEnabled", boolean.class, UserHandle.class);
+ } catch (NoSuchMethodException e) {
+ Log.e(TAG, "requestQuietModeEnabled is not available", e);
+ }
+ }
+
+ @Override
+ public boolean requestQuietModeEnabled(boolean enableQuietMode, UserHandle user) {
+ if (mRequestQuietModeEnabled == null) {
+ return false;
+ }
+ try {
+ return (boolean)
+ mRequestQuietModeEnabled.invoke(mUserManager, enableQuietMode, user);
+ } catch (IllegalAccessException | InvocationTargetException e) {
+ Log.e(TAG, "Failed to invoke mRequestQuietModeEnabled", e);
+ }
+ return false;
+ }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
index 4cc70d3..299f090 100644
--- a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -18,6 +18,7 @@
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.graphics.ColorExtractor.findDominantColorByHue;
import android.app.WallpaperInfo;
import android.app.WallpaperManager;
@@ -257,7 +258,7 @@
String value = VERSION_PREFIX + wallpaperId;
if (bitmap != null) {
- int color = Utilities.findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+ int color = findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
value += "," + color;
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 1924710..e494bea 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -31,8 +31,6 @@
public static final boolean IS_DOGFOOD_BUILD = false;
public static final String AUTHORITY = "com.android.launcher3.settings".intern();
- // Custom flags go below this
- public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
// When enabled allows to use any point on the fast scrollbar to start dragging.
public static final boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled the promise icon is visible in all apps while installation an app.
@@ -46,12 +44,6 @@
public static final boolean QSB_ON_FIRST_SCREEN = true;
// When enabled the all-apps icon is not added to the hotseat.
public static final boolean NO_ALL_APPS_ICON = true;
- // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in {@link FixedScaleDrawable}.
- public static final boolean LEGACY_ICON_TREATMENT = true;
- // When enabled, adaptive icons would have shadows baked when being stored to icon cache.
- public static final boolean ADAPTIVE_ICON_SHADOW = true;
- // When enabled, app discovery will be enabled if service is implemented
- public static final boolean DISCOVERY_ENABLED = false;
// When true, custom widgets are loaded using CustomWidgetParser.
public static final boolean ENABLE_CUSTOM_WIDGETS = false;
@@ -60,5 +52,7 @@
public static final boolean GO_DISABLE_WIDGETS = false;
// When enabled shows a work profile tab in all apps
- public static final boolean ALL_APPS_TABS_ENABLED = false;
+ public static final boolean ALL_APPS_TABS_ENABLED = true;
+
+ public static final boolean ENABLE_TWO_SWIPE_TARGETS = true;
}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java b/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
deleted file mode 100644
index ed5cfeb..0000000
--- a/src/com/android/launcher3/discovery/AppDiscoveryAppInfo.java
+++ /dev/null
@@ -1,70 +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.discovery;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import com.android.launcher3.AppInfo;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.ShortcutInfo;
-
-public class AppDiscoveryAppInfo extends AppInfo {
-
- public final boolean showAsDiscoveryItem;
- public final boolean isInstantApp;
- public final boolean isRecent;
- public final float rating;
- public final long reviewCount;
- public final @NonNull String publisher;
- public final @NonNull Intent installIntent;
- public final @NonNull Intent launchIntent;
- public final @Nullable String priceFormatted;
-
- public AppDiscoveryAppInfo(AppDiscoveryItem item) {
- this.intent = item.isInstantApp ? item.launchIntent : item.installIntent;
- this.title = item.title;
- this.iconBitmap = item.bitmap;
- this.usingLowResIcon = false;
- this.isInstantApp = item.isInstantApp;
- this.isRecent = item.isRecent;
- this.rating = item.starRating;
- this.showAsDiscoveryItem = true;
- this.publisher = item.publisher != null ? item.publisher : "";
- this.priceFormatted = item.price;
- this.componentName = new ComponentName(item.packageName, "");
- this.installIntent = item.installIntent;
- this.launchIntent = item.launchIntent;
- this.reviewCount = item.reviewCount;
- this.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
- }
-
- @Override
- public ShortcutInfo makeShortcut() {
- if (!isDragAndDropSupported()) {
- throw new RuntimeException("DnD is currently not supported for discovered store apps");
- }
- return super.makeShortcut();
- }
-
- public boolean isDragAndDropSupported() {
- return isInstantApp;
- }
-
-}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryItem.java b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
deleted file mode 100644
index 2e48b25..0000000
--- a/src/com/android/launcher3/discovery/AppDiscoveryItem.java
+++ /dev/null
@@ -1,64 +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.discovery;
-
-import android.content.Intent;
-import android.graphics.Bitmap;
-
-/**
- * This class represents the model for a discovered app via app discovery.
- * It holds all information for one result retrieved from an app discovery service.
- */
-public class AppDiscoveryItem {
-
- public final String packageName;
- public final boolean isInstantApp;
- public final boolean isRecent;
- public final float starRating;
- public final long reviewCount;
- public final Intent launchIntent;
- public final Intent installIntent;
- public final CharSequence title;
- public final String publisher;
- public final String price;
- public final Bitmap bitmap;
-
- public AppDiscoveryItem(String packageName,
- boolean isInstantApp,
- boolean isRecent,
- float starRating,
- long reviewCount,
- CharSequence title,
- String publisher,
- Bitmap bitmap,
- String price,
- Intent launchIntent,
- Intent installIntent) {
- this.packageName = packageName;
- this.isInstantApp = isInstantApp;
- this.isRecent = isRecent;
- this.starRating = starRating;
- this.reviewCount = reviewCount;
- this.launchIntent = launchIntent;
- this.installIntent = installIntent;
- this.title = title;
- this.publisher = publisher;
- this.price = price;
- this.bitmap = bitmap;
- }
-
-}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryItemView.java b/src/com/android/launcher3/discovery/AppDiscoveryItemView.java
deleted file mode 100644
index 809d724..0000000
--- a/src/com/android/launcher3/discovery/AppDiscoveryItemView.java
+++ /dev/null
@@ -1,97 +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.discovery;
-
-import android.content.Context;
-import android.support.annotation.NonNull;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import com.android.launcher3.R;
-
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-
-public class AppDiscoveryItemView extends RelativeLayout {
-
- private static boolean SHOW_REVIEW_COUNT = false;
-
- private ImageView mImage;
- private TextView mTitle;
- private TextView mRatingText;
- private RatingView mRatingView;
- private TextView mReviewCount;
- private TextView mPrice;
- private OnLongClickListener mOnLongClickListener;
-
- public AppDiscoveryItemView(Context context) {
- this(context, null);
- }
-
- public AppDiscoveryItemView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public AppDiscoveryItemView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- this.mImage = (ImageView) findViewById(R.id.image);
- this.mTitle = (TextView) findViewById(R.id.title);
- this.mRatingText = (TextView) findViewById(R.id.rating);
- this.mRatingView = (RatingView) findViewById(R.id.rating_view);
- this.mPrice = (TextView) findViewById(R.id.price);
- this.mReviewCount = (TextView) findViewById(R.id.review_count);
- }
-
- public void init(OnClickListener clickListener,
- AccessibilityDelegate accessibilityDelegate,
- OnLongClickListener onLongClickListener) {
- setOnClickListener(clickListener);
- mImage.setOnClickListener(clickListener);
- setAccessibilityDelegate(accessibilityDelegate);
- mOnLongClickListener = onLongClickListener;
- }
-
- public void apply(@NonNull AppDiscoveryAppInfo info) {
- setTag(info);
- mImage.setTag(info);
- mImage.setImageBitmap(info.iconBitmap);
- mImage.setOnLongClickListener(info.isDragAndDropSupported() ? mOnLongClickListener : null);
- mTitle.setText(info.title);
- mPrice.setText(info.priceFormatted != null ? info.priceFormatted : "");
- mReviewCount.setVisibility(SHOW_REVIEW_COUNT ? View.VISIBLE : View.GONE);
- if (info.rating >= 0) {
- mRatingText.setText(new DecimalFormat("#.0").format(info.rating));
- mRatingView.setRating(info.rating);
- mRatingView.setVisibility(View.VISIBLE);
- String reviewCountFormatted = NumberFormat.getInstance().format(info.reviewCount);
- mReviewCount.setText("(" + reviewCountFormatted + ")");
- } else {
- // if we don't have a rating
- mRatingView.setVisibility(View.GONE);
- mRatingText.setText("");
- mReviewCount.setText("");
- }
- }
-}
diff --git a/src/com/android/launcher3/discovery/RatingView.java b/src/com/android/launcher3/discovery/RatingView.java
deleted file mode 100644
index 8fe63d6..0000000
--- a/src/com/android/launcher3/discovery/RatingView.java
+++ /dev/null
@@ -1,94 +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.discovery;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.drawable.ClipDrawable;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-
-import com.android.launcher3.R;
-
-/**
- * A simple rating view that shows stars with a rating from 0-5.
- */
-public class RatingView extends View {
-
- private static final float WIDTH_FACTOR = 0.9f;
- private static final int MAX_LEVEL = 10000;
- private static final int MAX_STARS = 5;
-
- private final Drawable mStarDrawable;
- private final int mColorGray;
- private final int mColorHighlight;
-
- private float rating;
-
- public RatingView(Context context) {
- this(context, null);
- }
-
- public RatingView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public RatingView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mStarDrawable = getResources().getDrawable(R.drawable.ic_star_rating, null);
- mColorGray = 0x1E000000;
- mColorHighlight = 0x8A000000;
- }
-
- public void setRating(float rating) {
- this.rating = Math.min(Math.max(rating, 0), MAX_STARS);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- drawStars(canvas, MAX_STARS, mColorGray);
- drawStars(canvas, rating, mColorHighlight);
- }
-
- private void drawStars(Canvas canvas, float stars, int color) {
- int fullWidth = getLayoutParams().width;
- int cellWidth = fullWidth / MAX_STARS;
- int starWidth = (int) (cellWidth * WIDTH_FACTOR);
- int padding = cellWidth - starWidth;
- int fullStars = (int) stars;
- float partialStarFactor = stars - fullStars;
-
- for (int i = 0; i < fullStars; i++) {
- int x = i * cellWidth + padding;
- Drawable star = mStarDrawable.getConstantState().newDrawable().mutate();
- star.setTint(color);
- star.setBounds(x, padding, x + starWidth, padding + starWidth);
- star.draw(canvas);
- }
- if (partialStarFactor > 0f) {
- int x = fullStars * cellWidth + padding;
- ClipDrawable star = new ClipDrawable(mStarDrawable,
- Gravity.LEFT, ClipDrawable.HORIZONTAL);
- star.setTint(color);
- star.setLevel((int) (MAX_LEVEL * partialStarFactor));
- star.setBounds(x, padding, x + starWidth, padding + starWidth);
- star.draw(canvas);
- }
- }
-}
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
index 4629dad..9638a75 100644
--- a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -75,13 +75,14 @@
}
@Override
- public void init(Launcher launcher, boolean alreadyOnHome) {
+ public boolean init(Launcher launcher, boolean alreadyOnHome) {
AbstractFloatingView.closeAllOpenViews(launcher, alreadyOnHome);
launcher.getStateManager().goToState(NORMAL, alreadyOnHome /* animated */);
launcher.getDragLayer().setOnDragListener(this);
mLauncher = launcher;
mDragController = launcher.getDragController();
+ return false;
}
@Override
@@ -176,7 +177,4 @@
mLauncher.getDragLayer().setOnDragListener(null);
}
}
-
- @Override
- public void onLauncherResume() { }
}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 9f9822c..1cf407e 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -16,6 +16,8 @@
package com.android.launcher3.dragndrop;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
@@ -45,13 +47,11 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.uioverrides.UiFactory;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
@@ -90,9 +90,6 @@
private final ViewGroupFocusHelper mFocusIndicatorHelper;
private final PageCutOutScrimDrawable mPageCutOutScrim;
- // Handles all apps pull up interaction
- private AllAppsTransitionController mAllAppsController;
-
protected TouchController[] mControllers;
private TouchController mActiveController;
/**
@@ -113,11 +110,9 @@
mPageCutOutScrim.setCallback(this);
}
- public void setup(Launcher launcher, DragController dragController,
- AllAppsTransitionController allAppsTransitionController) {
+ public void setup(Launcher launcher, DragController dragController) {
mLauncher = launcher;
mDragController = dragController;
- mAllAppsController = allAppsTransitionController;
mControllers = UiFactory.createTouchControllers(mLauncher);
}
@@ -156,17 +151,18 @@
public boolean onInterceptTouchEvent(MotionEvent ev) {
int action = ev.getAction();
- if (action == MotionEvent.ACTION_DOWN) {
- // Cancel discovery bounce animation when a user start interacting on anywhere on
- // dray layer even if mAllAppsController is NOT the active controller.
- // TODO: handle other input other than touch
- mAllAppsController.cancelDiscoveryAnimation();
- } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
if (mTouchCompleteListener != null) {
mTouchCompleteListener.onTouchComplete();
}
mTouchCompleteListener = null;
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ mLauncher.finishAutoCancelActionMode();
}
+ return findActiveController(ev);
+ }
+
+ private boolean findActiveController(MotionEvent ev) {
mActiveController = null;
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
@@ -233,7 +229,7 @@
private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
- Utilities.sendCustomAccessibilityEvent(
+ sendCustomAccessibilityEvent(
this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
}
@@ -293,8 +289,10 @@
if (mActiveController != null) {
return mActiveController.onControllerTouchEvent(ev);
+ } else {
+ // In case no child view handled the touch event, we may not get onIntercept anymore
+ return findActiveController(ev);
}
- return false;
}
/**
@@ -373,10 +371,13 @@
}
@Override
- public void setInsets(Rect insets) {
- super.setInsets(insets);
- setBackground(insets.top == 0 ? null
- : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ ev.offsetLocation(getTranslationX(), 0);
+ try {
+ return super.dispatchTouchEvent(ev);
+ } finally {
+ ev.offsetLocation(-getTranslationX(), 0);
+ }
}
@Override
@@ -800,6 +801,6 @@
}
public interface TouchCompleteListener {
- public void onTouchComplete();
+ void onTouchComplete();
}
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7c89df3..a59b899 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -224,8 +224,10 @@
mBadge = getBadge(info, appState, outObj[0]);
mBadge.setBounds(badgeBounds);
+ LauncherIcons li = LauncherIcons.obtain(mLauncher);
Utilities.scaleRectAboutCenter(bounds,
- IconNormalizer.getInstance(mLauncher).getScale(dr, null, null, null));
+ li.getNormalizer().getScale(dr, null, null, null));
+ li.recycle();
AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
// Shrink very tiny bit so that the clip path is smaller than the original bitmap
@@ -265,7 +267,7 @@
mDrawBitmap = !(dr instanceof FolderAdaptiveIcon);
if (info.isDisabled()) {
- FastBitmapDrawable d = new FastBitmapDrawable(null);
+ FastBitmapDrawable d = new FastBitmapDrawable((Bitmap) null);
d.setIsDisabled(true);
mBaseFilter = (ColorMatrixColorFilter) d.getColorFilter();
}
@@ -367,8 +369,9 @@
return new FixedSizeEmptyDrawable(iconSize);
}
ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
- Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
-
+ LauncherIcons li = LauncherIcons.obtain(appState.getContext());
+ Bitmap badge = li.getShortcutInfoBadge(si, appState.getIconCache()).iconBitmap;
+ li.recycle();
float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
float insetFraction = (iconSize - badgeSize) / iconSize;
return new InsetDrawable(new FastBitmapDrawable(badge),
diff --git a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
index c905460..1c6f77c 100644
--- a/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
+++ b/src/com/android/launcher3/dragndrop/FolderAdaptiveIcon.java
@@ -36,10 +36,9 @@
import com.android.launcher3.R;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.folder.PreviewBackground;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Preconditions;
-import java.util.concurrent.Callable;
-
/**
* {@link AdaptiveIconDrawable} representation of a {@link FolderIcon}
*/
@@ -66,7 +65,7 @@
}
public static FolderAdaptiveIcon createFolderAdaptiveIcon(
- final Launcher launcher, final long folderId, Point dragViewSize) {
+ Launcher launcher, long folderId, Point dragViewSize) {
Preconditions.assertNonUiThread();
int margin = launcher.getResources()
.getDimensionPixelSize(R.dimen.blur_size_medium_outline);
@@ -75,21 +74,12 @@
final Bitmap badge = Bitmap.createBitmap(
dragViewSize.x - margin, dragViewSize.y - margin, Bitmap.Config.ARGB_8888);
- // The bitmap for the preview is generated larger than needed to allow for the spring effect
- float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
- final Bitmap preview = Bitmap.createBitmap(
- (int) (dragViewSize.x * sizeScaleFactor), (int) (dragViewSize.y * sizeScaleFactor),
- Bitmap.Config.ARGB_8888);
-
// Create the actual drawable on the UI thread to avoid race conditions with
// FolderIcon draw pass
try {
- return new MainThreadExecutor().submit(new Callable<FolderAdaptiveIcon>() {
- @Override
- public FolderAdaptiveIcon call() throws Exception {
- FolderIcon icon = launcher.findFolderIcon(folderId);
- return icon == null ? null : createDrawableOnUiThread(icon, badge, preview);
- }
+ return new MainThreadExecutor().submit(() -> {
+ FolderIcon icon = launcher.findFolderIcon(folderId);
+ return icon == null ? null : createDrawableOnUiThread(icon, badge, dragViewSize);
}).get();
} catch (Exception e) {
Log.e(TAG, "Unable to create folder icon", e);
@@ -101,7 +91,7 @@
* Initializes various bitmaps on the UI thread and returns the final drawable.
*/
private static FolderAdaptiveIcon createDrawableOnUiThread(FolderIcon icon,
- Bitmap badgeBitmap, Bitmap previewBitmap) {
+ Bitmap badgeBitmap, Point dragViewSize) {
Preconditions.assertUIThread();
float margin = icon.getResources().getDimension(R.dimen.blur_size_medium_outline) / 2;
@@ -115,15 +105,21 @@
icon.drawBadge(c);
// Initialize preview
- float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() /
- (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction());
- float previewShiftX = shiftFactor * previewBitmap.getWidth();
- float previewShiftY = shiftFactor * previewBitmap.getHeight();
+ final float sizeScaleFactor = 1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction();
+ final int previewWidth = (int) (dragViewSize.x * sizeScaleFactor);
+ final int previewHeight = (int) (dragViewSize.y * sizeScaleFactor);
- c.setBitmap(previewBitmap);
- c.translate(previewShiftX, previewShiftY);
- icon.getPreviewItemManager().draw(c);
- c.setBitmap(null);
+ final float shiftFactor = AdaptiveIconDrawable.getExtraInsetFraction() / sizeScaleFactor;
+ final float previewShiftX = shiftFactor * previewWidth;
+ final float previewShiftY = shiftFactor * previewHeight;
+
+ Bitmap previewBitmap = UiFactory.createFromRenderer(previewWidth, previewHeight, false,
+ (canvas) -> {
+ int count = canvas.save();
+ canvas.translate(previewShiftX, previewShiftY);
+ icon.getPreviewItemManager().draw(canvas);
+ canvas.restoreToCount(count);
+ });
// Initialize mask
Path mask = new Path();
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 2168001..993663e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -346,7 +348,7 @@
mFolderName.setHint(sDefaultFolderName.contentEquals(newTitle) ? sHintText : null);
- Utilities.sendCustomAccessibilityEvent(
+ sendCustomAccessibilityEvent(
this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_renamed, newTitle));
@@ -411,17 +413,7 @@
mInfo = info;
ArrayList<ShortcutInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
-
- ArrayList<ShortcutInfo> overflow = mContent.bindItems(children);
-
- // If our folder has too many items we prune them from the list. This is an issue
- // when upgrading from the old Folders implementation which could contain an unlimited
- // number of items.
- // TODO: Remove this, as with multi-page folders, there will never be any overflow
- for (ShortcutInfo item: overflow) {
- mInfo.remove(item, false);
- mLauncher.getModelWriter().deleteItemFromDatabase(item);
- }
+ mContent.bindItems(children);
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
if (lp == null) {
@@ -462,9 +454,8 @@
*/
@SuppressLint("InflateParams")
static Folder fromXml(Launcher launcher) {
- return (Folder) launcher.getLayoutInflater().inflate(
- FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION
- ? R.layout.user_folder : R.layout.user_folder_icon_normalized, null);
+ return (Folder) launcher.getLayoutInflater()
+ .inflate(R.layout.user_folder_icon_normalized, null);
}
private void startAnimation(final AnimatorSet a) {
@@ -531,7 +522,7 @@
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+ mLauncher.getUserEventDispatcher().resetElapsedContainerMillis("folder opened");
}
};
anim.addListener(new AnimatorListenerAdapter() {
@@ -540,7 +531,7 @@
mFolderIcon.setBackgroundVisible(false);
mFolderIcon.drawLeaveBehindIfExists();
- Utilities.sendCustomAccessibilityEvent(
+ sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
@@ -654,7 +645,7 @@
}
@Override
public void onAnimationStart(Animator animation) {
- Utilities.sendCustomAccessibilityEvent(
+ sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
@@ -709,8 +700,7 @@
final int itemType = item.itemType;
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
- !isFull());
+ itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT));
}
public void onDragEnter(DragObject d) {
@@ -925,10 +915,6 @@
return mState != STATE_ANIMATING;
}
- public boolean isFull() {
- return mContent.isFull();
- }
-
private void centerAboutIcon() {
DeviceProfile grid = mLauncher.getDeviceProfile();
@@ -944,7 +930,12 @@
int centeredTop = centerY - height / 2;
// We need to bound the folder to the currently visible workspace area
- mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
+ if (mLauncher.isInState(OVERVIEW)) {
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(mLauncher.getOverviewPanel(),
+ sTempRect);
+ } else {
+ mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
+ }
int left = Math.min(Math.max(sTempRect.left, centeredLeft),
sTempRect.right- width);
int top = Math.min(Math.max(sTempRect.top, centeredTop),
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 5983029..2de09b8 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -63,7 +63,6 @@
import com.android.launcher3.dragndrop.BaseItemDragListener;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -206,7 +205,7 @@
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
- !mFolder.isFull() && item != mInfo && !mFolder.isOpen());
+ item != mInfo && !mFolder.isOpen());
}
public boolean acceptDrop(ItemInfo dragInfo) {
@@ -499,8 +498,7 @@
// If we are animating to the accepting state, animate the badge out.
float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
mTempSpaceForBadgeOffset.set(getWidth() - mTempBounds.right, mTempBounds.top);
- IconPalette badgePalette = IconPalette.getFolderBadgePalette(getResources());
- mBadgeRenderer.draw(canvas, badgePalette, mBadgeInfo, mTempBounds,
+ mBadgeRenderer.draw(canvas, mBackground.getBadgeColor(), mTempBounds,
badgeScale, mTempSpaceForBadgeOffset);
}
}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 9e5bc4f..a468cb5 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -44,19 +44,17 @@
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";
- private static final boolean ALLOW_FOLDER_SCROLL = true;
-
private static final int REORDER_ANIMATION_DURATION = 230;
private static final int START_VIEW_REORDER_DELAY = 30;
private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
@@ -91,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);
@@ -183,21 +179,13 @@
/**
* Binds items to the layout.
- * @return list of items that could not be bound, probably because we hit the max size limit.
*/
- public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
+ public void bindItems(ArrayList<ShortcutInfo> items) {
ArrayList<View> icons = new ArrayList<>();
- ArrayList<ShortcutInfo> extra = new ArrayList<>();
-
for (ShortcutInfo item : items) {
- if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
- extra.add(item);
- } else {
- icons.add(createNewView(item));
- }
+ icons.add(createNewView(item));
}
arrangeChildren(icons, icons.size(), false);
- return extra;
}
public void allocateSpaceForRank(int rank) {
@@ -431,10 +419,6 @@
pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
}
- public boolean isFull() {
- return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
- }
-
public View getFirstItem() {
if (getChildCount() < 1) {
return null;
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index eba5d98..285aef8 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -200,6 +200,10 @@
return ColorUtils.setAlphaComponent(mBgColor, alpha);
}
+ public int getBadgeColor() {
+ return mBgColor;
+ }
+
public void drawBackground(Canvas canvas) {
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(getBgColor());
diff --git a/src/com/android/launcher3/graphics/BitmapInfo.java b/src/com/android/launcher3/graphics/BitmapInfo.java
new file mode 100644
index 0000000..ab906e2
--- /dev/null
+++ b/src/com/android/launcher3/graphics/BitmapInfo.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.graphics;
+
+import android.graphics.Bitmap;
+
+import com.android.launcher3.ItemInfoWithIcon;
+
+public class BitmapInfo {
+
+ public Bitmap icon;
+ public int color;
+
+ public void applyTo(ItemInfoWithIcon info) {
+ info.iconBitmap = icon;
+ info.iconColor = color;
+ }
+
+ public void applyTo(BitmapInfo info) {
+ info.icon = icon;
+ info.color = color;
+ }
+
+ public static BitmapInfo fromBitmap(Bitmap bitmap) {
+ BitmapInfo info = new BitmapInfo();
+ info.icon = bitmap;
+ info.color = ColorExtractor.findDominantColorByHue(bitmap);
+ return info;
+ }
+}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryUpdateState.java b/src/com/android/launcher3/graphics/BitmapRenderer.java
similarity index 81%
rename from src/com/android/launcher3/discovery/AppDiscoveryUpdateState.java
rename to src/com/android/launcher3/graphics/BitmapRenderer.java
index 0700a10..4652ded 100644
--- a/src/com/android/launcher3/discovery/AppDiscoveryUpdateState.java
+++ b/src/com/android/launcher3/graphics/BitmapRenderer.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.launcher3.graphics;
-package com.android.launcher3.discovery;
+import android.graphics.Canvas;
-public enum AppDiscoveryUpdateState {
- START, UPDATE, END
+public interface BitmapRenderer {
+
+ void render(Canvas out);
}
diff --git a/src/com/android/launcher3/graphics/ColorExtractor.java b/src/com/android/launcher3/graphics/ColorExtractor.java
new file mode 100644
index 0000000..e9d72b7
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ColorExtractor.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.util.SparseArray;
+
+/**
+ * Utility class for extracting colors from a bitmap.
+ */
+public class ColorExtractor {
+
+ public static int findDominantColorByHue(Bitmap bitmap) {
+ return findDominantColorByHue(bitmap, 20);
+ }
+
+ /**
+ * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+ * @param bitmap The bitmap to scan
+ * @param samples The approximate max number of samples to use.
+ */
+ public static int findDominantColorByHue(Bitmap bitmap, int samples) {
+ final int height = bitmap.getHeight();
+ final int width = bitmap.getWidth();
+ int sampleStride = (int) Math.sqrt((height * width) / samples);
+ if (sampleStride < 1) {
+ sampleStride = 1;
+ }
+
+ // This is an out-param, for getting the hsv values for an rgb
+ float[] hsv = new float[3];
+
+ // First get the best hue, by creating a histogram over 360 hue buckets,
+ // where each pixel contributes a score weighted by saturation, value, and alpha.
+ float[] hueScoreHistogram = new float[360];
+ float highScore = -1;
+ int bestHue = -1;
+
+ int[] pixels = new int[samples];
+ int pixelCount = 0;
+
+ for (int y = 0; y < height; y += sampleStride) {
+ for (int x = 0; x < width; x += sampleStride) {
+ int argb = bitmap.getPixel(x, y);
+ int alpha = 0xFF & (argb >> 24);
+ if (alpha < 0x80) {
+ // Drop mostly-transparent pixels.
+ continue;
+ }
+ // Remove the alpha channel.
+ int rgb = argb | 0xFF000000;
+ Color.colorToHSV(rgb, hsv);
+ // Bucket colors by the 360 integer hues.
+ int hue = (int) hsv[0];
+ if (hue < 0 || hue >= hueScoreHistogram.length) {
+ // Defensively avoid array bounds violations.
+ continue;
+ }
+ if (pixelCount < samples) {
+ pixels[pixelCount++] = rgb;
+ }
+ float score = hsv[1] * hsv[2];
+ hueScoreHistogram[hue] += score;
+ if (hueScoreHistogram[hue] > highScore) {
+ highScore = hueScoreHistogram[hue];
+ bestHue = hue;
+ }
+ }
+ }
+
+ SparseArray<Float> rgbScores = new SparseArray<>();
+ int bestColor = 0xff000000;
+ highScore = -1;
+ // Go back over the RGB colors that match the winning hue,
+ // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
+ // The highest-scoring RGB color wins.
+ for (int i = 0; i < pixelCount; i++) {
+ int rgb = pixels[i];
+ Color.colorToHSV(rgb, hsv);
+ int hue = (int) hsv[0];
+ if (hue == bestHue) {
+ float s = hsv[1];
+ float v = hsv[2];
+ int bucket = (int) (s * 100) + (int) (v * 10000);
+ // Score by cumulative saturation * value.
+ float score = s * v;
+ Float oldTotal = rgbScores.get(bucket);
+ float newTotal = oldTotal == null ? score : oldTotal + score;
+ rgbScores.put(bucket, newTotal);
+ if (newTotal > highScore) {
+ highScore = newTotal;
+ // All the colors in the winning bucket are very similar. Last in wins.
+ bestColor = rgb;
+ }
+ }
+ }
+ return bestColor;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index 355c231..6a328e9 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -31,10 +31,11 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.UiThreadHelper;
import java.nio.ByteBuffer;
@@ -77,8 +78,10 @@
/**
* Draws the {@link #mView} into the given {@param destCanvas}.
*/
- private void drawDragView(Canvas destCanvas) {
+ private void drawDragView(Canvas destCanvas, float scale) {
destCanvas.save();
+ destCanvas.scale(scale, scale);
+
if (mView instanceof BubbleTextView) {
Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
@@ -120,6 +123,7 @@
int width = mView.getWidth();
int height = mView.getHeight();
+ boolean forceSoftwareRenderer = false;
if (mView instanceof BubbleTextView) {
Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
@@ -129,20 +133,14 @@
scale = ((LauncherAppWidgetHostView) mView).getScaleToFit();
width = (int) (mView.getWidth() * scale);
height = (int) (mView.getHeight() * scale);
+
+ // Use software renderer for widgets as we know that they already work
+ forceSoftwareRenderer = true;
}
- Bitmap b = Bitmap.createBitmap(width + blurSizeOutline, height + blurSizeOutline,
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(b);
-
- canvas.save();
- canvas.scale(scale, scale);
- drawDragView(canvas);
- canvas.restore();
-
- canvas.setBitmap(null);
-
- return b;
+ final float scaleFinal = scale;
+ return UiFactory.createFromRenderer(width + blurSizeOutline, height + blurSizeOutline,
+ forceSoftwareRenderer, (c) -> drawDragView(c, scaleFinal));
}
public final void generateDragOutline(Bitmap preview) {
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 371479b..32d9e41 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -31,7 +31,7 @@
import android.util.ArrayMap;
import android.util.Log;
import com.android.launcher3.FastBitmapDrawable;
-import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
@@ -64,18 +64,18 @@
/**
* Returns a FastBitmapDrawable with the icon.
*/
- public FastBitmapDrawable newIcon(Bitmap icon, ItemInfo info) {
- return new FastBitmapDrawable(icon);
+ public FastBitmapDrawable newIcon(ItemInfoWithIcon info) {
+ return new FastBitmapDrawable(info);
}
/**
* Returns a FastBitmapDrawable with the icon.
*/
- public PreloadIconDrawable newPendingIcon(Bitmap icon, Context context) {
+ public PreloadIconDrawable newPendingIcon(ItemInfoWithIcon info, Context context) {
if (mPreloadProgressPath == null) {
mPreloadProgressPath = getPreloadProgressPath(context);
}
- return new PreloadIconDrawable(icon, mPreloadProgressPath, context);
+ return new PreloadIconDrawable(info, mPreloadProgressPath, context);
}
diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java
index bacb063..6253e18 100644
--- a/src/com/android/launcher3/graphics/GradientView.java
+++ b/src/com/android/launcher3/graphics/GradientView.java
@@ -75,7 +75,7 @@
this.mMaskHeight = Utilities.pxFromDp(ALPHA_MASK_HEIGHT_DP, dm);
this.mMaskWidth = Utilities.pxFromDp(ALPHA_MASK_WIDTH_DP, dm);
Launcher launcher = Launcher.getLauncher(context);
- this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100;
+ this.mAlphaStart = 0;
this.mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher);
mAlphaColors = getResources().getInteger(R.integer.extracted_color_gradient_alpha);
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index fdf2d67..ebfe1e7 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -33,6 +33,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.UiFactory;
/**
* Utility class to generate shadow and outline effect, which are used for click feedback
@@ -106,7 +107,11 @@
int saveCount = mCanvas.save();
mCanvas.scale(scaleX, scaleY);
mCanvas.translate(-rect.left, -rect.top);
- drawable.draw(mCanvas);
+ if (!UiFactory.USE_HARDWARE_BITMAP) {
+ // TODO: Outline generation requires alpha extraction, which is costly for
+ // hardware bitmaps. Instead use canvas layer operations once its available.
+ drawable.draw(mCanvas);
+ }
mCanvas.restoreToCount(saveCount);
mCanvas.setBitmap(null);
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 5ee6a30..bd20c87 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -37,10 +37,7 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
-import java.io.File;
-import java.io.FileOutputStream;
import java.nio.ByteBuffer;
-import java.util.Random;
public class IconNormalizer {
@@ -64,9 +61,6 @@
private static final float PIXEL_DIFF_PERCENTAGE_THRESHOLD = 0.005f;
private static final float SCALE_NOT_INITIALIZED = 0;
- private static final Object LOCK = new Object();
- private static IconNormalizer sIconNormalizer;
-
private final int mMaxSize;
private final Bitmap mBitmap;
private final Bitmap mBitmapARGB;
@@ -88,11 +82,8 @@
private final Paint mPaintIcon;
private final Canvas mCanvasARGB;
- private final File mDir;
- private int mFileId;
- private final Random mRandom;
-
- private IconNormalizer(Context context) {
+ /** package private **/
+ IconNormalizer(Context context) {
// Use twice the icon size as maximum size to avoid scaling down twice.
mMaxSize = LauncherAppState.getIDP(context).iconBitmapSize * 2;
mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
@@ -124,9 +115,6 @@
mMatrix = new Matrix();
mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
-
- mDir = context.getExternalFilesDir(null);
- mRandom = new Random();
}
/**
@@ -148,18 +136,9 @@
// Condition 2:
// Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
// should generate transparent image, if the actual icon is equivalent to the shape.
- mFileId = mRandom.nextInt();
mBitmapARGB.eraseColor(Color.TRANSPARENT);
mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon);
- if (DEBUG) {
- final File beforeFile = new File(mDir, "isShape" + mFileId + "_before.png");
- try {
- mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100,
- new FileOutputStream(beforeFile));
- } catch (Exception e) {}
- }
-
// Fit the shape within the icon's bounding box
mMatrix.reset();
mMatrix.setScale(mBounds.width(), mBounds.height());
@@ -172,24 +151,8 @@
// DST_OUT operation around the mask path outline
mCanvasARGB.drawPath(maskPath, mPaintMaskShapeOutline);
- boolean isTrans = isTransparentBitmap(mBitmapARGB);
- if (DEBUG) {
- final File afterFile = new File(mDir,
- "isShape" + mFileId + "_after_" + isTrans + ".png");
- try {
- mBitmapARGB.compress(Bitmap.CompressFormat.PNG, 100,
- new FileOutputStream(afterFile));
- } catch (Exception e) {}
- }
-
// Check if the result is almost transparent
- if (!isTrans) {
- if (DEBUG) {
- Log.d(TAG, "Not same as mask shape");
- }
- return false;
- }
- return true;
+ return isTransparentBitmap(mBitmapARGB);
}
/**
@@ -203,19 +166,13 @@
mBounds.left, mBounds.top,
w, h);
int sum = 0;
- for (int i = 0; i < w * h; i++) {
+ for (int i = w * h - 1; i >= 0; i--) {
if(Color.alpha(mPixelsARGB[i]) > MIN_VISIBLE_ALPHA) {
sum++;
}
}
float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
- boolean transparentImage = percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
- if (DEBUG) {
- Log.d(TAG,
- "Total # pixel that is different (id=" + mFileId + "):" + percentageDiffPixels
- + "=" + sum + "/" + mBounds.width() * mBounds.height());
- }
- return transparentImage;
+ return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
}
/**
@@ -416,13 +373,4 @@
last = i;
}
}
-
- public static IconNormalizer getInstance(Context context) {
- synchronized (LOCK) {
- if (sIconNormalizer == null) {
- sIconNormalizer = new IconNormalizer(context);
- }
- }
- return sIconNormalizer;
- }
}
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index 6e01ed5..9c3b77e 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -18,12 +18,7 @@
import android.app.Notification;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.graphics.ColorUtils;
import android.util.Log;
@@ -41,37 +36,10 @@
private static final float MIN_PRELOAD_COLOR_SATURATION = 0.2f;
private static final float MIN_PRELOAD_COLOR_LIGHTNESS = 0.6f;
- private static IconPalette sBadgePalette;
- private static IconPalette sFolderBadgePalette;
-
- public final int dominantColor;
- public final int backgroundColor;
- public final ColorMatrixColorFilter backgroundColorMatrixFilter;
- public final ColorMatrixColorFilter saturatedBackgroundColorMatrixFilter;
- public final int textColor;
- public final int secondaryColor;
-
- private IconPalette(int color, boolean desaturateBackground) {
- dominantColor = color;
- backgroundColor = desaturateBackground ? getMutedColor(dominantColor, 0.87f) : dominantColor;
- ColorMatrix backgroundColorMatrix = new ColorMatrix();
- Themes.setColorScaleOnMatrix(backgroundColor, backgroundColorMatrix);
- backgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
- if (!desaturateBackground) {
- saturatedBackgroundColorMatrixFilter = backgroundColorMatrixFilter;
- } else {
- // Get slightly more saturated background color.
- Themes.setColorScaleOnMatrix(getMutedColor(dominantColor, 0.54f), backgroundColorMatrix);
- saturatedBackgroundColorMatrixFilter = new ColorMatrixColorFilter(backgroundColorMatrix);
- }
- textColor = getTextColorForBackground(backgroundColor);
- secondaryColor = getLowContrastColor(backgroundColor);
- }
-
/**
* Returns a color suitable for the progress bar color of preload icon.
*/
- public int getPreloadProgressColor(Context context) {
+ public static int getPreloadProgressColor(Context context, int dominantColor) {
int result = dominantColor;
// Make sure that the dominant color has enough saturation to be visible properly.
@@ -86,37 +54,6 @@
return result;
}
- public static IconPalette fromDominantColor(int dominantColor, boolean desaturateBackground) {
- return new IconPalette(dominantColor, desaturateBackground);
- }
-
- /**
- * Returns an IconPalette based on the badge_color in colors.xml.
- * If that color is Color.TRANSPARENT, then returns null instead.
- */
- public static @Nullable IconPalette getBadgePalette(Resources resources) {
- int badgeColor = resources.getColor(R.color.badge_color);
- if (badgeColor == Color.TRANSPARENT) {
- // Colors will be extracted per app icon, so a static palette won't work.
- return null;
- }
- if (sBadgePalette == null) {
- sBadgePalette = fromDominantColor(badgeColor, false);
- }
- return sBadgePalette;
- }
-
- /**
- * Returns an IconPalette based on the folder_badge_color in colors.xml.
- */
- public static @NonNull IconPalette getFolderBadgePalette(Resources resources) {
- if (sFolderBadgePalette == null) {
- int badgeColor = resources.getColor(R.color.folder_badge_color);
- sFolderBadgePalette = fromDominantColor(badgeColor, false);
- }
- return sFolderBadgePalette;
- }
-
/**
* Resolves a color such that it has enough contrast to be used as the
* color of an icon or text on the given background color.
@@ -208,30 +145,8 @@
return ColorUtils.LABToColor(low, a, b);
}
- private static int getMutedColor(int color, float whiteScrimAlpha) {
+ public static int getMutedColor(int color, float whiteScrimAlpha) {
int whiteScrim = ColorUtils.setAlphaComponent(Color.WHITE, (int) (255 * whiteScrimAlpha));
return ColorUtils.compositeColors(whiteScrim, color);
}
-
- private static int getTextColorForBackground(int backgroundColor) {
- return getLighterOrDarkerVersionOfColor(backgroundColor, 4.5f);
- }
-
- private static int getLowContrastColor(int color) {
- return getLighterOrDarkerVersionOfColor(color, 1.5f);
- }
-
- private static int getLighterOrDarkerVersionOfColor(int color, float contrastRatio) {
- int whiteMinAlpha = ColorUtils.calculateMinimumAlpha(Color.WHITE, color, contrastRatio);
- int blackMinAlpha = ColorUtils.calculateMinimumAlpha(Color.BLACK, color, contrastRatio);
- int translucentWhiteOrBlack;
- if (whiteMinAlpha >= 0) {
- translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.WHITE, whiteMinAlpha);
- } else if (blackMinAlpha >= 0) {
- translucentWhiteOrBlack = ColorUtils.setAlphaComponent(Color.BLACK, blackMinAlpha);
- } else {
- translucentWhiteOrBlack = Color.WHITE;
- }
- return ColorUtils.compositeColors(translucentWhiteOrBlack, color);
- }
}
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 1eddb2c..34fc921 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -16,17 +16,19 @@
package com.android.launcher3.graphics;
-import android.annotation.TargetApi;
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import static com.android.launcher3.graphics.ShadowGenerator.BLUR_FACTOR;
+
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -42,44 +44,110 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.Provider;
+import com.android.launcher3.util.Themes;
/**
* Helper methods for generating various launcher icons
*/
-public class LauncherIcons {
+public class LauncherIcons implements AutoCloseable {
- private static final Rect sOldBounds = new Rect();
- private static final Canvas sCanvas = new Canvas();
+ public static final Object sPoolSync = new Object();
+ private static LauncherIcons sPool;
- static {
- sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
- Paint.FILTER_BITMAP_FLAG));
+ /**
+ * Return a new Message instance from the global pool. Allows us to
+ * avoid allocating new objects in many cases.
+ */
+ public static LauncherIcons obtain(Context context) {
+ synchronized (sPoolSync) {
+ if (sPool != null) {
+ LauncherIcons m = sPool;
+ sPool = m.next;
+ m.next = null;
+ return m;
+ }
+ }
+ return new LauncherIcons(context);
+ }
+
+ /**
+ * Recycles a LauncherIcons that may be in-use.
+ */
+ public void recycle() {
+ synchronized (sPoolSync) {
+ next = sPool;
+ sPool = this;
+ }
+ }
+
+ @Override
+ public void close() {
+ recycle();
+ }
+
+ private final Rect mOldBounds = new Rect();
+ private final Context mContext;
+ private final Canvas mCanvas;
+ private final PackageManager mPm;
+
+ private final int mFillResIconDpi;
+ private final int mIconBitmapSize;
+
+ private IconNormalizer mNormalizer;
+ private ShadowGenerator mShadowGenerator;
+
+ // sometimes we store linked lists of these things
+ private LauncherIcons next;
+
+ private LauncherIcons(Context context) {
+ mContext = context.getApplicationContext();
+ mPm = mContext.getPackageManager();
+
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
+ mFillResIconDpi = idp.fillResIconDpi;
+ mIconBitmapSize = idp.iconBitmapSize;
+
+ mCanvas = new Canvas();
+ mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
+ }
+
+ public ShadowGenerator getShadowGenerator() {
+ if (mShadowGenerator == null) {
+ mShadowGenerator = new ShadowGenerator(mContext);
+ }
+ return mShadowGenerator;
+ }
+
+ public IconNormalizer getNormalizer() {
+ if (mNormalizer == null) {
+ mNormalizer = new IconNormalizer(mContext);
+ }
+ return mNormalizer;
}
/**
* Returns a bitmap suitable for the all apps view. If the package or the resource do not
* exist, it returns null.
*/
- public static Bitmap createIconBitmap(ShortcutIconResource iconRes, Context context) {
- PackageManager packageManager = context.getPackageManager();
- // the resource
+ public BitmapInfo createIconBitmap(ShortcutIconResource iconRes) {
try {
- Resources resources = packageManager.getResourcesForApplication(iconRes.packageName);
+ Resources resources = mPm.getResourcesForApplication(iconRes.packageName);
if (resources != null) {
final int id = resources.getIdentifier(iconRes.resourceName, null, null);
// do not stamp old legacy shortcuts as the app may have already forgotten about it
- return createBadgedIconBitmap(resources.getDrawableForDensity(
- id, LauncherAppState.getIDP(context).fillResIconDpi),
+ return createBadgedIconBitmap(
+ resources.getDrawableForDensity(id, mFillResIconDpi),
Process.myUserHandle() /* only available on primary user */,
- context,
0 /* do not apply legacy treatment */);
}
} catch (Exception e) {
@@ -91,12 +159,12 @@
/**
* Returns a bitmap which is of the appropriate size to be displayed as an icon
*/
- public static Bitmap createIconBitmap(Bitmap icon, Context context) {
- final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
- if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) {
- return icon;
+ public BitmapInfo createIconBitmap(Bitmap icon) {
+ if (mIconBitmapSize == icon.getWidth() && mIconBitmapSize == icon.getHeight()) {
+ return BitmapInfo.fromBitmap(icon);
}
- return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context, 1f);
+ return BitmapInfo.fromBitmap(
+ createIconBitmap(new BitmapDrawable(mContext.getResources(), icon), 1f));
}
/**
@@ -104,237 +172,195 @@
* view or workspace. The icon is badged for {@param user}.
* The bitmap is also visually normalized with other icons.
*/
- public static Bitmap createBadgedIconBitmap(
- Drawable icon, UserHandle user, Context context, int iconAppTargetSdk) {
-
- IconNormalizer normalizer;
- float scale = 1f;
- if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
- normalizer = IconNormalizer.getInstance(context);
- if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
- boolean[] outShape = new boolean[1];
- AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
- context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
- dr.setBounds(0, 0, 1, 1);
- scale = normalizer.getScale(icon, null, dr.getIconMask(), outShape);
- if (FeatureFlags.LEGACY_ICON_TREATMENT &&
- !outShape[0]){
- Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale);
- if (wrappedIcon != icon) {
- icon = wrappedIcon;
- scale = normalizer.getScale(icon, null, null, null);
- }
- }
- } else {
- scale = normalizer.getScale(icon, null, null, null);
- }
- }
- Bitmap bitmap = createIconBitmap(icon, context, scale);
- if (FeatureFlags.ADAPTIVE_ICON_SHADOW && Utilities.ATLEAST_OREO &&
- icon instanceof AdaptiveIconDrawable) {
- bitmap = ShadowGenerator.getInstance(context).recreateIcon(bitmap);
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) {
+ float[] scale = new float[1];
+ icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale);
+ Bitmap bitmap = createIconBitmap(icon, scale[0]);
+ if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
+ mCanvas.setBitmap(bitmap);
+ getShadowGenerator().recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
+ mCanvas.setBitmap(null);
}
+ final Bitmap result;
if (user != null && !Process.myUserHandle().equals(user)) {
BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
- Drawable badged = context.getPackageManager().getUserBadgedIcon(
- drawable, user);
+ Drawable badged = mPm.getUserBadgedIcon(drawable, user);
if (badged instanceof BitmapDrawable) {
- return ((BitmapDrawable) badged).getBitmap();
+ result = ((BitmapDrawable) badged).getBitmap();
} else {
- return createIconBitmap(badged, context, 1f);
+ result = createIconBitmap(badged, 1f);
}
} else {
- return bitmap;
+ result = bitmap;
}
+ return BitmapInfo.fromBitmap(result);
}
/**
* Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
* normalized with other icons and has enough spacing to add shadow.
*/
- public static Bitmap createScaledBitmapWithoutShadow(
- Drawable icon, Context context, int iconAppTargetSdk) {
+ public Bitmap createScaledBitmapWithoutShadow(Drawable icon, int iconAppTargetSdk) {
RectF iconBounds = new RectF();
- IconNormalizer normalizer;
- float scale = 1f;
- if (!FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION) {
- normalizer = IconNormalizer.getInstance(context);
- if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
- boolean[] outShape = new boolean[1];
- AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
- context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
- dr.setBounds(0, 0, 1, 1);
- scale = normalizer.getScale(icon, iconBounds, dr.getIconMask(), outShape);
- if (Utilities.ATLEAST_OREO && FeatureFlags.LEGACY_ICON_TREATMENT &&
- !outShape[0]) {
- Drawable wrappedIcon = wrapToAdaptiveIconDrawable(context, icon, scale);
- if (wrappedIcon != icon) {
- icon = wrappedIcon;
- scale = normalizer.getScale(icon, iconBounds, null, null);
- }
- }
- } else {
- scale = normalizer.getScale(icon, iconBounds, null, null);
- }
-
- }
- scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds));
- return createIconBitmap(icon, context, scale);
+ float[] scale = new float[1];
+ icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, iconBounds, scale);
+ return createIconBitmap(icon,
+ Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
}
- public static Bitmap badgeWithDrawable(Bitmap srcTgt, Drawable badge, Context context) {
- int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
- synchronized (sCanvas) {
- sCanvas.setBitmap(srcTgt);
- int iconSize = srcTgt.getWidth();
- badge.setBounds(iconSize - badgeSize, iconSize - badgeSize, iconSize, iconSize);
- badge.draw(sCanvas);
- sCanvas.setBitmap(null);
+ private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
+ RectF outIconBounds, float[] outScale) {
+ float scale = 1f;
+ if (Utilities.ATLEAST_OREO && iconAppTargetSdk >= Build.VERSION_CODES.O) {
+ boolean[] outShape = new boolean[1];
+ AdaptiveIconDrawable dr = (AdaptiveIconDrawable)
+ mContext.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
+ dr.setBounds(0, 0, 1, 1);
+ scale = getNormalizer().getScale(icon, outIconBounds, dr.getIconMask(), outShape);
+ if (Utilities.ATLEAST_OREO && !outShape[0] && !(icon instanceof AdaptiveIconDrawable)) {
+ FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
+ fsd.setDrawable(icon);
+ fsd.setScale(scale);
+ icon = dr;
+ scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+ }
+ } else {
+ scale = getNormalizer().getScale(icon, outIconBounds, null, null);
}
- return srcTgt;
+
+ outScale[0] = scale;
+ return icon;
+ }
+
+ /**
+ * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+ */
+ public void badgeWithDrawable(Bitmap target, Drawable badge) {
+ mCanvas.setBitmap(target);
+ badgeWithDrawable(mCanvas, badge);
+ mCanvas.setBitmap(null);
+ }
+
+ /**
+ * Adds the {@param badge} on top of {@param target} using the badge dimensions.
+ */
+ private void badgeWithDrawable(Canvas target, Drawable badge) {
+ int badgeSize = mContext.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+ badge.setBounds(mIconBitmapSize - badgeSize, mIconBitmapSize - badgeSize,
+ mIconBitmapSize, mIconBitmapSize);
+ badge.draw(target);
}
/**
* @param scale the scale to apply before drawing {@param icon} on the canvas
*/
- private static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
- synchronized (sCanvas) {
- final int iconBitmapSize = LauncherAppState.getIDP(context).iconBitmapSize;
- int width = iconBitmapSize;
- int height = iconBitmapSize;
+ private Bitmap createIconBitmap(Drawable icon, float scale) {
+ int width = mIconBitmapSize;
+ int height = mIconBitmapSize;
- if (icon instanceof PaintDrawable) {
- PaintDrawable painter = (PaintDrawable) icon;
- painter.setIntrinsicWidth(width);
- painter.setIntrinsicHeight(height);
- } else if (icon instanceof BitmapDrawable) {
- // Ensure the bitmap has a density.
- BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
- bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
- }
+ if (icon instanceof PaintDrawable) {
+ PaintDrawable painter = (PaintDrawable) icon;
+ painter.setIntrinsicWidth(width);
+ painter.setIntrinsicHeight(height);
+ } else if (icon instanceof BitmapDrawable) {
+ // Ensure the bitmap has a density.
+ BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
+ Bitmap bitmap = bitmapDrawable.getBitmap();
+ if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
+ bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
}
-
- int sourceWidth = icon.getIntrinsicWidth();
- int sourceHeight = icon.getIntrinsicHeight();
- if (sourceWidth > 0 && sourceHeight > 0) {
- // Scale the icon proportionally to the icon dimensions
- final float ratio = (float) sourceWidth / sourceHeight;
- if (sourceWidth > sourceHeight) {
- height = (int) (width / ratio);
- } else if (sourceHeight > sourceWidth) {
- width = (int) (height * ratio);
- }
- }
- // no intrinsic size --> use default size
- int textureWidth = iconBitmapSize;
- int textureHeight = iconBitmapSize;
-
- Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
- Bitmap.Config.ARGB_8888);
- final Canvas canvas = sCanvas;
- canvas.setBitmap(bitmap);
-
- final int left = (textureWidth-width) / 2;
- final int top = (textureHeight-height) / 2;
-
- sOldBounds.set(icon.getBounds());
- if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
- int offset = Math.max((int)(ShadowGenerator.BLUR_FACTOR * iconBitmapSize),
- Math.min(left, top));
- int size = Math.max(width, height);
- icon.setBounds(offset, offset, size, size);
- } else {
- icon.setBounds(left, top, left+width, top+height);
- }
- canvas.save(Canvas.MATRIX_SAVE_FLAG);
- canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
- icon.draw(canvas);
- canvas.restore();
- icon.setBounds(sOldBounds);
- canvas.setBitmap(null);
-
- return bitmap;
- }
- }
-
- /**
- * If the platform is running O but the app is not providing AdaptiveIconDrawable, then
- * shrink the legacy icon and set it as foreground. Use color drawable as background to
- * create AdaptiveIconDrawable.
- */
- @TargetApi(Build.VERSION_CODES.O)
- private static Drawable wrapToAdaptiveIconDrawable(
- Context context, Drawable drawable, float scale) {
- if (!(FeatureFlags.LEGACY_ICON_TREATMENT && Utilities.ATLEAST_OREO)) {
- return drawable;
}
- try {
- if (!(drawable instanceof AdaptiveIconDrawable)) {
- AdaptiveIconDrawable iconWrapper = (AdaptiveIconDrawable)
- context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper).mutate();
- FixedScaleDrawable fsd = ((FixedScaleDrawable) iconWrapper.getForeground());
- fsd.setDrawable(drawable);
- fsd.setScale(scale);
- return iconWrapper;
+ int sourceWidth = icon.getIntrinsicWidth();
+ int sourceHeight = icon.getIntrinsicHeight();
+ if (sourceWidth > 0 && sourceHeight > 0) {
+ // Scale the icon proportionally to the icon dimensions
+ final float ratio = (float) sourceWidth / sourceHeight;
+ if (sourceWidth > sourceHeight) {
+ height = (int) (width / ratio);
+ } else if (sourceHeight > sourceWidth) {
+ width = (int) (height * ratio);
}
- } catch (Exception e) {
- return drawable;
}
- return drawable;
+ // no intrinsic size --> use default size
+ int textureWidth = mIconBitmapSize;
+ int textureHeight = mIconBitmapSize;
+
+ Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
+ Bitmap.Config.ARGB_8888);
+ mCanvas.setBitmap(bitmap);
+
+ final int left = (textureWidth-width) / 2;
+ final int top = (textureHeight-height) / 2;
+
+ mOldBounds.set(icon.getBounds());
+ if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
+ int offset = Math.max((int)(BLUR_FACTOR * textureWidth), Math.min(left, top));
+ int size = Math.max(width, height);
+ icon.setBounds(offset, offset, size, size);
+ } else {
+ icon.setBounds(left, top, left+width, top+height);
+ }
+ mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
+ mCanvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
+ icon.draw(mCanvas);
+ mCanvas.restore();
+ icon.setBounds(mOldBounds);
+ mCanvas.setBitmap(null);
+
+ return bitmap;
}
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context) {
- return createShortcutIcon(shortcutInfo, context, true /* badged */);
+ public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo) {
+ return createShortcutIcon(shortcutInfo, true /* badged */);
}
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
- boolean badged) {
- return createShortcutIcon(shortcutInfo, context, badged, null);
+ public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo, boolean badged) {
+ return createShortcutIcon(shortcutInfo, badged, null);
}
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
- final Bitmap fallbackIcon) {
- // If the shortcut is pinned but no longer has an icon in the system,
- // keep the current icon instead of reverting to the default icon.
- return createShortcutIcon(shortcutInfo, context, true, Provider.of(fallbackIcon));
- }
-
- public static Bitmap createShortcutIcon(ShortcutInfoCompat shortcutInfo, Context context,
+ public BitmapInfo createShortcutIcon(ShortcutInfoCompat shortcutInfo,
boolean badged, @Nullable Provider<Bitmap> fallbackIconProvider) {
- LauncherAppState app = LauncherAppState.getInstance(context);
- Drawable unbadgedDrawable = DeepShortcutManager.getInstance(context)
- .getShortcutIconDrawable(shortcutInfo,
- app.getInvariantDeviceProfile().fillResIconDpi);
- IconCache cache = app.getIconCache();
+ Drawable unbadgedDrawable = DeepShortcutManager.getInstance(mContext)
+ .getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
+ IconCache cache = LauncherAppState.getInstance(mContext).getIconCache();
+
Bitmap unbadgedBitmap = null;
if (unbadgedDrawable != null) {
- unbadgedBitmap = LauncherIcons.createScaledBitmapWithoutShadow(
- unbadgedDrawable, context, 0);
+ unbadgedBitmap = createScaledBitmapWithoutShadow(unbadgedDrawable, 0);
} else {
if (fallbackIconProvider != null) {
unbadgedBitmap = fallbackIconProvider.get();
}
if (unbadgedBitmap == null) {
- unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle());
+ unbadgedBitmap = cache.getDefaultIcon(Process.myUserHandle()).icon;
}
}
+ BitmapInfo result = new BitmapInfo();
if (!badged) {
- return unbadgedBitmap;
+ result.color = Themes.getColorAccent(mContext);
+ result.icon = unbadgedBitmap;
+ return result;
}
- unbadgedBitmap = ShadowGenerator.getInstance(context).recreateIcon(unbadgedBitmap);
- return badgeWithDrawable(unbadgedBitmap,
- new FastBitmapDrawable(getShortcutInfoBadge(shortcutInfo, cache)), context);
+
+ final Bitmap unbadgedfinal = unbadgedBitmap;
+ final ItemInfoWithIcon badge = getShortcutInfoBadge(shortcutInfo, cache);
+
+ result.color = badge.iconColor;
+ result.icon = UiFactory.createFromRenderer(mIconBitmapSize, mIconBitmapSize, false, (c) -> {
+ getShadowGenerator().recreateIcon(unbadgedfinal, c);
+ badgeWithDrawable(c, new FastBitmapDrawable(badge));
+ });
+ return result;
}
- public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
- final Bitmap badgeBitmap;
+ public ItemInfoWithIcon getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
ComponentName cn = shortcutInfo.getActivity();
- if (cn != null) {
+ String badgePkg = shortcutInfo.getBadgePackage(mContext);
+ boolean hasBadgePkgSet = !badgePkg.equals(shortcutInfo.getPackage());
+ if (cn != null && !hasBadgePkgSet) {
// Get the app info for the source activity.
AppInfo appInfo = new AppInfo();
appInfo.user = shortcutInfo.getUserHandle();
@@ -343,13 +369,12 @@
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(cn);
cache.getTitleAndIcon(appInfo, false);
- badgeBitmap = appInfo.iconBitmap;
+ return appInfo;
} else {
- PackageItemInfo pkgInfo = new PackageItemInfo(shortcutInfo.getPackage());
+ PackageItemInfo pkgInfo = new PackageItemInfo(badgePkg);
cache.getTitleAndIconForApp(pkgInfo, false);
- badgeBitmap = pkgInfo.iconBitmap;
+ return pkgInfo;
}
- return badgeBitmap;
}
/**
diff --git a/src/com/android/launcher3/graphics/NinePatchDrawHelper.java b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
new file mode 100644
index 0000000..fc20926
--- /dev/null
+++ b/src/com/android/launcher3/graphics/NinePatchDrawHelper.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+/**
+ * Utility class which draws a bitmap by dissecting it into 3 segments and stretching
+ * the middle segment.
+ */
+public class NinePatchDrawHelper {
+
+ // The extra width used for the bitmap. This portion of the bitmap is stretched to match the
+ // width of the draw region. Randomly chosen, any value > 4 will be sufficient.
+ public static final int EXTENSION_PX = 20;
+
+ private final Rect mSrc = new Rect();
+ private final RectF mDst = new RectF();
+ public final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ /**
+ * Draws the bitmap split into three parts horizontally, with the middle part having width
+ * as {@link #EXTENSION_PX} in the center of the bitmap.
+ */
+ public void draw(Bitmap bitmap, Canvas canvas, float left, float top, float right) {
+ int height = bitmap.getHeight();
+
+ mSrc.top = 0;
+ mSrc.bottom = height;
+ mDst.top = top;
+ mDst.bottom = top + height;
+ draw3Patch(bitmap, canvas, left, right);
+ }
+
+
+ /**
+ * Draws the bitmap split horizontally into 3 parts (same as {@link #draw}) and split
+ * vertically into two parts, bottom part of size {@link #EXTENSION_PX} / 2 which is
+ * stretched vertically.
+ */
+ public void drawVerticallyStretched(Bitmap bitmap, Canvas canvas, float left, float top,
+ float right, float bottom) {
+ draw(bitmap, canvas, left, top, right);
+
+ // Draw bottom stretched region.
+ int height = bitmap.getHeight();
+ mSrc.top = height - EXTENSION_PX / 4;
+ mSrc.bottom = height;
+ mDst.top = top + height;
+ mDst.bottom = bottom;
+ draw3Patch(bitmap, canvas, left, right);
+ }
+
+
+
+ private void draw3Patch(Bitmap bitmap, Canvas canvas, float left, float right) {
+ int width = bitmap.getWidth();
+ int halfWidth = width / 2;
+
+ // Draw left edge
+ drawRegion(bitmap, canvas, 0, halfWidth, left, left + halfWidth);
+
+ // Draw right edge
+ drawRegion(bitmap, canvas, halfWidth, width, right - halfWidth, right);
+
+ // Draw middle stretched region
+ int halfExt = EXTENSION_PX / 4;
+ drawRegion(bitmap, canvas, halfWidth - halfExt, halfWidth + halfExt,
+ left + halfWidth, right - halfWidth);
+ }
+
+ private void drawRegion(Bitmap bitmap, Canvas c,
+ int srcLeft, int srcRight, float dstLeft, float dstRight) {
+ mSrc.left = srcLeft;
+ mSrc.right = srcRight;
+
+ mDst.left = dstLeft;
+ mDst.right = dstRight;
+ c.drawBitmap(bitmap, mSrc, mDst, paint);
+ }
+}
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 6d486ee..a40b6df 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -32,6 +32,7 @@
import android.util.SparseArray;
import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.anim.Interpolators;
import java.lang.ref.WeakReference;
@@ -86,7 +87,7 @@
private final Paint mProgressPaint;
private Bitmap mShadowBitmap;
- private int mIndicatorColor = 0;
+ private final int mIndicatorColor;
private int mTrackAlpha;
private float mTrackLength;
@@ -103,8 +104,8 @@
/**
* @param progressPath fixed path in the bounds [0, 0, 100, 100] representing a progress bar.
*/
- public PreloadIconDrawable(Bitmap b, Path progressPath, Context context) {
- super(b);
+ public PreloadIconDrawable(ItemInfoWithIcon info, Path progressPath, Context context) {
+ super(info);
mContext = context;
mProgressPath = progressPath;
mScaledTrackPath = new Path();
@@ -113,6 +114,7 @@
mProgressPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
+ mIndicatorColor = IconPalette.getPreloadProgressColor(context, mIconColor);
setInternalProgress(0);
}
@@ -266,9 +268,6 @@
mScaledTrackPath.reset();
mTrackAlpha = MAX_PAINT_ALPHA;
setIsDisabled(true);
- } else if (mIndicatorColor == 0) {
- // Update the indicator color
- mIndicatorColor = getIconPalette().getPreloadProgressColor(mContext);
}
if (progress < 1 && progress > 0) {
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 60eeef5..5fbf502 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -24,6 +24,8 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.v4.graphics.ColorUtils;
@@ -44,67 +46,40 @@
private static final int AMBIENT_SHADOW_ALPHA = 30;
- private static final Object LOCK = new Object();
- // Singleton object guarded by {@link #LOCK}
- private static ShadowGenerator sShadowGenerator;
-
private final int mIconSize;
- private final Canvas mCanvas;
private final Paint mBlurPaint;
private final Paint mDrawPaint;
private final BlurMaskFilter mDefaultBlurMaskFilter;
- private ShadowGenerator(Context context) {
+ public ShadowGenerator(Context context) {
mIconSize = LauncherAppState.getIDP(context).iconBitmapSize;
- mCanvas = new Canvas();
mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mDefaultBlurMaskFilter = new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL);
}
- public synchronized Bitmap recreateIcon(Bitmap icon) {
- return recreateIcon(icon, true, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA,
- KEY_SHADOW_ALPHA);
+ public synchronized void recreateIcon(Bitmap icon, Canvas out) {
+ recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out);
}
- public synchronized Bitmap recreateIcon(Bitmap icon, boolean resize,
- BlurMaskFilter blurMaskFilter, int ambientAlpha, int keyAlpha) {
- int width = resize ? mIconSize : icon.getWidth();
- int height = resize ? mIconSize : icon.getHeight();
+ public synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter,
+ int ambientAlpha, int keyAlpha, Canvas out) {
int[] offset = new int[2];
-
mBlurPaint.setMaskFilter(blurMaskFilter);
Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
- Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
- mCanvas.setBitmap(result);
// Draw ambient shadow
mDrawPaint.setAlpha(ambientAlpha);
- mCanvas.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
+ out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
// Draw key shadow
mDrawPaint.setAlpha(keyAlpha);
- mCanvas.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
+ out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
// Draw the icon
mDrawPaint.setAlpha(255);
- mCanvas.drawBitmap(icon, 0, 0, mDrawPaint);
-
- mCanvas.setBitmap(null);
- return result;
- }
-
- public static ShadowGenerator getInstance(Context context) {
- // TODO: This currently fails as the system default icon also needs a shadow as it
- // uses adaptive icon.
- // Preconditions.assertNonUiThread();
- synchronized (LOCK) {
- if (sShadowGenerator == null) {
- sShadowGenerator = new ShadowGenerator(context);
- }
- }
- return sShadowGenerator;
+ out.drawBitmap(icon, 0, 0, mDrawPaint);
}
/**
@@ -178,6 +153,18 @@
p.setShadowLayer(shadowBlur, 0, 0,
ColorUtils.setAlphaComponent(Color.BLACK, ambientShadowAlpha));
c.drawRoundRect(bounds, radius, radius, p);
+
+ if (Color.alpha(color) < 255) {
+ // Clear any content inside the pill-rect for translucent fill.
+ p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+ p.clearShadowLayer();
+ p.setColor(Color.BLACK);
+ c.drawRoundRect(bounds, radius, radius, p);
+
+ p.setXfermode(null);
+ p.setColor(color);
+ c.drawRoundRect(bounds, radius, radius, p);
+ }
}
}
}
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/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 816c1d4..8640401 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -118,9 +118,9 @@
deepShortcutMap.clear();
}
- public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
- String[] args) {
- if (args.length > 0 && TextUtils.equals(args[0], "--proto")) {
+ public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+ if (Arrays.asList(args).contains("--proto")) {
dumpProto(prefix, fd, writer, args);
return;
}
@@ -219,7 +219,7 @@
targetList.addAll(workspaces.valueAt(i).getFlattenedList());
}
- if (args.length > 1 && TextUtils.equals(args[1], "--debug")) {
+ if (Arrays.asList(args).contains("--debug")) {
for (int i = 0; i < targetList.size(); i++) {
writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index ccef9b7..6378ea1 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -44,6 +44,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.ContentWriter;
@@ -151,10 +152,9 @@
info.user = user;
info.itemType = itemType;
info.title = getTitle();
- info.iconBitmap = loadIcon(info);
// the fallback icon
- if (info.iconBitmap == null) {
- info.iconBitmap = mIconCache.getDefaultIcon(info.user);
+ if (!loadIcon(info)) {
+ mIconCache.getDefaultIcon(info.user).applyTo(info);
}
// TODO: If there's an explicit component and we can't install that, delete it.
@@ -165,8 +165,7 @@
/**
* Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
*/
- protected Bitmap loadIcon(ShortcutInfo info) {
- Bitmap icon = null;
+ protected boolean loadIcon(ShortcutInfo info) {
if (itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
String packageName = getString(iconPackageIndex);
String resourceName = getString(iconResourceIndex);
@@ -174,24 +173,25 @@
info.iconResource = new ShortcutIconResource();
info.iconResource.packageName = packageName;
info.iconResource.resourceName = resourceName;
- icon = LauncherIcons.createIconBitmap(info.iconResource, mContext);
+ LauncherIcons li = LauncherIcons.obtain(mContext);
+ BitmapInfo iconInfo = li.createIconBitmap(info.iconResource);
+ li.recycle();
+ if (iconInfo != null) {
+ iconInfo.applyTo(info);
+ return true;
+ }
}
}
- if (icon == null) {
- // Failed to load from resource, try loading from DB.
- byte[] data = getBlob(iconIndex);
- try {
- icon = LauncherIcons.createIconBitmap(
- BitmapFactory.decodeByteArray(data, 0, data.length), mContext);
- } catch (Exception e) {
- Log.e(TAG, "Failed to load icon for info " + info, e);
- return null;
- }
+
+ // Failed to load from resource, try loading from DB.
+ byte[] data = getBlob(iconIndex);
+ try (LauncherIcons li = LauncherIcons.obtain(mContext)) {
+ li.createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length)).applyTo(info);
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to load icon for info " + info, e);
+ return false;
}
- if (icon == null) {
- Log.e(TAG, "Failed to load icon for info " + info);
- }
- return icon;
}
/**
@@ -211,9 +211,8 @@
info.user = user;
info.intent = intent;
- info.iconBitmap = loadIcon(info);
// the fallback icon
- if (info.iconBitmap == null) {
+ if (!loadIcon(info)) {
mIconCache.getTitleAndIcon(info, false /* useLowResIcon */);
}
@@ -269,8 +268,7 @@
mIconCache.getTitleAndIcon(info, lai, useLowResIcon);
if (mIconCache.isDefaultIcon(info.iconBitmap, user)) {
- Bitmap icon = loadIcon(info);
- info.iconBitmap = icon != null ? icon : info.iconBitmap;
+ loadIcon(info);
}
if (lai != null) {
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/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 310416f..00dd3aa 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -54,7 +54,6 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIconPreviewVerifier;
-import com.android.launcher3.graphics.IconNormalizer;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.provider.ImportDataTask;
@@ -215,9 +214,10 @@
public void loadUiResources() {
if (Utilities.ATLEAST_OREO) {
- ClickShadowView.setAdaptiveIconScaleFactor(
- IconNormalizer.getInstance(mApp.getContext()).getScale(
- new AdaptiveIconDrawable(null, null), null, null, null));
+ LauncherIcons li = LauncherIcons.obtain(mApp.getContext());
+ ClickShadowView.setAdaptiveIconScaleFactor(li.getNormalizer()
+ .getScale(new AdaptiveIconDrawable(null, null), null, null, null));
+ li.recycle();
}
}
@@ -472,12 +472,14 @@
public Bitmap get() {
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
- return c.loadIcon(finalInfo);
+ return c.loadIcon(finalInfo)
+ ? finalInfo.iconBitmap : null;
}
};
- info.iconBitmap = LauncherIcons
- .createShortcutIcon(pinnedShortcut, context,
- true /* badged */, fallbackIconProvider);
+ LauncherIcons li = LauncherIcons.obtain(context);
+ li.createShortcutIcon(pinnedShortcut,
+ true /* badged */, fallbackIconProvider).applyTo(info);
+ li.recycle();
if (pmHelper.isAppSuspended(
pinnedShortcut.getPackage(), info.user)) {
info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
diff --git a/src/com/android/launcher3/model/ModelPreload.java b/src/com/android/launcher3/model/ModelPreload.java
new file mode 100644
index 0000000..f186e95
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelPreload.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import android.content.Context;
+import android.support.annotation.WorkerThread;
+import android.util.Log;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.ModelUpdateTask;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Utility class to preload LauncherModel
+ */
+public class ModelPreload implements ModelUpdateTask {
+
+ private static final String TAG = "ModelPreload";
+
+ private LauncherAppState mApp;
+ private LauncherModel mModel;
+ private BgDataModel mBgDataModel;
+ private AllAppsList mAllAppsList;
+
+ @Override
+ public final void init(LauncherAppState app, LauncherModel model, BgDataModel dataModel,
+ AllAppsList allAppsList, Executor uiExecutor) {
+ mApp = app;
+ mModel = model;
+ mBgDataModel = dataModel;
+ mAllAppsList = allAppsList;
+ }
+
+ @Override
+ public final void run() {
+ mModel.startLoaderForResultsIfNotLoaded(
+ new LoaderResults(mApp, mBgDataModel, mAllAppsList, 0, null));
+ Log.d(TAG, "Preload completed : " + mModel.isModelLoaded());
+ onComplete(mModel.isModelLoaded());
+ }
+
+ /**
+ * Called when the task is complete
+ */
+ @WorkerThread
+ public void onComplete(boolean isSuccess) { }
+
+ public void start(Context context) {
+ LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(this);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 470dadf..089303e 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -40,6 +40,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -191,9 +192,11 @@
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
&& packageSet.contains(si.iconResource.packageName)) {
- Bitmap icon = LauncherIcons.createIconBitmap(si.iconResource, context);
- if (icon != null) {
- si.iconBitmap = icon;
+ LauncherIcons li = LauncherIcons.obtain(context);
+ BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
+ li.recycle();
+ if (iconInfo != null) {
+ iconInfo.applyTo(si);
infoUpdated = true;
}
}
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index c1f33a6..59f3d1c 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -29,6 +29,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.HashSet;
@@ -92,8 +93,12 @@
}
for (final ShortcutInfo shortcutInfo : shortcutInfos) {
shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
- shortcutInfo.iconBitmap = LauncherIcons.createShortcutIcon(fullDetails, context,
- shortcutInfo.iconBitmap);
+ // If the shortcut is pinned but no longer has an icon in the system,
+ // keep the current icon instead of reverting to the default icon.
+ LauncherIcons li = LauncherIcons.obtain(context);
+ li.createShortcutIcon(fullDetails, true, Provider.of(shortcutInfo.iconBitmap))
+ .applyTo(shortcutInfo);
+ li.recycle();
updatedShortcutInfos.add(shortcutInfo);
}
}
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 2e9ac72..9521a9e 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -32,6 +32,7 @@
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.Provider;
import java.util.ArrayList;
import java.util.HashMap;
@@ -91,8 +92,11 @@
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
- si.iconBitmap = LauncherIcons.createShortcutIcon(shortcut, context,
- si.iconBitmap);
+ // If the shortcut is pinned but no longer has an icon in the system,
+ // keep the current icon instead of reverting to the default icon.
+ LauncherIcons li = LauncherIcons.obtain(context);
+ li.createShortcutIcon(shortcut, true, Provider.of(si.iconBitmap)).applyTo(si);
+ li.recycle();
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 120de04..6918935 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -83,7 +83,7 @@
if (mIconDrawable == null) {
mIconDrawable = new BitmapDrawable(context.getResources(), LauncherAppState
.getInstance(context).getIconCache()
- .getDefaultIcon(statusBarNotification.getUser()));
+ .getDefaultIcon(statusBarNotification.getUser()).icon);
mBadgeIcon = Notification.BADGE_ICON_NONE;
}
intent = notification.contentIntent;
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 5bbd19c..2fefa85 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.content.Context;
+import android.graphics.Color;
import android.graphics.Rect;
import android.support.annotation.Nullable;
import android.view.MotionEvent;
@@ -114,12 +115,12 @@
}
}
- public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
+ public void updateHeader(int notificationCount, int iconColor) {
mHeaderCount.setText(notificationCount <= 1 ? "" : String.valueOf(notificationCount));
- if (palette != null) {
+ if (Color.alpha(iconColor) > 0) {
if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
mNotificationHeaderTextColor =
- IconPalette.resolveContrastColor(mContext, palette.dominantColor,
+ IconPalette.resolveContrastColor(mContext, iconColor,
Themes.getAttrColor(mContext, R.attr.popupColorPrimary));
}
mHeaderText.setTextColor(mNotificationHeaderTextColor);
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 7b70df7..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,
@@ -62,6 +62,7 @@
private static NotificationListener sNotificationListenerInstance = null;
private static NotificationsChangedListener sNotificationsChangedListener;
+ private static StatusBarNotificationsChangedListener sStatusBarNotificationsChangedListener;
private static boolean sIsConnected;
private static boolean sIsCreated;
@@ -70,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;
@@ -180,10 +183,19 @@
}
}
+ public static void setStatusBarNotificationsChangedListener
+ (StatusBarNotificationsChangedListener listener) {
+ sStatusBarNotificationsChangedListener = listener;
+ }
+
public static void removeNotificationsChangedListener() {
sNotificationsChangedListener = null;
}
+ public static void removeStatusBarNotificationsChangedListener() {
+ sStatusBarNotificationsChangedListener = null;
+ }
+
@Override
public void onListenerConnected() {
super.onListenerConnected();
@@ -205,7 +217,10 @@
public void onNotificationPosted(final StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, new NotificationPostedMsg(sbn))
- .sendToTarget();
+ .sendToTarget();
+ if (sStatusBarNotificationsChangedListener != null) {
+ sStatusBarNotificationsChangedListener.onNotificationPosted(sbn);
+ }
}
/**
@@ -227,10 +242,13 @@
public void onNotificationRemoved(final StatusBarNotification sbn) {
super.onNotificationRemoved(sbn);
Pair<PackageUserKey, NotificationKeyData> packageUserKeyAndNotificationKey
- = new Pair<>(PackageUserKey.fromNotification(sbn),
- NotificationKeyData.fromNotification(sbn));
+ = new Pair<>(PackageUserKey.fromNotification(sbn),
+ NotificationKeyData.fromNotification(sbn));
mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
- .sendToTarget();
+ .sendToTarget();
+ if (sStatusBarNotificationsChangedListener != null) {
+ sStatusBarNotificationsChangedListener.onNotificationRemoved(sbn);
+ }
NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
if (notificationGroup != null) {
@@ -242,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
@@ -279,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()) {
@@ -308,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);
}
@@ -318,4 +366,9 @@
NotificationKeyData notificationKey);
void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications);
}
+
+ public interface StatusBarNotificationsChangedListener {
+ void onNotificationPosted(StatusBarNotification sbn);
+ void onNotificationRemoved(StatusBarNotification sbn);
+ }
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
index be6bcc5..3ce7291 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -15,42 +15,16 @@
*/
package com.android.launcher3.pageindicators;
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
/**
* Base class for a page indicator.
*/
-public abstract class PageIndicator extends FrameLayout {
+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);
- setWillNotDraw(false);
- }
+ 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/PageIndicatorLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java
deleted file mode 100644
index 7325235..0000000
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLandscape.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.pageindicators;
-
-import android.content.Context;
-import android.util.AttributeSet;
-
-import com.android.launcher3.Launcher;
-
-/**
- * Simply draws the caret drawable bottom-right aligned in the view. This ensures that we can have
- * a view with as large an area as we want (for touching) while maintaining a caret of size
- * all_apps_caret_size. Used only for the landscape layout.
- */
-public class PageIndicatorLandscape extends PageIndicator {
- // all apps pull up handle drawable.
-
- public PageIndicatorLandscape(Context context) {
- this(context, null);
- }
-
- public PageIndicatorLandscape(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PageIndicatorLandscape(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- Launcher l = Launcher.getLauncher(context);
- setOnClickListener(l);
- setOnFocusChangeListener(l.mFocusHandler);
- }
-}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java b/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
deleted file mode 100644
index 09a06b0..0000000
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLine.java
+++ /dev/null
@@ -1,253 +0,0 @@
-package com.android.launcher3.pageindicators;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.AttributeSet;
-import android.util.Property;
-import android.view.ViewConfiguration;
-import android.widget.ImageView;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.dynamicui.WallpaperColorInfo;
-
-/**
- * A PageIndicator that briefly shows a fraction of a line when moving between pages.
- *
- * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
- */
-public class PageIndicatorLine extends PageIndicator {
-
- private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
- private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
- public static final int WHITE_ALPHA = (int) (0.70f * 255);
- public static final int BLACK_ALPHA = (int) (0.65f * 255);
-
- private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
- private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
- private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
-
- private ValueAnimator[] mAnimators = new ValueAnimator[3];
-
- private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
-
- private boolean mShouldAutoHide = true;
-
- // The alpha of the line when it is showing.
- private int mActiveAlpha = 0;
- // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
- private int mToAlpha;
- // A float value representing the number of pages, to allow for an animation when it changes.
- private float mNumPagesFloat;
- private int mCurrentScroll;
- private int mTotalScroll;
- private Paint mLinePaint;
- private Launcher mLauncher;
- private final int mLineHeight;
- private ImageView mAllAppsHandle;
-
- private static final Property<PageIndicatorLine, Integer> PAINT_ALPHA
- = new Property<PageIndicatorLine, Integer>(Integer.class, "paint_alpha") {
- @Override
- public Integer get(PageIndicatorLine obj) {
- return obj.mLinePaint.getAlpha();
- }
-
- @Override
- public void set(PageIndicatorLine obj, Integer alpha) {
- obj.mLinePaint.setAlpha(alpha);
- obj.invalidate();
- }
- };
-
- private static final Property<PageIndicatorLine, Float> NUM_PAGES
- = new Property<PageIndicatorLine, Float>(Float.class, "num_pages") {
- @Override
- public Float get(PageIndicatorLine obj) {
- return obj.mNumPagesFloat;
- }
-
- @Override
- public void set(PageIndicatorLine obj, Float numPages) {
- obj.mNumPagesFloat = numPages;
- obj.invalidate();
- }
- };
-
- private static final Property<PageIndicatorLine, Integer> TOTAL_SCROLL
- = new Property<PageIndicatorLine, Integer>(Integer.class, "total_scroll") {
- @Override
- public Integer get(PageIndicatorLine obj) {
- return obj.mTotalScroll;
- }
-
- @Override
- public void set(PageIndicatorLine obj, Integer totalScroll) {
- obj.mTotalScroll = totalScroll;
- obj.invalidate();
- }
- };
-
- private Runnable mHideLineRunnable = new Runnable() {
- @Override
- public void run() {
- animateLineToAlpha(0);
- }
- };
-
- public PageIndicatorLine(Context context) {
- this(context, null);
- }
-
- public PageIndicatorLine(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PageIndicatorLine(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- Resources res = context.getResources();
- mLinePaint = new Paint();
- mLinePaint.setAlpha(0);
-
- mLauncher = Launcher.getLauncher(context);
- mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
-
- boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
- mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
- mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mAllAppsHandle = findViewById(R.id.all_apps_handle);
- mAllAppsHandle.setOnClickListener(mLauncher);
- mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
- mLauncher.setAllAppsButton(mAllAppsHandle);
- }
-
- @Override
- public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
- mAllAppsHandle.setAccessibilityDelegate(delegate);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- if (mTotalScroll == 0 || mNumPagesFloat == 0) {
- return;
- }
-
- // Compute and draw line rect.
- float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
- int availableWidth = canvas.getWidth();
- int lineWidth = (int) (availableWidth / mNumPagesFloat);
- int lineLeft = (int) (progress * (availableWidth - lineWidth));
- int lineRight = lineLeft + lineWidth;
- canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(),
- mLinePaint);
- }
-
- @Override
- public void setContentDescription(CharSequence contentDescription) {
- mAllAppsHandle.setContentDescription(contentDescription);
- }
-
- @Override
- public void setScroll(int currentScroll, int totalScroll) {
- if (getAlpha() == 0) {
- return;
- }
- animateLineToAlpha(mActiveAlpha);
-
- mCurrentScroll = currentScroll;
- if (mTotalScroll == 0) {
- mTotalScroll = totalScroll;
- } else if (mTotalScroll != totalScroll) {
- animateToTotalScroll(totalScroll);
- } else {
- invalidate();
- }
-
- if (mShouldAutoHide) {
- hideAfterDelay();
- }
- }
-
- private void hideAfterDelay() {
- mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
- mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
- }
-
- @Override
- public void setActiveMarker(int activePage) {
- }
-
- @Override
- protected void onPageCountChanged() {
- if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
- animateToNumPages(mNumPages);
- }
- }
-
- public void setShouldAutoHide(boolean shouldAutoHide) {
- mShouldAutoHide = shouldAutoHide;
- if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
- hideAfterDelay();
- } else if (!shouldAutoHide) {
- mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
- }
- }
-
- private void animateLineToAlpha(int alpha) {
- if (alpha == mToAlpha) {
- // Ignore the new animation if it is going to the same alpha as the current animation.
- return;
- }
- mToAlpha = alpha;
- setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
- LINE_ALPHA_ANIMATOR_INDEX);
- }
-
- private void animateToNumPages(int numPages) {
- setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
- NUM_PAGES_ANIMATOR_INDEX);
- }
-
- private void animateToTotalScroll(int totalScroll) {
- setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
- TOTAL_SCROLL_ANIMATOR_INDEX);
- }
-
- /**
- * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
- * the animation ends.
- *
- * If an animator is already at the index (i.e. it is already playing), it is canceled and
- * replaced with the new animator.
- */
- private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
- if (mAnimators[animatorIndex] != null) {
- mAnimators[animatorIndex].cancel();
- }
- mAnimators[animatorIndex] = animator;
- mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mAnimators[animatorIndex] = null;
- }
- });
- mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
- mAnimators[animatorIndex].start();
- }
-}
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
new file mode 100644
index 0000000..4fc7d8a
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -0,0 +1,307 @@
+package com.android.launcher3.pageindicators;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+
+/**
+ * A PageIndicator that briefly shows a fraction of a line when moving between pages in
+ * portrait mode. In Landscape simply draws the caret drawable bottom-corner aligned in
+ * the drag-layer.
+ *
+ * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
+ */
+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();
+ public static final int WHITE_ALPHA = (int) (0.70f * 255);
+ public static final int BLACK_ALPHA = (int) (0.65f * 255);
+
+ private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
+ private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
+ private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
+
+ private ValueAnimator[] mAnimators = new ValueAnimator[3];
+
+ private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
+ private final Launcher mLauncher;
+ private final AccessibilityManager mAccessibilityManager;
+
+ private boolean mShouldAutoHide = true;
+
+ // The alpha of the line when it is showing.
+ private int mActiveAlpha = 0;
+ // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
+ private int mToAlpha;
+ // A float value representing the number of pages, to allow for an animation when it changes.
+ private float mNumPagesFloat;
+ private int mCurrentScroll;
+ private int mTotalScroll;
+ private Paint mLinePaint;
+ private final int mLineHeight;
+
+ private boolean mIsLandscapeUi;
+
+ private static final Property<WorkspacePageIndicator, Integer> PAINT_ALPHA
+ = new Property<WorkspacePageIndicator, Integer>(Integer.class, "paint_alpha") {
+ @Override
+ public Integer get(WorkspacePageIndicator obj) {
+ return obj.mLinePaint.getAlpha();
+ }
+
+ @Override
+ public void set(WorkspacePageIndicator obj, Integer alpha) {
+ obj.mLinePaint.setAlpha(alpha);
+ obj.invalidateIfPortrait();
+ }
+ };
+
+ private static final Property<WorkspacePageIndicator, Float> NUM_PAGES
+ = new Property<WorkspacePageIndicator, Float>(Float.class, "num_pages") {
+ @Override
+ public Float get(WorkspacePageIndicator obj) {
+ return obj.mNumPagesFloat;
+ }
+
+ @Override
+ public void set(WorkspacePageIndicator obj, Float numPages) {
+ obj.mNumPagesFloat = numPages;
+ obj.invalidateIfPortrait();
+ }
+ };
+
+ private static final Property<WorkspacePageIndicator, Integer> TOTAL_SCROLL
+ = new Property<WorkspacePageIndicator, Integer>(Integer.class, "total_scroll") {
+ @Override
+ public Integer get(WorkspacePageIndicator obj) {
+ return obj.mTotalScroll;
+ }
+
+ @Override
+ public void set(WorkspacePageIndicator obj, Integer totalScroll) {
+ obj.mTotalScroll = totalScroll;
+ obj.invalidateIfPortrait();
+ }
+ };
+
+ private Runnable mHideLineRunnable = () -> animateLineToAlpha(0);
+
+ public WorkspacePageIndicator(Context context) {
+ this(context, null);
+ }
+
+ public WorkspacePageIndicator(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WorkspacePageIndicator(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ Resources res = context.getResources();
+ mLinePaint = new Paint();
+ mLinePaint.setAlpha(0);
+
+ mLauncher = Launcher.getLauncher(context);
+ mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
+
+ boolean darkText = WallpaperColorInfo.getInstance(context).supportsDarkText();
+ mActiveAlpha = darkText ? BLACK_ALPHA : WHITE_ALPHA;
+ mLinePaint.setColor(darkText ? Color.BLACK : Color.WHITE);
+ mAccessibilityManager = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mTotalScroll == 0 || mNumPagesFloat == 0 || mIsLandscapeUi) {
+ return;
+ }
+
+ // Compute and draw line rect.
+ float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
+ int availableWidth = canvas.getWidth();
+ int lineWidth = (int) (availableWidth / mNumPagesFloat);
+ int lineLeft = (int) (progress * (availableWidth - lineWidth));
+ int lineRight = lineLeft + lineWidth;
+ canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(),
+ mLinePaint);
+ }
+
+ @Override
+ public void setScroll(int currentScroll, int totalScroll) {
+ if (getAlpha() == 0) {
+ return;
+ }
+ animateLineToAlpha(mActiveAlpha);
+
+ mCurrentScroll = currentScroll;
+ if (mTotalScroll == 0) {
+ mTotalScroll = totalScroll;
+ } else if (mTotalScroll != totalScroll) {
+ animateToTotalScroll(totalScroll);
+ } else {
+ invalidateIfPortrait();
+ }
+
+ if (mShouldAutoHide) {
+ hideAfterDelay();
+ }
+ }
+
+ private void hideAfterDelay() {
+ mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
+ mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
+ }
+
+ @Override
+ public void setActiveMarker(int activePage) { }
+
+ @Override
+ public void setMarkersCount(int numMarkers) {
+ if (Float.compare(numMarkers, mNumPagesFloat) != 0) {
+ animateToNumPages(numMarkers);
+ }
+ }
+
+ @Override
+ public void setPageDescription(CharSequence description) {
+ setContentDescription(description);
+ }
+
+ public void setShouldAutoHide(boolean shouldAutoHide) {
+ mShouldAutoHide = shouldAutoHide;
+ if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
+ hideAfterDelay();
+ } else if (!shouldAutoHide) {
+ mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
+ }
+ }
+
+ private void animateLineToAlpha(int alpha) {
+ if (alpha == mToAlpha) {
+ // Ignore the new animation if it is going to the same alpha as the current animation.
+ return;
+ }
+ mToAlpha = alpha;
+ setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
+ LINE_ALPHA_ANIMATOR_INDEX);
+ }
+
+ private void animateToNumPages(int numPages) {
+ setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
+ NUM_PAGES_ANIMATOR_INDEX);
+ }
+
+ private void animateToTotalScroll(int totalScroll) {
+ setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
+ TOTAL_SCROLL_ANIMATOR_INDEX);
+ }
+
+ /**
+ * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
+ * the animation ends.
+ *
+ * If an animator is already at the index (i.e. it is already playing), it is canceled and
+ * replaced with the new animator.
+ */
+ private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
+ if (mAnimators[animatorIndex] != null) {
+ mAnimators[animatorIndex].cancel();
+ }
+ mAnimators[animatorIndex] = animator;
+ mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimators[animatorIndex] = null;
+ }
+ });
+ mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
+ mAnimators[animatorIndex].start();
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ mIsLandscapeUi = grid.isVerticalBarLayout();
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
+
+ if (mIsLandscapeUi) {
+ if (grid.isSeascape()) {
+ lp.leftMargin = grid.hotseatBarSidePaddingPx;
+ lp.rightMargin = insets.right;
+ lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;
+ } else {
+ lp.leftMargin = insets.left;
+ lp.rightMargin = grid.hotseatBarSidePaddingPx;
+ lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ }
+ lp.bottomMargin = grid.workspacePadding.bottom;
+ lp.width = lp.height = getResources()
+ .getDimensionPixelSize(R.dimen.dynamic_grid_min_page_indicator_size);
+
+ setBackgroundResource(R.drawable.all_apps_handle_landscape);
+ setOnFocusChangeListener(mLauncher.mFocusHandler);
+ setOnClickListener(this);
+
+ } else {
+ lp.leftMargin = lp.rightMargin = 0;
+ lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+ lp.height = grid.pageIndicatorSizePx;
+ lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
+ lp.width = MATCH_PARENT;
+
+ setBackgroundResource(0);
+ setOnFocusChangeListener(null);
+ setOnClickListener(mAccessibilityManager.isTouchExplorationEnabled() ? this : null);
+ }
+
+ setLayoutParams(lp);
+ }
+
+ private void invalidateIfPortrait() {
+ if (!mIsLandscapeUi) {
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (!mLauncher.isInState(ALL_APPS)) {
+ mLauncher.getUserEventDispatcher().logActionOnControl(
+ Action.Touch.TAP, ControlType.ALL_APPS_BUTTON);
+ mLauncher.getStateManager().goToState(ALL_APPS);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 3dc58a1..b3ef7bb 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -54,6 +54,7 @@
import com.android.launcher3.DropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherModel;
@@ -61,13 +62,13 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
+import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
-import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.graphics.TriangleShape;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.notification.NotificationInfo;
@@ -85,6 +86,7 @@
import java.util.Map;
import java.util.Set;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.notification.NotificationMainView.NOTIFICATION_ITEM_INFO;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
@@ -340,15 +342,17 @@
updateDividers();
// Add the arrow.
- final int arrowHorizontalOffset = getResources().getDimensionPixelSize(isAlignedWithStart()
- ? R.dimen.popup_arrow_horizontal_offset_start
- : R.dimen.popup_arrow_horizontal_offset_end);
+ final Resources res = getResources();
+ final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
+ ? R.dimen.popup_arrow_horizontal_center_start
+ : R.dimen.popup_arrow_horizontal_center_end);
+ final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
mLauncher.getDragLayer().addView(mArrow);
DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
if (mIsLeftAligned) {
- mArrow.setX(getX() + arrowHorizontalOffset);
+ mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
} else {
- mArrow.setX(getX() + getMeasuredWidth() - arrowHorizontalOffset);
+ mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
}
if (Gravity.isVertical(mGravity)) {
@@ -434,9 +438,6 @@
x = rightAlignedX;
}
mIsLeftAligned = x == leftAlignedX;
- if (mIsRtl) {
- x -= dragLayer.getWidth() - width;
- }
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
int iconWidth = mOriginalIcon.getWidth()
@@ -528,8 +529,7 @@
// enforce contained is within screen
DragLayer dragLayer = mLauncher.getDragLayer();
- if (getTranslationX() + l < 0 ||
- getTranslationX() + r > dragLayer.getWidth()) {
+ if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) {
// If we are still off screen, center horizontally too.
mGravity |= Gravity.CENTER_HORIZONTAL;
}
@@ -573,7 +573,7 @@
@Override
public void onAnimationEnd(Animator animation) {
mOpenCloseAnimator = null;
- Utilities.sendCustomAccessibilityEvent(
+ sendCustomAccessibilityEvent(
PopupContainerWithArrow.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.action_deep_shortcut));
@@ -650,7 +650,7 @@
// reopen the container to ensure measurements etc. all work out. While this could
// be quite janky, in practice the user would typically see a small flicker as the
// animation restarts partway through, and this is a very rare edge case anyway.
- ((PopupContainerWithArrow) getParent()).close(false);
+ close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
} else if (onClickListener == null && widgetsView != null) {
@@ -658,7 +658,7 @@
if (mSystemShortcutContainer != this) {
mSystemShortcutContainer.removeView(widgetsView);
} else {
- ((PopupContainerWithArrow) getParent()).close(false);
+ close(false);
PopupContainerWithArrow.showForIcon(mOriginalIcon);
}
}
@@ -743,11 +743,11 @@
}
private void updateNotificationHeader() {
- ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
+ ItemInfoWithIcon itemInfo = (ItemInfoWithIcon) mOriginalIcon.getTag();
BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
if (mNotificationItemView != null && badgeInfo != null) {
- IconPalette palette = mOriginalIcon.getBadgePalette();
- mNotificationItemView.updateHeader(badgeInfo.getNotificationCount(), palette);
+ mNotificationItemView.updateHeader(
+ badgeInfo.getNotificationCount(), itemInfo.iconColor);
}
}
@@ -811,10 +811,10 @@
return;
}
mEndRect.setEmpty();
+ if (getOutlineProvider() instanceof RevealOutlineAnimation) {
+ ((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
+ }
if (mOpenCloseAnimator != null) {
- Outline outline = new Outline();
- getOutlineProvider().getOutline(this, outline);
- outline.getRect(mEndRect);
mOpenCloseAnimator.cancel();
}
mIsOpen = false;
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index abc186b..335426c 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -25,9 +25,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.badge.BadgeInfo;
-import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.notification.NotificationInfo;
import com.android.launcher3.notification.NotificationKeyData;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.shortcuts.DeepShortcutManager;
@@ -42,7 +40,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Provides data for the popup menu that appears after long-clicking on apps.
@@ -94,8 +91,9 @@
mPackageUserToBadgeInfos.remove(postedPackageUserKey);
}
}
- updateLauncherIconBadges(Utilities.singletonHashSet(postedPackageUserKey),
- badgeShouldBeRefreshed);
+ if (badgeShouldBeRefreshed) {
+ mLauncher.updateIconBadges(Utilities.singletonHashSet(postedPackageUserKey));
+ }
}
@Override
@@ -106,7 +104,7 @@
if (oldBadgeInfo.getNotificationKeys().size() == 0) {
mPackageUserToBadgeInfos.remove(removedPackageUserKey);
}
- updateLauncherIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
+ mLauncher.updateIconBadges(Utilities.singletonHashSet(removedPackageUserKey));
trimNotifications(mPackageUserToBadgeInfos);
}
}
@@ -142,7 +140,7 @@
}
if (!updatedBadges.isEmpty()) {
- updateLauncherIconBadges(updatedBadges.keySet());
+ mLauncher.updateIconBadges(updatedBadges.keySet());
}
trimNotifications(updatedBadges);
}
@@ -154,66 +152,6 @@
}
}
- private void updateLauncherIconBadges(Set<PackageUserKey> updatedBadges) {
- updateLauncherIconBadges(updatedBadges, true);
- }
-
- /**
- * Updates the icons on launcher (workspace, folders, all apps) to refresh their badges.
- * @param updatedBadges The packages whose badges should be refreshed (either a notification was
- * added or removed, or the badge should show the notification icon).
- * @param shouldRefresh An optional parameter that will allow us to only refresh badges that
- * have actually changed. If a notification updated its content but not
- * its count or icon, then the badge doesn't change.
- */
- private void updateLauncherIconBadges(Set<PackageUserKey> updatedBadges,
- boolean shouldRefresh) {
- Iterator<PackageUserKey> iterator = updatedBadges.iterator();
- while (iterator.hasNext()) {
- BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(iterator.next());
- if (badgeInfo != null && !updateBadgeIcon(badgeInfo) && !shouldRefresh) {
- // The notification icon isn't used, and the badge hasn't changed
- // so there is no update to be made.
- iterator.remove();
- }
- }
- if (!updatedBadges.isEmpty()) {
- mLauncher.updateIconBadges(updatedBadges);
- }
- }
-
- /**
- * Determines whether the badge should show a notification icon rather than a number,
- * and sets that icon on the BadgeInfo if so.
- * @param badgeInfo The badge to update with an icon (null if it shouldn't show one).
- * @return Whether the badge icon potentially changed (true unless it stayed null).
- */
- private boolean updateBadgeIcon(BadgeInfo badgeInfo) {
- boolean hadNotificationToShow = badgeInfo.hasNotificationToShow();
- NotificationInfo notificationInfo = null;
- NotificationListener notificationListener = NotificationListener.getInstanceIfConnected();
- if (notificationListener != null && badgeInfo.getNotificationKeys().size() >= 1) {
- // Look for the most recent notification that has an icon that should be shown in badge.
- for (NotificationKeyData notificationKeyData : badgeInfo.getNotificationKeys()) {
- String notificationKey = notificationKeyData.notificationKey;
- StatusBarNotification[] activeNotifications = notificationListener
- .getActiveNotifications(new String[]{notificationKey});
- if (activeNotifications.length == 1) {
- notificationInfo = new NotificationInfo(mLauncher, activeNotifications[0]);
- if (notificationInfo.shouldShowIconInBadge()) {
- // Found an appropriate icon.
- break;
- } else {
- // Keep looking.
- notificationInfo = null;
- }
- }
- }
- }
- badgeInfo.setNotificationToShow(notificationInfo);
- return hadNotificationToShow || badgeInfo.hasNotificationToShow();
- }
-
public void setDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
mDeepShortcutMap = deepShortcutMapCopy;
if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index 6c83d12..b295bb2 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -148,8 +148,9 @@
final ShortcutInfoCompat shortcut = shortcuts.get(i);
final ShortcutInfo si = new ShortcutInfo(shortcut, launcher);
// Use unbadged icon for the menu.
- si.iconBitmap = LauncherIcons.createShortcutIcon(
- shortcut, launcher, false /* badged */);
+ LauncherIcons li = LauncherIcons.obtain(launcher);
+ li.createShortcutIcon(shortcut, false /* badged */).applyTo(si);
+ li.recycle();
si.rank = i;
final DeepShortcutView view = shortcutViews.get(i);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index c398aaa..32fd063 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,5 +1,8 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
@@ -19,9 +22,6 @@
import java.util.List;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-
/**
* Represents a system shortcut for a given app. The shortcut should have a static label and
* icon, and an onClickListener that depends on the item that the shortcut services.
@@ -82,7 +82,7 @@
@Override
public void onClick(View view) {
Rect sourceBounds = launcher.getViewBounds(view);
- Bundle opts = launcher.getActivityLaunchOptions(view);
+ Bundle opts = launcher.getActivityLaunchOptionsAsBundle(view, false);
InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, sourceBounds, opts);
launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
@@ -110,14 +110,15 @@
if (!enabled) {
return null;
}
- return new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = PackageManagerHelper.getMarketIntent(itemInfo
- .getTargetComponent().getPackageName());
- launcher.startActivitySafely(view, intent, itemInfo);
- AbstractFloatingView.closeAllOpenViews(launcher);
- }
+ return createOnClickListener(launcher, itemInfo);
+ }
+
+ public View.OnClickListener createOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ return view -> {
+ Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
+ itemInfo.getTargetComponent().getPackageName());
+ launcher.startActivitySafely(view, intent, itemInfo);
+ AbstractFloatingView.closeAllOpenViews(launcher);
};
}
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
index 9c91c87..325777d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -18,11 +18,14 @@
import android.annotation.TargetApi;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Build;
import android.os.UserHandle;
+import com.android.launcher3.R;
+
/**
* Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
*
@@ -31,8 +34,8 @@
@TargetApi(Build.VERSION_CODES.N)
public class ShortcutInfoCompat {
private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+ private static final String EXTRA_BADGEPKG = "badge_package";
public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
-
private ShortcutInfo mShortcutInfo;
public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
@@ -57,6 +60,15 @@
return mShortcutInfo.getPackage();
}
+ public String getBadgePackage(Context context) {
+ String whitelistedPkg = context.getString(R.string.shortcutinfocompat_badgepkg_whitelist);
+ if (whitelistedPkg.equals(getPackage())
+ && mShortcutInfo.getExtras().containsKey(EXTRA_BADGEPKG)) {
+ return mShortcutInfo.getExtras().getString(EXTRA_BADGEPKG);
+ }
+ return getPackage();
+ }
+
public String getId() {
return mShortcutInfo.getId();
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index e86bfb2..cbef85a 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -17,6 +17,10 @@
super(new ComponentName(packageName, id), user);
}
+ public ShortcutKey(ComponentName componentName, UserHandle user) {
+ super(componentName, user);
+ }
+
public String getId() {
return componentName.getClassName();
}
diff --git a/src/com/android/launcher3/states/InternalStateHandler.java b/src/com/android/launcher3/states/InternalStateHandler.java
index f084fd2..d3c0fef 100644
--- a/src/com/android/launcher3/states/InternalStateHandler.java
+++ b/src/com/android/launcher3/states/InternalStateHandler.java
@@ -21,7 +21,11 @@
import android.os.IBinder;
import com.android.launcher3.Launcher;
-import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.MainThreadExecutor;
+
+import java.lang.ref.WeakReference;
/**
* Utility class to sending state handling logic to Launcher from within the same process.
@@ -29,11 +33,17 @@
* Extending {@link Binder} ensures that the platform maintains a single instance of each object
* which allows this object to safely navigate the system process.
*/
-public abstract class InternalStateHandler extends Binder implements OnResumeCallback {
+public abstract class InternalStateHandler extends Binder {
public static final String EXTRA_STATE_HANDLER = "launcher.state_handler";
- protected abstract void init(Launcher launcher, boolean alreadyOnHome);
+ private static final Scheduler sScheduler = new Scheduler();
+
+ /**
+ * Initializes the handler when the launcher is ready.
+ * @return true if the handler wants to stay alive.
+ */
+ protected abstract boolean init(Launcher launcher, boolean alreadyOnHome);
public final Intent addToIntent(Intent intent) {
Bundle extras = new Bundle();
@@ -42,27 +52,83 @@
return intent;
}
+ public final void initWhenReady() {
+ sScheduler.schedule(this);
+ }
+
+ public void clearReference() {
+ sScheduler.clearReference(this);
+ }
+
public static boolean handleCreate(Launcher launcher, Intent intent) {
- return handleIntent(launcher, intent, false);
+ return handleIntent(launcher, intent, false, false);
}
public static boolean handleNewIntent(Launcher launcher, Intent intent, boolean alreadyOnHome) {
- return handleIntent(launcher, intent, alreadyOnHome);
+ return handleIntent(launcher, intent, alreadyOnHome, true);
}
private static boolean handleIntent(
- Launcher launcher, Intent intent, boolean alreadyOnHome) {
+ Launcher launcher, Intent intent, boolean alreadyOnHome, boolean explicitIntent) {
boolean result = false;
if (intent != null && intent.getExtras() != null) {
IBinder stateBinder = intent.getExtras().getBinder(EXTRA_STATE_HANDLER);
if (stateBinder instanceof InternalStateHandler) {
InternalStateHandler handler = (InternalStateHandler) stateBinder;
- launcher.setOnResumeCallback(handler);
- handler.init(launcher, alreadyOnHome);
+ if (!handler.init(launcher, alreadyOnHome)) {
+ intent.getExtras().remove(EXTRA_STATE_HANDLER);
+ }
result = true;
}
- intent.getExtras().remove(EXTRA_STATE_HANDLER);
+ }
+ if (!result && !explicitIntent) {
+ 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/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 3864e3a..6d584cd 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -35,31 +35,37 @@
public class SpringLoadedState extends LauncherState {
private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE |
- FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE;
+ FLAG_DISABLE_ACCESSIBILITY | FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED |
+ FLAG_DISABLE_PAGE_CLIPPING;
// Determines how long to wait after a rotation before restoring the screen orientation to
// match the sensor state.
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
public SpringLoadedState(int id) {
- super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, 1f, STATE_FLAGS);
+ super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS);
}
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
DeviceProfile grid = launcher.getDeviceProfile();
Workspace ws = launcher.getWorkspace();
- if (grid.isVerticalBarLayout() || ws.getChildCount() == 0) {
+ if (ws.getChildCount() == 0) {
return super.getWorkspaceScaleAndTranslation(launcher);
}
+ if (grid.isVerticalBarLayout()) {
+ float scale = grid.workspaceSpringLoadShrinkFactor;
+ return new float[] {scale, 0, 0};
+ }
+
float scale = grid.workspaceSpringLoadShrinkFactor;
Rect insets = launcher.getDragLayer().getInsets();
float scaledHeight = scale * ws.getNormalChildHeight();
float shrunkTop = insets.top + grid.dropTargetBarSizePx;
- float shrunkBottom = ws.getViewportHeight() - insets.bottom
- - grid.getWorkspacePadding(null).bottom
+ float shrunkBottom = ws.getMeasuredHeight() - insets.bottom
+ - grid.workspacePadding.bottom
- grid.workspaceSpringLoadedBottomSpace;
float totalShrunkSpace = shrunkBottom - shrunkTop;
@@ -69,7 +75,7 @@
float myCenter = ws.getTop() + halfHeight;
float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
float actualCellTop = myCenter - cellTopFromCenter * scale;
- return new float[] { scale, (desiredCellTop - actualCellTop) / scale};
+ return new float[] { scale, 0, (desiredCellTop - actualCellTop) / scale};
}
@Override
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index 351f88d..4b36ad9 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -150,7 +150,7 @@
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
- private final Direction mDir;
+ private Direction mDir;
private final float mTouchSlop;
@@ -185,6 +185,10 @@
mDir = dir;
}
+ public void updateDirection(Direction dir) {
+ mDir = dir;
+ }
+
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
mScrollConditions = scrollDirectionFlags;
mIgnoreSlopWhenSettling = ignoreSlop;
@@ -286,6 +290,16 @@
}
}
+ /**
+ * Returns if the start drag was towards the positive direction or negative.
+ *
+ * @see #setDetectableScrollConditions(int, boolean)
+ * @see #DIRECTION_BOTH
+ */
+ public boolean wasInitialTouchPositive() {
+ return mSubtractDisplacement < 0;
+ }
+
private boolean reportDragging() {
if (mDisplacement != mLastDisplacement) {
if (DBG) {
@@ -336,7 +350,7 @@
/**
* Returns the linear interpolation between two values
*/
- private static float interpolate(float from, float to, float alpha) {
+ public static float interpolate(float from, float to, float alpha) {
return (1.0f - alpha) * from + alpha * to;
}
diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java
index 242df2e..d478ffa 100644
--- a/src/com/android/launcher3/util/ComponentKey.java
+++ b/src/com/android/launcher3/util/ComponentKey.java
@@ -17,12 +17,8 @@
*/
import android.content.ComponentName;
-import android.content.Context;
-import android.os.Process;
import android.os.UserHandle;
-import com.android.launcher3.compat.UserManagerCompat;
-
import java.util.Arrays;
public class ComponentKey {
@@ -41,29 +37,6 @@
}
- /**
- * Creates a new component key from an encoded component key string in the form of
- * [flattenedComponentString#userId]. If the userId is not present, then it defaults
- * to the current user.
- */
- public ComponentKey(Context context, String componentKeyStr) {
- int userDelimiterIndex = componentKeyStr.indexOf("#");
- if (userDelimiterIndex != -1) {
- String componentStr = componentKeyStr.substring(0, userDelimiterIndex);
- Long componentUser = Long.valueOf(componentKeyStr.substring(userDelimiterIndex + 1));
- componentName = ComponentName.unflattenFromString(componentStr);
- user = UserManagerCompat.getInstance(context)
- .getUserForSerialNumber(componentUser.longValue());
- } else {
- // No user provided, default to the current user
- componentName = ComponentName.unflattenFromString(componentKeyStr);
- user = Process.myUserHandle();
- }
- Preconditions.assertNotNull(componentName);
- Preconditions.assertNotNull(user);
- mHashCode = Arrays.hashCode(new Object[] {componentName, user});
- }
-
@Override
public int hashCode() {
return mHashCode;
diff --git a/src/com/android/launcher3/util/ComponentKeyMapper.java b/src/com/android/launcher3/util/ComponentKeyMapper.java
deleted file mode 100644
index 916176a..0000000
--- a/src/com/android/launcher3/util/ComponentKeyMapper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.android.launcher3.util;
-
-/**
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class ComponentKeyMapper<T> {
-
- protected final ComponentKey mComponentKey;
-
- public ComponentKeyMapper(ComponentKey key) {
- this.mComponentKey = key;
- }
-
- public @Nullable T getItem(Map<ComponentKey, T> map) {
- return map.get(mComponentKey);
- }
-
- public String getPackage() {
- return mComponentKey.componentName.getPackageName();
- }
-
- public String getComponentClass() {
- return mComponentKey.componentName.getClassName();
- }
-
- @Override
- public String toString() {
- return mComponentKey.toString();
- }
-
-}
diff --git a/src/com/android/launcher3/util/FloatRange.java b/src/com/android/launcher3/util/FloatRange.java
new file mode 100644
index 0000000..12772f3
--- /dev/null
+++ b/src/com/android/launcher3/util/FloatRange.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+/**
+ * A mutable class for describing the range of two int values.
+ */
+public class FloatRange {
+
+ public float start, end;
+
+ public FloatRange() { }
+
+ public FloatRange(float s, float e) {
+ set(s, e);
+ }
+
+ public void set(float s, float e) {
+ start = s;
+ end = e;
+ }
+
+ public boolean contains(float value) {
+ return value >= start && value <= end;
+ }
+}
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 99ce7ca..601a5ab 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -18,8 +18,11 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -44,6 +47,17 @@
return false;
}
+ public boolean isInstantApp(Launcher launcher, String packageName) {
+ PackageManager packageManager = launcher.getPackageManager();
+ try {
+ return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e("InstantAppResolver", "Failed to determine whether package is instant app "
+ + packageName, e);
+ }
+ return false;
+ }
+
public List<ApplicationInfo> getInstantApps() {
return Collections.emptyList();
}
diff --git a/src/com/android/launcher3/util/LooperExecutor.java b/src/com/android/launcher3/util/LooperExecutor.java
index 5b7c20b..cc07469 100644
--- a/src/com/android/launcher3/util/LooperExecutor.java
+++ b/src/com/android/launcher3/util/LooperExecutor.java
@@ -33,6 +33,10 @@
mHandler = new Handler(looper);
}
+ public Handler getHandler() {
+ return mHandler;
+ }
+
@Override
public void execute(Runnable runnable) {
if (mHandler.getLooper() == Looper.myLooper()) {
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 13034dd..81df153 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -143,13 +143,15 @@
return false;
}
- public static Intent getMarketIntent(String packageName) {
+ public Intent getMarketIntent(String packageName) {
return new Intent(Intent.ACTION_VIEW)
.setData(new Uri.Builder()
.scheme("market")
.authority("details")
.appendQueryParameter("id", packageName)
- .build());
+ .build())
+ .putExtra(Intent.EXTRA_REFERRER, new Uri.Builder().scheme("android-app")
+ .authority(mContext.getPackageName()).build());
}
/**
diff --git a/src/com/android/launcher3/util/RunnableWithId.java b/src/com/android/launcher3/util/RunnableWithId.java
deleted file mode 100644
index 030eb09..0000000
--- a/src/com/android/launcher3/util/RunnableWithId.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-/**
- * A runnable with an id associated which is used for equality check.
- */
-public abstract class RunnableWithId implements Runnable {
-
- public static final int RUNNABLE_ID_BIND_APPS = 1;
- public static final int RUNNABLE_ID_BIND_WIDGETS = 2;
-
- public final int id;
-
- public RunnableWithId(int id) {
- this.id = id;
- }
-
- @Override
- public boolean equals(Object obj) {
- return obj instanceof RunnableWithId && ((RunnableWithId) obj).id == id;
- }
-}
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index 5b66fcd..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,17 +35,17 @@
*/
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 = true;
- private static final ArrayMap<String, MutableLong> sUpTimes =
- ENABLED ? new ArrayMap<String, MutableLong>() : null;
+ private static final boolean SYSTEM_TRACE = false;
+ private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
public static void beginSection(String sectionName) {
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) {
@@ -56,7 +60,7 @@
public static void partitionSection(String sectionName, String partition) {
if (ENABLED) {
MutableLong time = sUpTimes.get(sectionName);
- if (time.value >= 0) {
+ if (time != null && time.value >= 0) {
if (SYSTEM_TRACE) {
Trace.endSection();
@@ -79,7 +83,7 @@
public static void endSection(String sectionName, String msg) {
if (ENABLED) {
MutableLong time = sUpTimes.get(sectionName);
- if (time.value >= 0) {
+ if (time != null && time.value >= 0) {
if (SYSTEM_TRACE) {
Trace.endSection();
}
diff --git a/src/com/android/launcher3/VerticalSwipeController.java b/src/com/android/launcher3/util/VerticalSwipeController.java
similarity index 68%
rename from src/com/android/launcher3/VerticalSwipeController.java
rename to src/com/android/launcher3/util/VerticalSwipeController.java
index b3dc176..a647378 100644
--- a/src/com/android/launcher3/VerticalSwipeController.java
+++ b/src/com/android/launcher3/util/VerticalSwipeController.java
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.util;
import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.anim.SpringAnimationHandler.Y_DIRECTION;
@@ -28,21 +27,23 @@
import android.util.Log;
import android.view.MotionEvent;
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.touch.SwipeDetector;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.TouchController;
+import com.android.launcher3.touch.SwipeDetector.Direction;
import java.util.ArrayList;
/**
- * Handles vertical touch gesture on the DragLayer
+ * Handles vertical touch gesture on the DragLayer allowing transitioning from
+ * {@link #mBaseState} to {@link LauncherState#ALL_APPS} and vice-versa.
*/
-public class VerticalSwipeController extends AnimatorListenerAdapter
+public abstract class VerticalSwipeController extends AnimatorListenerAdapter
implements TouchController, SwipeDetector.Listener {
private static final String TAG = "VerticalSwipeController";
@@ -53,45 +54,47 @@
// Progress after which the transition is assumed to be a success in case user does not fling
private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
- private final Launcher mLauncher;
- private final SwipeDetector mDetector;
+ protected final Launcher mLauncher;
+ protected final SwipeDetector mDetector;
+ private final LauncherState mBaseState;
+ private final LauncherState mTargetState;
private boolean mNoIntercept;
- private int mStartContainerType;
private AnimatorPlaybackController mCurrentAnimation;
- private LauncherState mToState;
+ protected LauncherState mToState;
private float mStartProgress;
// Ratio of transition process [0, 1] to drag displacement (px)
private float mProgressMultiplier;
- private SpringAnimationHandler[] mSpringHandlers;
+ protected SpringAnimationHandler[] mSpringHandlers;
- public VerticalSwipeController(Launcher l) {
+ public VerticalSwipeController(Launcher l, LauncherState baseState) {
+ this(l, baseState, ALL_APPS, SwipeDetector.VERTICAL);
+ }
+
+ public VerticalSwipeController(
+ Launcher l, LauncherState baseState, LauncherState targetState, Direction dir) {
mLauncher = l;
- mDetector = new SwipeDetector(l, this, SwipeDetector.VERTICAL);
+ mDetector = new SwipeDetector(l, this, dir);
+ mBaseState = baseState;
+ mTargetState = targetState;
}
private boolean canInterceptTouch(MotionEvent ev) {
- if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) {
- // Don't listen for the swipe gesture if we are already in some other state.
- return false;
- }
if (mCurrentAnimation != null) {
// If we are already animating from a previous state, we can intercept.
return true;
}
- if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) {
- return false;
- }
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
return false;
}
-
- return true;
+ return shouldInterceptTouch(ev);
}
+ protected abstract boolean shouldInterceptTouch(MotionEvent ev);
+
@Override
public void onAnimationCancel(Animator animation) {
if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
@@ -101,7 +104,7 @@
}
}
- private void initSprings() {
+ protected void initSprings() {
AllAppsContainerView appsView = mLauncher.getAppsView();
SpringAnimationHandler handler = appsView.getSpringAnimationHandler();
@@ -147,14 +150,7 @@
ignoreSlopWhenSettling = true;
}
} else {
- if (mLauncher.isInState(ALL_APPS)) {
- directionsToDetectScroll = SwipeDetector.DIRECTION_NEGATIVE;
- mStartContainerType = ContainerType.ALLAPPS;
- } else {
- directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
- mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ?
- ContainerType.HOTSEAT : ContainerType.WORKSPACE;
- }
+ directionsToDetectScroll = getSwipeDirection(ev);
}
mDetector.setDetectableScrollConditions(
@@ -173,6 +169,8 @@
return mDetector.isDraggingOrSettling();
}
+ protected abstract int getSwipeDirection(MotionEvent ev);
+
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
for (SpringAnimationHandler h : mSpringHandlers) {
@@ -188,12 +186,13 @@
long maxAccuracy = (long) (2 * range);
// Build current animation
- mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
+ mToState = mLauncher.isInState(mTargetState) ? mBaseState : mTargetState;
mCurrentAnimation = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mToState, maxAccuracy);
mCurrentAnimation.getTarget().addListener(this);
mStartProgress = 0;
- mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
+ mProgressMultiplier =
+ (mLauncher.isInState(mTargetState) ^ isTransitionFlipped() ? 1 : -1) / range;
mCurrentAnimation.dispatchOnStart();
} else {
mCurrentAnimation.pause();
@@ -205,8 +204,12 @@
}
}
- private float getShiftRange() {
- return mLauncher.mAllAppsController.getShiftRange();
+ protected boolean isTransitionFlipped() {
+ return false;
+ }
+
+ protected float getShiftRange() {
+ return mLauncher.getAllAppsController().getShiftRange();
}
@Override
@@ -219,59 +222,44 @@
@Override
public void onDragEnd(float velocity, boolean fling) {
final long animationDuration;
- final int logAction;
final LauncherState targetState;
final float progress = mCurrentAnimation.getProgressFraction();
if (fling) {
- logAction = Touch.FLING;
- if (velocity < 0) {
- targetState = ALL_APPS;
- animationDuration = SwipeDetector.calculateDuration(velocity,
- mToState == ALL_APPS ? (1 - progress) : progress);
+ if (velocity < 0 ^ isTransitionFlipped()) {
+ targetState = mTargetState;
} else {
- targetState = NORMAL;
- animationDuration = SwipeDetector.calculateDuration(velocity,
- mToState == ALL_APPS ? progress : (1 - progress));
+ targetState = mBaseState;
}
+ animationDuration = SwipeDetector.calculateDuration(velocity,
+ mToState == targetState ? (1 - progress) : progress);
// snap to top or bottom using the release velocity
} else {
- logAction = Touch.SWIPE;
if (progress > SUCCESS_TRANSITION_PROGRESS) {
targetState = mToState;
animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
} else {
- targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
+ targetState = mToState == mTargetState ? mBaseState : mTargetState;
animationDuration = SwipeDetector.calculateDuration(velocity, progress);
}
}
- if (fling && targetState == ALL_APPS) {
+ if (fling && targetState == mTargetState) {
for (SpringAnimationHandler h : mSpringHandlers) {
// The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
}
}
- mCurrentAnimation.setEndAction(new Runnable() {
- @Override
- public void run() {
- if (targetState == mToState) {
- // Transition complete. log the action
- mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
- mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
- mStartContainerType, mLauncher.getWorkspace().getCurrentPage());
- } else {
- mLauncher.getStateManager().goToState(
- mToState == ALL_APPS ? NORMAL : ALL_APPS, false);
- }
- mDetector.finishedScrolling();
- mCurrentAnimation = null;
- }
+ mCurrentAnimation.setEndAction(() -> {
+ mLauncher.getStateManager().goToState(targetState, false);
+ onTransitionComplete(fling, targetState == mToState);
+ mDetector.finishedScrolling();
+ mCurrentAnimation = null;
});
float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
+ progress + velocity * SINGLE_FRAME_MS * mProgressMultiplier, 0f, 1f);
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
anim.setFloatValues(nextFrameProgress, targetState == mToState ? 1f : 0f);
@@ -279,4 +267,6 @@
anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
anim.start();
}
+
+ protected abstract void onTransitionComplete(boolean wasFling, boolean stateChanged);
}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index e5c1dd1..acce308 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -34,24 +34,25 @@
OnAttachStateChangeListener {
private final ArrayList<Runnable> mTasks = new ArrayList<>();
- private final Executor mExecutor;
private Launcher mLauncher;
private View mAttachedView;
private boolean mCompleted;
- private boolean mIsExecuting;
private boolean mLoadAnimationCompleted;
private boolean mFirstDrawCompleted;
- public ViewOnDrawExecutor(Executor executor) {
- mExecutor = executor;
+ public void attachTo(Launcher launcher) {
+ attachTo(launcher, launcher.getWorkspace(), true /* waitForLoadAnimation */);
}
- public void attachTo(Launcher launcher) {
+ public void attachTo(Launcher launcher, View attachedView, boolean waitForLoadAnimation) {
mLauncher = launcher;
- mAttachedView = launcher.getWorkspace();
+ mAttachedView = attachedView;
mAttachedView.addOnAttachStateChangeListener(this);
+ if (!waitForLoadAnimation) {
+ mLoadAnimationCompleted = true;
+ }
attachObserver();
}
@@ -74,7 +75,7 @@
}
@Override
- public void onViewDetachedFromWindow(View v) { }
+ public void onViewDetachedFromWindow(View v) {}
@Override
public void onDraw() {
@@ -82,13 +83,6 @@
mAttachedView.post(this);
}
- /**
- * Returns whether the executor is still queuing tasks and hasn't yet executed them.
- */
- public boolean canQueue() {
- return !mIsExecuting && !mCompleted;
- }
-
public void onLoadAnimationCompleted() {
mLoadAnimationCompleted = true;
if (mAttachedView != null) {
@@ -100,18 +94,13 @@
public void run() {
// Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
- mIsExecuting = true;
- for (final Runnable r : mTasks) {
- mExecutor.execute(r);
- }
- markCompleted();
+ runAllTasks();
}
}
public void markCompleted() {
mTasks.clear();
mCompleted = true;
- mIsExecuting = false;
if (mAttachedView != null) {
mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
mAttachedView.removeOnAttachStateChangeListener(this);
@@ -121,4 +110,15 @@
}
LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_DEFAULT);
}
+
+ protected boolean isCompleted() {
+ return mCompleted;
+ }
+
+ protected void runAllTasks() {
+ for (final Runnable r : mTasks) {
+ r.run();
+ }
+ markCompleted();
+ }
}
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
new file mode 100644
index 0000000..7c4529d
--- /dev/null
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.touch.SwipeDetector;
+
+/**
+ * Extension of AbstractFloatingView with common methods for sliding in from bottom
+ */
+public abstract class AbstractSlideInView extends AbstractFloatingView
+ implements SwipeDetector.Listener {
+
+ protected static Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
+ new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
+
+ @Override
+ public Float get(AbstractSlideInView view) {
+ return view.mTranslationShift;
+ }
+
+ @Override
+ public void set(AbstractSlideInView view, Float value) {
+ view.setTranslationShift(value);
+ }
+ };
+ protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
+ protected static final float TRANSLATION_SHIFT_OPENED = 0f;
+
+ protected final Launcher mLauncher;
+ protected final SwipeDetector mSwipeDetector;
+ protected final ObjectAnimator mOpenCloseAnimator;
+
+ protected View mContent;
+ protected Interpolator mScrollInterpolator;
+
+ // range [0, 1], 0=> completely open, 1=> completely closed
+ protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
+
+ protected boolean mNoIntercept;
+
+ public AbstractSlideInView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mLauncher = Launcher.getLauncher(context);
+
+ mScrollInterpolator = Interpolators.SCROLL_CUBIC;
+ mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
+
+ mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mSwipeDetector.finishedScrolling();
+ }
+ });
+ }
+
+ protected void setTranslationShift(float translationShift) {
+ mTranslationShift = translationShift;
+ mContent.setTranslationY(mTranslationShift * mContent.getHeight());
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (mNoIntercept) {
+ return false;
+ }
+
+ int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
+ SwipeDetector.DIRECTION_NEGATIVE : 0;
+ mSwipeDetector.setDetectableScrollConditions(
+ directionsToDetectScroll, false);
+ mSwipeDetector.onTouchEvent(ev);
+ return mSwipeDetector.isDraggingOrSettling()
+ || !mLauncher.getDragLayer().isEventOverView(mContent, ev);
+ }
+
+ @Override
+ public boolean onControllerTouchEvent(MotionEvent ev) {
+ mSwipeDetector.onTouchEvent(ev);
+ if (ev.getAction() == MotionEvent.ACTION_UP && mSwipeDetector.isIdleState()) {
+ // If we got ACTION_UP without ever starting swipe, close the panel.
+ if (!mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
+ close(true);
+ }
+ }
+ return true;
+ }
+
+ /* SwipeDetector.Listener */
+
+ @Override
+ public void onDragStart(boolean start) { }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ float range = mContent.getHeight();
+ displacement = Utilities.boundToRange(displacement, 0, range);
+ setTranslationShift(displacement / range);
+ return true;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ if ((fling && velocity > 0) || mTranslationShift > 0.5f) {
+ mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
+ mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(
+ velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
+ close(true);
+ } else {
+ mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
+ TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator.setDuration(
+ SwipeDetector.calculateDuration(velocity, mTranslationShift))
+ .setInterpolator(Interpolators.DEACCEL);
+ mOpenCloseAnimator.start();
+ }
+ }
+
+ protected void handleClose(boolean animate, long defaultDuration) {
+ if (mIsOpen && !animate) {
+ mOpenCloseAnimator.cancel();
+ setTranslationShift(TRANSLATION_SHIFT_CLOSED);
+ onCloseComplete();
+ return;
+ }
+ if (!mIsOpen || mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
+ mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onCloseComplete();
+ }
+ });
+ if (mSwipeDetector.isIdleState()) {
+ mOpenCloseAnimator
+ .setDuration(defaultDuration)
+ .setInterpolator(Interpolators.ACCEL);
+ } else {
+ mOpenCloseAnimator.setInterpolator(mScrollInterpolator);
+ }
+ mOpenCloseAnimator.start();
+ }
+
+ protected void onCloseComplete() {
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+}
diff --git a/src/com/android/launcher3/views/AllAppsScrim.java b/src/com/android/launcher3/views/AllAppsScrim.java
new file mode 100644
index 0000000..0ef9c8f
--- /dev/null
+++ b/src/com/android/launcher3/views/AllAppsScrim.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+import com.android.launcher3.dynamicui.WallpaperColorInfo.OnChangeListener;
+import com.android.launcher3.graphics.NinePatchDrawHelper;
+import com.android.launcher3.graphics.ShadowGenerator;
+import com.android.launcher3.util.Themes;
+
+import static com.android.launcher3.graphics.NinePatchDrawHelper.EXTENSION_PX;
+
+public class AllAppsScrim extends View implements OnChangeListener, Insettable {
+
+ private static final int MAX_ALPHA = 235;
+ private static final int MIN_ALPHA_PORTRAIT = 100;
+ private static final int MIN_ALPHA_LANDSCAPE = MAX_ALPHA;
+
+ protected final WallpaperColorInfo mWallpaperColorInfo;
+ private final Paint mFillPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ private final Rect mDrawRect = new Rect();
+ private final Rect mPadding = new Rect();
+ private final Rect mInsets = new Rect();
+
+ private final float mRadius;
+ private final int mScrimColor;
+
+ private int mMinAlpha;
+ private int mAlphaRange;
+
+ private final float mShadowBlur;
+ private final Bitmap mShadowBitmap;
+
+ private final NinePatchDrawHelper mShadowHelper = new NinePatchDrawHelper();
+
+ private int mFillAlpha;
+
+ private float mDrawHeight;
+ private float mDrawOffsetY;
+
+ public AllAppsScrim(Context context) {
+ this(context, null);
+ }
+
+ public AllAppsScrim(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AllAppsScrim(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mWallpaperColorInfo = WallpaperColorInfo.getInstance(context);
+ mScrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ mRadius = getResources().getDimension(R.dimen.all_apps_scrim_radius);
+ mShadowBlur = getResources().getDimension(R.dimen.all_apps_scrim_blur);
+
+ initDefaults();
+ mFillAlpha = mMinAlpha;
+ mShadowBitmap = generateShadowBitmap();
+
+ updateColors(mWallpaperColorInfo);
+ }
+
+ private DeviceProfile initDefaults() {
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+ mMinAlpha = grid.isVerticalBarLayout()
+ ? MIN_ALPHA_LANDSCAPE : MIN_ALPHA_PORTRAIT;
+ mAlphaRange = MAX_ALPHA - mMinAlpha;
+ return grid;
+ }
+
+ private Bitmap generateShadowBitmap() {
+ float curveBot = mRadius + mShadowBlur;
+
+ ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.TRANSPARENT);
+ builder.radius = mRadius;
+ builder.shadowBlur = mShadowBlur;
+
+ // Create the bitmap such that only the top half is drawn in the bitmap.
+ int bitmapWidth = 2 * Math.round(curveBot) + EXTENSION_PX;
+ int bitmapHeight = bitmapWidth / 2;
+ Bitmap result = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+
+ float fullSize = 2 * curveBot + EXTENSION_PX - mShadowBlur;
+ builder.bounds.set(mShadowBlur, mShadowBlur, fullSize, fullSize);
+ builder.drawShadow(new Canvas(result));
+ return result;
+ }
+
+ public Bitmap getShadowBitmap() {
+ return mShadowBitmap;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mWallpaperColorInfo.addOnChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mWallpaperColorInfo.removeOnChangeListener(this);
+ }
+
+ @Override
+ public void onExtractedColorsChanged(WallpaperColorInfo info) {
+ updateColors(info);
+ invalidate();
+ }
+
+ private void updateColors(WallpaperColorInfo info) {
+ mFillPaint.setColor(ColorUtils.compositeColors(mScrimColor,
+ ColorUtils.compositeColors(mScrimColor, info.getMainColor())));
+ mFillPaint.setAlpha(mFillAlpha);
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ float edgeTop = getHeight() + mDrawOffsetY - mDrawHeight + mPadding.top;
+ float edgeRight = getWidth() - mPadding.right;
+
+ if (mPadding.left > 0 || mPadding.right > 0) {
+ mShadowHelper.drawVerticallyStretched(mShadowBitmap, canvas,
+ mPadding.left - mShadowBlur,
+ edgeTop - mShadowBlur,
+ edgeRight + mShadowBlur,
+ getHeight());
+ } else {
+ mShadowHelper.draw(mShadowBitmap, canvas, mPadding.left - mShadowBlur,
+ edgeTop - mShadowBlur, edgeRight + mShadowBlur);
+ }
+ canvas.drawRoundRect(mPadding.left, edgeTop, edgeRight,
+ getHeight() + mRadius, mRadius, mRadius, mFillPaint);
+ }
+
+ public void setProgress(float translateY, float alpha) {
+ int newAlpha = Math.round(alpha * mAlphaRange + mMinAlpha);
+ // Negative translation means the scrim is moving up. For negative translation, we change
+ // draw offset as it requires redraw (since more area of the scrim needs to be shown). For
+ // position translation, we simply translate the scrim down as it avoids invalidate and
+ // hence could be optimized by the platform.
+ float drawOffsetY = Math.min(translateY, 0);
+
+ if (newAlpha != mFillAlpha || drawOffsetY != mDrawOffsetY) {
+ invalidateDrawRect();
+
+ mFillAlpha = newAlpha;
+ mFillPaint.setAlpha(mFillAlpha);
+ mDrawOffsetY = drawOffsetY;
+ invalidateDrawRect();
+ }
+
+ setTranslationY(Math.max(translateY, 0));
+ }
+
+ private void invalidateDrawRect() {
+ mDrawRect.top = (int) (getHeight()
+ + mDrawOffsetY - mDrawHeight + mPadding.top - mShadowBlur - 0.5f);
+ invalidate(mDrawRect);
+ }
+
+ public void setDrawRegion(float height) {
+ mDrawHeight = height;
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ mInsets.set(insets);
+ DeviceProfile grid = initDefaults();
+ if (grid.isVerticalBarLayout()) {
+ mPadding.set(grid.workspacePadding);
+ mPadding.bottom = 0;
+ mPadding.left += mInsets.left;
+ mPadding.top = mInsets.top;
+ mPadding.right += mInsets.right;
+ setDrawRegion(0);
+ } else {
+ mPadding.setEmpty();
+ float scrimMargin = getResources().getDimension(R.dimen.all_apps_scrim_margin);
+ setDrawRegion(grid.hotseatBarSizePx + insets.bottom + scrimMargin);
+ }
+ updateDrawRect(grid);
+ invalidate();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ updateDrawRect(Launcher.getLauncher(getContext()).getDeviceProfile());
+ }
+
+ private void updateDrawRect(DeviceProfile grid) {
+ mDrawRect.bottom = getHeight();
+ if (grid.isVerticalBarLayout()) {
+ mDrawRect.left = (int) (mPadding.left - mShadowBlur - 0.5f);
+ mDrawRect.right = (int) (getWidth() - mPadding.right + 0.5f);
+ } else {
+ mDrawRect.left = 0;
+ mDrawRect.right = getWidth();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/BottomUserEducationView.java b/src/com/android/launcher3/views/BottomUserEducationView.java
new file mode 100644
index 0000000..ba78cf6
--- /dev/null
+++ b/src/com/android/launcher3/views/BottomUserEducationView.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.animation.PropertyValuesHolder;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.TouchDelegate;
+import android.view.View;
+import com.android.launcher3.Insettable;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.Interpolators;
+
+public class BottomUserEducationView extends AbstractSlideInView implements Insettable {
+
+ private static final String KEY_SHOWED_BOTTOM_USER_EDUCATION = "showed_bottom_user_education";
+
+ private static final int DEFAULT_CLOSE_DURATION = 200;
+
+ private final Rect mInsets = new Rect();
+
+ private View mCloseButton;
+
+ public BottomUserEducationView(Context context, AttributeSet attr) {
+ this(context, attr, 0);
+ }
+
+ public BottomUserEducationView(Context context, AttributeSet attrs,
+ int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ mContent = this;
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mCloseButton = findViewById(R.id.close_bottom_user_tip);
+ mCloseButton.setOnClickListener(view -> handleClose(true));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ setTranslationShift(mTranslationShift);
+ expandTouchAreaOfCloseButton();
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // Since this is on-boarding popup, it is not a user controlled action.
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_ON_BOARD_POPUP) != 0;
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ // Extend behind left, right, and bottom insets.
+ int leftInset = insets.left - mInsets.left;
+ int rightInset = insets.right - mInsets.right;
+ int bottomInset = insets.bottom - mInsets.bottom;
+ mInsets.set(insets);
+ setPadding(getPaddingLeft() + leftInset, getPaddingTop(),
+ getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ handleClose(animate, DEFAULT_CLOSE_DURATION);
+ if (animate) {
+ // We animate only when the user is visible, which is a proxy for an explicit
+ // close action.
+ mLauncher.getSharedPrefs().edit()
+ .putBoolean(KEY_SHOWED_BOTTOM_USER_EDUCATION, true).apply();
+ }
+ }
+
+ private void open(boolean animate) {
+ if (mIsOpen || mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ mIsOpen = true;
+ if (animate) {
+ mOpenCloseAnimator.setValues(
+ PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
+ mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mOpenCloseAnimator.start();
+ } else {
+ setTranslationShift(TRANSLATION_SHIFT_OPENED);
+ }
+ }
+
+ public static void showIfNeeded(Launcher launcher) {
+ if (launcher.getSharedPrefs().getBoolean(KEY_SHOWED_BOTTOM_USER_EDUCATION, false)) {
+ return;
+ }
+
+ LayoutInflater layoutInflater = LayoutInflater.from(launcher);
+ BottomUserEducationView bottomUserEducationView =
+ (BottomUserEducationView) layoutInflater.inflate(
+ R.layout.work_tab_bottom_user_education_view, launcher.getDragLayer(),
+ false);
+ launcher.getDragLayer().addView(bottomUserEducationView);
+ bottomUserEducationView.open(true);
+ }
+
+ private void expandTouchAreaOfCloseButton() {
+ Rect hitRect = new Rect();
+ mCloseButton.getHitRect(hitRect);
+ hitRect.left -= mCloseButton.getWidth();
+ hitRect.top -= mCloseButton.getHeight();
+ hitRect.right += mCloseButton.getWidth();
+ hitRect.bottom += mCloseButton.getHeight();
+ View parent = (View) mCloseButton.getParent();
+ parent.setTouchDelegate(new TouchDelegate(hitRect, mCloseButton));
+ }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
index 8f20a8d..fc121d3 100644
--- a/src/com/android/launcher3/views/RecyclerViewFastScroller.java
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -141,10 +141,11 @@
}
public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
- mRv = rv;
- if (mOnScrollListener != null) {
+ if (mRv != null && mOnScrollListener != null) {
mRv.removeOnScrollListener(mOnScrollListener);
}
+ mRv = rv;
+
mRv.addOnScrollListener(mOnScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
@@ -321,7 +322,7 @@
* Returns whether the specified point is inside the thumb bounds.
*/
private boolean isNearThumb(int x, int y) {
- int offset = y - mRv.getScrollBarTop() - mThumbOffsetY;
+ int offset = y - mThumbOffsetY;
return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
}
diff --git a/src/com/android/launcher3/views/SlidingTabStrip.java b/src/com/android/launcher3/views/SlidingTabStrip.java
deleted file mode 100644
index 45c6261..0000000
--- a/src/com/android/launcher3/views/SlidingTabStrip.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.views;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.Button;
-import android.widget.LinearLayout;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
-
-public class SlidingTabStrip extends LinearLayout {
-
- private final Paint mSelectedIndicatorPaint;
- private int mSelectedIndicatorHeight;
- private int mIndicatorLeft = -1;
- private int mIndicatorRight = -1;
- private int mSelectedPosition = -1;
- private float mSelectionOffset;
-
- public SlidingTabStrip(@NonNull Context context, @Nullable AttributeSet attrs) {
- super(context, attrs);
- setOrientation(HORIZONTAL);
- setWillNotDraw(false);
- mSelectedIndicatorPaint = new Paint();
- mSelectedIndicatorPaint.setColor(Themes.getAttrColor(context, android.R.attr.colorAccent));
- mSelectedIndicatorHeight = getResources()
- .getDimensionPixelSize(R.dimen.all_apps_tabs_indicator_height);
- }
-
- public void updateIndicatorPosition(int position, float positionOffset) {
- mSelectedPosition = position;
- mSelectionOffset = positionOffset;
- updateIndicatorPosition();
- }
-
- public void updateTabTextColor(int pos) {
- for (int i=0; i < getChildCount(); i++) {
- Button tab = (Button) getChildAt(i);
- tab.setSelected(i == pos);
- }
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- updateTabTextColor(0);
- updateIndicatorPosition(0, 0);
- }
-
- private void updateIndicatorPosition() {
- final View tab = getChildAt(mSelectedPosition);
- int left, right;
-
- if (tab != null && tab.getWidth() > 0) {
- left = tab.getLeft();
- right = tab.getRight();
-
- if (mSelectionOffset > 0f && mSelectedPosition < getChildCount() - 1) {
- // Draw the selection partway between the tabs
- View nextTitle = getChildAt(mSelectedPosition + 1);
- left = (int) (mSelectionOffset * nextTitle.getLeft() +
- (1.0f - mSelectionOffset) * left);
- right = (int) (mSelectionOffset * nextTitle.getRight() +
- (1.0f - mSelectionOffset) * right);
- }
- } else {
- left = right = -1;
- }
-
- setIndicatorPosition(left, right);
- }
-
- private void setIndicatorPosition(int left, int right) {
- if (left != mIndicatorLeft || right != mIndicatorRight) {
- mIndicatorLeft = left;
- mIndicatorRight = right;
- invalidate();
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawRect(mIndicatorLeft, getHeight() - mSelectedIndicatorHeight,
- mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
- }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/views/TopRoundedCornerView.java b/src/com/android/launcher3/views/TopRoundedCornerView.java
new file mode 100644
index 0000000..ba223c4
--- /dev/null
+++ b/src/com/android/launcher3/views/TopRoundedCornerView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.R;
+
+/**
+ * View with top rounded corners.
+ */
+public class TopRoundedCornerView extends FrameLayout {
+
+ private final RectF mRect = new RectF();
+ private final Path mClipPath = new Path();
+ private float[] mRadii;
+
+ public TopRoundedCornerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ int radius = getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
+ mRadii = new float[] {radius, radius, radius, radius, 0, 0, 0, 0};
+ }
+
+ public TopRoundedCornerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.save();
+ canvas.clipPath(mClipPath);
+ super.draw(canvas);
+ canvas.restore();
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ mRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ mClipPath.reset();
+ mClipPath.addRoundRect(mRect, mRadii, Path.Direction.CW);
+ }
+}
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
new file mode 100644
index 0000000..fb17b4f
--- /dev/null
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.views;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+/**
+ * Container to show work footer in all-apps.
+ */
+public class WorkFooterContainer extends RelativeLayout {
+
+ public WorkFooterContainer(Context context) {
+ super(context);
+ }
+
+ public WorkFooterContainer(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public WorkFooterContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ updateTranslation();
+ }
+
+ @Override
+ public void offsetTopAndBottom(int offset) {
+ super.offsetTopAndBottom(offset);
+ updateTranslation();
+ }
+
+ private void updateTranslation() {
+ if (getParent() instanceof View) {
+ View parent = (View) getParent();
+ int availableBot = parent.getHeight() - parent.getPaddingBottom();
+ setTranslationY(Math.max(0, availableBot - getBottom()));
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index e328759..b22509c 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,94 +15,43 @@
*/
package com.android.launcher3.widget;
-import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Point;
import android.util.AttributeSet;
-import android.util.Property;
-import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
-import android.view.animation.Interpolator;
import android.widget.Toast;
-import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.GradientView;
-import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.views.AbstractSlideInView;
/**
* Base class for various widgets popup
*/
-abstract class BaseWidgetSheet extends AbstractFloatingView
- implements OnClickListener, OnLongClickListener, DragSource, SwipeDetector.Listener {
+abstract class BaseWidgetSheet extends AbstractSlideInView
+ implements OnClickListener, OnLongClickListener, DragSource {
- protected static Property<BaseWidgetSheet, Float> TRANSLATION_SHIFT =
- new Property<BaseWidgetSheet, Float>(Float.class, "translationShift") {
-
- @Override
- public Float get(BaseWidgetSheet view) {
- return view.mTranslationShift;
- }
-
- @Override
- public void set(BaseWidgetSheet view, Float value) {
- view.setTranslationShift(value);
- }
- };
- protected static final float TRANSLATION_SHIFT_CLOSED = 1f;
- protected static final float TRANSLATION_SHIFT_OPENED = 0f;
-
/* Touch handling related member variables. */
private Toast mWidgetInstructionToast;
- protected final Launcher mLauncher;
- protected final SwipeDetector mSwipeDetector;
- protected final ObjectAnimator mOpenCloseAnimator;
-
- protected View mContent;
protected GradientView mGradientView;
- protected Interpolator mScrollInterpolator;
-
- // range [0, 1], 0=> completely open, 1=> completely closed
- protected float mTranslationShift = TRANSLATION_SHIFT_CLOSED;
-
- protected boolean mNoIntercept;
public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mLauncher = Launcher.getLauncher(context);
-
- mScrollInterpolator = Interpolators.SCROLL_CUBIC;
- mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
-
- mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mSwipeDetector.finishedScrolling();
- }
- });
}
@Override
@@ -130,9 +79,8 @@
}
protected void setTranslationShift(float translationShift) {
- mTranslationShift = translationShift;
+ super.setTranslationShift(translationShift);
mGradientView.setAlpha(1 - mTranslationShift);
- mContent.setTranslationY(mTranslationShift * mContent.getHeight());
}
private boolean beginDraggingWidget(WidgetCell v) {
@@ -163,94 +111,12 @@
public void onDropCompleted(View target, DragObject d, boolean success) { }
- @Override
- public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_UP && !mNoIntercept) {
- // If we got ACTION_UP without ever returning true on intercept,
- // the user never started dragging the bottom sheet.
- if (!mLauncher.getDragLayer().isEventOverView(mContent, ev)) {
- close(true);
- return false;
- }
- }
-
- if (mNoIntercept) {
- return false;
- }
-
- int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
- SwipeDetector.DIRECTION_NEGATIVE : 0;
- mSwipeDetector.setDetectableScrollConditions(
- directionsToDetectScroll, false);
- mSwipeDetector.onTouchEvent(ev);
- return mSwipeDetector.isDraggingOrSettling();
- }
-
- @Override
- public boolean onControllerTouchEvent(MotionEvent ev) {
- return mSwipeDetector.onTouchEvent(ev);
- }
-
- /* SwipeDetector.Listener */
-
- @Override
- public void onDragStart(boolean start) { }
-
- @Override
- public boolean onDrag(float displacement, float velocity) {
- float range = mContent.getHeight();
- displacement = Utilities.boundToRange(displacement, 0, range);
- setTranslationShift(displacement / range);
- return true;
- }
-
- @Override
- public void onDragEnd(float velocity, boolean fling) {
- if ((fling && velocity > 0) || mTranslationShift > 0.5f) {
- mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
- mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(
- velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
- close(true);
- } else {
- mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
- TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
- mOpenCloseAnimator.setDuration(
- SwipeDetector.calculateDuration(velocity, mTranslationShift))
- .setInterpolator(Interpolators.DEACCEL);
- mOpenCloseAnimator.start();
- }
- }
-
- protected void handleClose(boolean animate, long defaultDuration) {
- if (!mIsOpen || mOpenCloseAnimator.isRunning()) {
- return;
- }
- if (animate) {
- mOpenCloseAnimator.setValues(
- PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_CLOSED));
- mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onCloseComplete();
- }
- });
- if (mSwipeDetector.isIdleState()) {
- mOpenCloseAnimator
- .setDuration(defaultDuration)
- .setInterpolator(Interpolators.ACCEL);
- } else {
- mOpenCloseAnimator.setInterpolator(mScrollInterpolator);
- }
- mOpenCloseAnimator.start();
- } else {
- setTranslationShift(TRANSLATION_SHIFT_CLOSED);
- onCloseComplete();
- }
- }
-
protected void onCloseComplete() {
- mIsOpen = false;
- mLauncher.getDragLayer().removeView(this);
+ super.onCloseComplete();
+ clearNavBarColor();
+ }
+
+ protected void clearNavBarColor() {
mLauncher.getSystemUiController().updateUiState(
SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
}
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
new file mode 100644
index 0000000..37e5efc
--- /dev/null
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.TypedValue;
+import android.widget.RemoteViews;
+
+import com.android.launcher3.R;
+
+/**
+ * A widget host views created while the host has not bind to the system service.
+ */
+public class DeferredAppWidgetHostView extends LauncherAppWidgetHostView {
+
+ private final TextPaint mPaint;
+ private Layout mSetupTextLayout;
+
+ public DeferredAppWidgetHostView(Context context) {
+ super(context);
+ setWillNotDraw(false);
+
+ mPaint = new TextPaint();
+ mPaint.setColor(Color.WHITE);
+ mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
+ mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+ setBackgroundResource(R.drawable.bg_deferred_app_widget);
+ }
+
+ @Override
+ public void updateAppWidget(RemoteViews remoteViews) {
+ // Not allowed
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+ AppWidgetProviderInfo info = getAppWidgetInfo();
+ if (info == null || TextUtils.isEmpty(info.label)) {
+ return;
+ }
+
+ // Use double padding so that there is extra space between background and text
+ int availableWidth = getMeasuredWidth() - 2 * (getPaddingLeft() + getPaddingRight());
+ if (mSetupTextLayout != null && mSetupTextLayout.getText().equals(info.label)
+ && mSetupTextLayout.getWidth() == availableWidth) {
+ return;
+ }
+ mSetupTextLayout = new StaticLayout(info.label, mPaint, availableWidth,
+ Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mSetupTextLayout != null) {
+ canvas.translate(getPaddingLeft() * 2,
+ (getHeight() - mSetupTextLayout.getHeight()) / 2);
+ mSetupTextLayout.draw(canvas);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
similarity index 92%
rename from src/com/android/launcher3/LauncherAppWidgetHostView.java
rename to src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 6f953e5..0b1474a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.widget;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
@@ -37,6 +37,15 @@
import android.widget.Advanceable;
import android.widget.RemoteViews;
+import com.android.launcher3.CheckLongPressHelper;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.SimpleOnStylusPressListener;
+import com.android.launcher3.StylusEventHelper;
+import com.android.launcher3.Utilities;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
@@ -59,14 +68,10 @@
private final CheckLongPressHelper mLongPressHelper;
private final StylusEventHelper mStylusEventHelper;
- private final Launcher mLauncher;
-
- private static final int DONT_REINFLATE = 0;
- private static final int REINFLATE_ON_RESUME = 1;
- private static final int REINFLATE_ON_CONFIG_CHANGE = 2;
+ protected final Launcher mLauncher;
@ViewDebug.ExportedProperty(category = "launcher")
- private int mReinflateStatus;
+ private boolean mReinflateOnConfigChange;
private float mSlop;
@@ -128,12 +133,7 @@
// Consequently, the widgets will be inflated for the orientation of the foreground activity
// (framework issue). On resuming, we ensure that any widgets are inflated for the current
// orientation.
- if (mReinflateStatus == DONT_REINFLATE && !isSameOrientation()) {
- mReinflateStatus = REINFLATE_ON_RESUME;
- if (!mLauncher.waitUntilResume(new ReInflateRunnable())) {
- mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
- }
- }
+ mReinflateOnConfigChange = !isSameOrientation();
}
private boolean isSameOrientation() {
@@ -485,40 +485,20 @@
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mReinflateStatus == REINFLATE_ON_CONFIG_CHANGE) {
- // We are finally in the same orientation
- reinflateIfNecessary();
+ // Only reinflate when the final configuration is same as the required configuration
+ if (mReinflateOnConfigChange && isSameOrientation()) {
+ mReinflateOnConfigChange = false;
+ if (isAttachedToWindow()) {
+ reInflate();
+ }
}
}
- private void reinflateIfNecessary() {
- if (!isSameOrientation()) {
- // We cannot reinflate yet, wait until next config change
- mReinflateStatus = REINFLATE_ON_CONFIG_CHANGE;
- return;
- }
-
- mReinflateStatus = DONT_REINFLATE;
- if (isAttachedToWindow()) {
- LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
- reinflate();
- }
- }
-
- public void reinflate() {
+ public void reInflate() {
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
// Remove and rebind the current widget (which was inflated in the wrong
// orientation), but don't delete it from the database
mLauncher.removeItem(this, info, false /* deleteFromDb */);
mLauncher.bindAppWidget(info);
}
-
- private class ReInflateRunnable implements Runnable {
- @Override
- public void run() {
- if (mReinflateStatus == REINFLATE_ON_RESUME) {
- reinflateIfNecessary();
- }
- }
- }
}
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
similarity index 92%
rename from src/com/android/launcher3/PendingAppWidgetHostView.java
rename to src/com/android/launcher3/widget/PendingAppWidgetHostView.java
index b86d413..6970833 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/PendingAppWidgetHostView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.widget;
import android.content.Context;
import android.graphics.Bitmap;
@@ -32,10 +32,19 @@
import android.view.View;
import android.view.View.OnClickListener;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.IconCache;
import com.android.launcher3.IconCache.ItemInfoUpdateReceiver;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView
implements OnClickListener, ItemInfoUpdateReceiver {
@@ -48,9 +57,6 @@
private final LauncherAppWidgetInfo mInfo;
private final int mStartState;
private final boolean mDisabledForSafeMode;
- private Launcher mLauncher;
-
- private Bitmap mIcon;
private Drawable mCenterDrawable;
private Drawable mSettingIconDrawable;
@@ -64,7 +70,6 @@
IconCache cache, boolean disabledForSafeMode) {
super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
- mLauncher = Launcher.getLauncher(context);
mInfo = info;
mStartState = info.restoreStatus;
mDisabledForSafeMode = disabledForSafeMode;
@@ -122,53 +127,44 @@
@Override
public void reapplyItemInfo(ItemInfoWithIcon info) {
- Bitmap icon = info.iconBitmap;
- if (mIcon == icon) {
- return;
- }
- mIcon = icon;
if (mCenterDrawable != null) {
mCenterDrawable.setCallback(null);
mCenterDrawable = null;
}
- if (mIcon != null) {
+ if (info.iconBitmap != null) {
// The view displays three modes,
// 1) App icon in the center
// 2) Preload icon in the center
// 3) Setup icon in the center and app icon in the top right corner.
DrawableFactory drawableFactory = DrawableFactory.get(getContext());
if (mDisabledForSafeMode) {
- FastBitmapDrawable disabledIcon = drawableFactory.newIcon(mIcon, mInfo);
+ FastBitmapDrawable disabledIcon = drawableFactory.newIcon(info);
disabledIcon.setIsDisabled(true);
mCenterDrawable = disabledIcon;
mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
- mCenterDrawable = drawableFactory.newIcon(mIcon, mInfo);
+ mCenterDrawable = drawableFactory.newIcon(info);
mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
-
- updateSettingColor();
+ updateSettingColor(info.iconColor);
} else {
mCenterDrawable = DrawableFactory.get(getContext())
- .newPendingIcon(mIcon, getContext());
- mCenterDrawable.setCallback(this);
+ .newPendingIcon(info, getContext());
mSettingIconDrawable = null;
applyState();
}
+ mCenterDrawable.setCallback(this);
mDrawableSizeChanged = true;
}
invalidate();
}
- private void updateSettingColor() {
- int color = Utilities.findDominantColorByHue(mIcon, 20);
+ private void updateSettingColor(int dominantColor) {
// Make the dominant color bright.
float[] hsv = new float[3];
- Color.colorToHSV(color, hsv);
+ Color.colorToHSV(dominantColor, hsv);
hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
hsv[2] = 1;
- color = Color.HSVToColor(hsv);
-
- mSettingIconDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ mSettingIconDrawable.setColorFilter(Color.HSVToColor(hsv), PorterDuff.Mode.SRC_IN);
}
@Override
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index c5cf5e2..aa5b785 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -113,7 +113,9 @@
} else {
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
- preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0);
+ LauncherIcons li = LauncherIcons.obtain(launcher);
+ preview = li.createScaledBitmapWithoutShadow(icon, 0);
+ li.recycle();
scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
dragOffset = new Point(previewPadding / 2, previewPadding / 2);
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 7fa5ff0..6a9013d 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -57,7 +57,7 @@
mInsets = new Rect();
mGradientView = (GradientView) mLauncher.getLayoutInflater().inflate(
- R.layout.gradient_bg, mLauncher.getDragLayer(), false);
+ R.layout.widgets_bottom_sheet_scrim, mLauncher.getDragLayer(), false);
mGradientView.setProgress(1, false);
mContent = this;
}
diff --git a/src/com/android/launcher3/widget/WidgetsFullSheet.java b/src/com/android/launcher3/widget/WidgetsFullSheet.java
index a40ea1b..e461afc 100644
--- a/src/com/android/launcher3/widget/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsFullSheet.java
@@ -42,8 +42,6 @@
private static final long FADE_IN_DURATION = 150;
private static final float VERTICAL_START_POSITION = 0.3f;
- private static final Rect sTempRect = new Rect();
-
private final Rect mInsets = new Rect();
private final WidgetsListAdapter mAdapter;
@@ -102,6 +100,8 @@
mRecyclerView.getPaddingRight(), insets.bottom);
if (insets.bottom > 0) {
setupNavBarColor();
+ } else {
+ clearNavBarColor();
}
requestLayout();
}
@@ -115,8 +115,9 @@
mGradientView.setVisibility(View.INVISIBLE);
widthUsed = 0;
} else {
- mLauncher.getDeviceProfile().getWorkspacePadding(sTempRect);
- widthUsed = Math.max(sTempRect.left + sTempRect.right,
+ mGradientView.setVisibility(View.VISIBLE);
+ Rect padding = mLauncher.getDeviceProfile().workspacePadding;
+ widthUsed = Math.max(padding.left + padding.right,
2 * (mInsets.left + mInsets.right));
}
diff --git a/src/com/android/launcher3/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
similarity index 63%
rename from src/com/android/launcher3/states/AllAppsState.java
rename to src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
index ed3023a..fd33ee1 100644
--- a/src/com/android/launcher3/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.states;
+package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
+import static com.android.launcher3.allapps.DiscoveryBounce.APPS_VIEW_SHOWN;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import android.view.View;
@@ -23,6 +25,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
/**
@@ -30,12 +33,17 @@
*/
public class AllAppsState extends LauncherState {
- public static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
-
private static final int STATE_FLAGS = FLAG_DISABLE_ACCESSIBILITY;
+ private static final PageAlphaProvider PAGE_ALPHA_PROVIDER = new PageAlphaProvider(DEACCEL_2) {
+ @Override
+ public float getPageAlpha(int pageIndex) {
+ return 0;
+ }
+ };
+
public AllAppsState(int id) {
- super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, 0f, STATE_FLAGS);
+ super(id, ContainerType.ALLAPPS, ALL_APPS_TRANSITION_MS, STATE_FLAGS);
}
@Override
@@ -54,7 +62,29 @@
}
@Override
+ public float getHoseatAlpha(Launcher launcher) {
+ return 0;
+ }
+
+ @Override
public View getFinalFocus(Launcher launcher) {
return launcher.getAppsView();
}
+
+ @Override
+ public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+ return new float[] { 1f, 0,
+ -launcher.getAllAppsController().getShiftRange()
+ * AllAppsTransitionController.PARALLAX_COEFFICIENT};
+ }
+
+ @Override
+ public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
+ return PAGE_ALPHA_PROVIDER;
+ }
+
+ @Override
+ public float getVerticalProgress(Launcher launcher) {
+ return 0f;
+ }
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
new file mode 100644
index 0000000..76b7e0d
--- /dev/null
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.VerticalSwipeController;
+
+/**
+ * Extension of {@link VerticalSwipeController} to switch between NORMAL and ALL_APPS state.
+ */
+public class AllAppsSwipeController extends VerticalSwipeController {
+
+ private int mStartContainerType;
+
+ public AllAppsSwipeController(Launcher l) {
+ super(l, NORMAL);
+ }
+
+ @Override
+ protected boolean shouldInterceptTouch(MotionEvent ev) {
+ if (!mLauncher.isInState(NORMAL) && !mLauncher.isInState(ALL_APPS)) {
+ // Don't listen for the swipe gesture if we are already in some other state.
+ return false;
+ }
+
+ if (mLauncher.isInState(ALL_APPS) && !mLauncher.getAppsView().shouldContainerScroll(ev)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected int getSwipeDirection(MotionEvent ev) {
+ if (mLauncher.isInState(ALL_APPS)) {
+ mStartContainerType = ContainerType.ALLAPPS;
+ return SwipeDetector.DIRECTION_NEGATIVE;
+ } else {
+ mStartContainerType = mLauncher.getDragLayer().isEventOverHotseat(ev) ?
+ ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+ return SwipeDetector.DIRECTION_POSITIVE;
+ }
+ }
+
+ @Override
+ protected void onTransitionComplete(boolean wasFling, boolean stateChanged) {
+ if (stateChanged) {
+ // Transition complete. log the action
+ mLauncher.getUserEventDispatcher().logActionOnContainer(
+ wasFling ? Touch.FLING : Touch.SWIPE,
+ mLauncher.isInState(ALL_APPS) ? Direction.UP : Direction.DOWN,
+ mStartContainerType,
+ mLauncher.getWorkspace().getCurrentPage());
+ }
+ }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
index 3ce1014..4168e11 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewPanel.java
@@ -17,7 +17,6 @@
import static com.android.launcher3.WorkspaceStateTransitionAnimation.NO_ANIM_PROPERTY_SETTER;
-import android.animation.AnimatorSet;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -37,7 +36,8 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.WorkspaceStateTransitionAnimation.AnimatedPropertySetter;
import com.android.launcher3.WorkspaceStateTransitionAnimation.PropertySetter;
-import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.widget.WidgetsFullSheet;
@@ -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));
+ getContext().startActivity(intent, mLauncher.getActivityLaunchOptionsAsBundle(v, false));
}
@Override
@@ -168,19 +168,13 @@
}
@Override
- public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
- AnimatorSet anim, AnimationConfig config) {
- setState(toState, new AnimatedPropertySetter(config.duration, layerViews, anim));
+ public void setStateWithAnimation(LauncherState toState,
+ AnimatorSetBuilder builder, AnimationConfig config) {
+ setState(toState, new AnimatedPropertySetter(config.duration, builder));
}
private void setState(LauncherState state, PropertySetter setter) {
- boolean isOverview = state == LauncherState.OVERVIEW;
- float finalHotseatAlpha = isOverview ? 0 : 1;
-
- setter.setViewAlpha(null, this, isOverview ? 1 : 0);
- setter.setViewAlpha(
- mLauncher.getWorkspace().createHotseatAlphaAnimator(finalHotseatAlpha),
- mLauncher.getHotseat(), finalHotseatAlpha);
+ setter.setViewAlpha(this, 1f - state.getHoseatAlpha(mLauncher), Interpolators.ACCEL);
}
public static int getButtonBarHeight(Launcher launcher) {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
index dcf7453..d18901d 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,7 +16,7 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import android.graphics.Rect;
import android.view.View;
@@ -36,10 +36,11 @@
// The percent to shrink the workspace during overview mode
private static final float SCALE_FACTOR = 0.7f;
- private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE;
+ private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE |
+ FLAG_DISABLE_PAGE_CLIPPING;
public OverviewState(int id) {
- super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, 1f, STATE_FLAGS);
+ super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
}
@Override
@@ -50,15 +51,19 @@
int overviewButtonBarHeight = OverviewPanel.getButtonBarHeight(launcher);
int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight());
- Rect workspacePadding = grid.getWorkspacePadding(null);
- int workspaceTop = insets.top + workspacePadding.top;
- int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom;
+ int workspaceTop = insets.top + grid.workspacePadding.top;
+ int workspaceBottom = ws.getHeight() - insets.bottom - grid.workspacePadding.bottom;
int overviewTop = insets.top;
- int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight;
+ int overviewBottom = ws.getHeight() - insets.bottom - overviewButtonBarHeight;
int workspaceOffsetTopEdge =
workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
- return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
+ return new float[] {SCALE_FACTOR, 0, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
+ }
+
+ @Override
+ public float getHoseatAlpha(Launcher launcher) {
+ return 0;
}
@Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java b/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java
index 40bf057..a7c8cee 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/PinchToOverviewListener.java
@@ -19,7 +19,7 @@
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index 51cf661..64a29ea 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,20 +16,28 @@
package com.android.launcher3.uioverrides;
+import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.view.View;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.VerticalSwipeController;
+import com.android.launcher3.graphics.BitmapRenderer;
import com.android.launcher3.util.TouchController;
public class UiFactory {
+ public static final boolean USE_HARDWARE_BITMAP = false;
+
public static TouchController[] createTouchControllers(Launcher launcher) {
return new TouchController[] {
- new VerticalSwipeController(launcher), new PinchToOverviewListener(launcher)};
+ new AllAppsSwipeController(launcher), new PinchToOverviewListener(launcher)};
}
public static AccessibilityDelegate newPageIndicatorAccessibilityDelegate() {
@@ -42,7 +50,18 @@
launcher.getAllAppsController(), launcher.getWorkspace() };
}
- public static void onWorkspaceLongPress(Launcher launcher) {
+ public static void onWorkspaceLongPress(Launcher launcher, PointF touchPoint) {
launcher.getStateManager().goToState(OVERVIEW);
}
+
+ public static Bitmap createFromRenderer(int width, int height, boolean forceSoftwareRenderer,
+ BitmapRenderer renderer) {
+ Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ renderer.render(new Canvas(result));
+ return result;
+ }
+
+ public static void resetOverview(Launcher launcher) { }
+
+ public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
}
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index bbb6772..cf90afd 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -9,12 +9,12 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
+import android.graphics.Color;
import android.os.Process;
import android.os.UserHandle;
import android.support.annotation.NonNull;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.provider.ProviderTestRule;
-import android.support.test.runner.AndroidJUnit4;
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppFilter;
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.graphics.BitmapInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TestLauncherProvider;
@@ -208,7 +209,7 @@
CacheEntry entry = mCache.get(new ComponentKey(componentName, user));
if (entry == null) {
entry = new CacheEntry();
- entry.icon = getDefaultIcon(user);
+ getDefaultIcon(user).applyTo(entry);
}
return entry;
}
@@ -216,6 +217,7 @@
public void addCache(ComponentName key, String title) {
CacheEntry entry = new CacheEntry();
entry.icon = newIcon();
+ entry.color = Color.RED;
entry.title = title;
mCache.put(new ComponentKey(key, Process.myUserHandle()), entry);
}
@@ -225,8 +227,8 @@
}
@Override
- protected Bitmap makeDefaultIcon(UserHandle user) {
- return newIcon();
+ protected BitmapInfo makeDefaultIcon(UserHandle user) {
+ return BitmapInfo.fromBitmap(newIcon());
}
}
}
diff --git a/tests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
index 173c556..dfefa31 100644
--- a/tests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -17,6 +17,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.graphics.BitmapInfo;
import org.junit.Before;
import org.junit.Test;
@@ -138,7 +139,8 @@
assertTrue(mLoaderCursor.moveToNext());
Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
- when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user))).thenReturn(icon);
+ when(mMockIconCache.getDefaultIcon(eq(mLoaderCursor.user)))
+ .thenReturn(BitmapInfo.fromBitmap(icon));
ShortcutInfo info = mLoaderCursor.loadSimpleShortcut();
assertEquals(icon, info.iconBitmap);
assertEquals("my-shortcut", info.title);
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index 87103d7..32f90a6 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -30,12 +30,12 @@
import android.support.test.uiautomator.UiSelector;
import com.android.launcher3.LauncherAppWidgetHost;
-import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.PendingAppWidgetHostView;
+import com.android.launcher3.widget.PendingAppWidgetHostView;
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.PackageInstallerCompat;