Import translations. DO NOT MERGE
am: 0256289bf6 -s ours
Change-Id: I16a31eb9135752d2db6a1b4b560d76efdc0f9ae8
diff --git a/Android.mk b/Android.mk
index 713d082..c8a53d2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,11 +26,13 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
android-support-v7-recyclerview \
- android-support-v7-palette
+ android-support-v7-palette \
+ android-support-dynamic-animation
LOCAL_SRC_FILES := \
$(call all-java-files-under, src) \
$(call all-java-files-under, src_config) \
+ $(call all-java-files-under, src_flags) \
$(call all-proto-files-under, protos)
LOCAL_RESOURCE_DIR := \
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index ab582fe..ad404c0 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -86,6 +86,12 @@
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
+ <service
+ android:name="com.android.launcher3.compat.WallpaperManagerCompatVL$ColorExtractionService"
+ android:exported="false"
+ android:process=":wallpaper_chooser"
+ android:permission="android.permission.BIND_JOB_SERVICE" />
+
<service android:name="com.android.launcher3.notification.NotificationListener"
android:enabled="@bool/notification_badging_enabled"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
diff --git a/build.gradle b/build.gradle
index 9c71693..c23a299 100644
--- a/build.gradle
+++ b/build.gradle
@@ -39,7 +39,7 @@
sourceSets {
main {
res.srcDirs = ['res']
- java.srcDirs = ['src', 'src_config']
+ java.srcDirs = ['src', 'src_flags']
manifest.srcFile 'AndroidManifest-common.xml'
proto.srcDirs 'protos/'
}
@@ -68,6 +68,7 @@
final String SUPPORT_LIBS_VERSION = '26.0.0-SNAPSHOT'
dependencies {
compile "com.android.support:support-v4:${SUPPORT_LIBS_VERSION}"
+ compile "com.android.support:support-dynamic-animation:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:recyclerview-v7:${SUPPORT_LIBS_VERSION}"
compile "com.android.support:palette-v7:${SUPPORT_LIBS_VERSION}"
compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
@@ -92,6 +93,7 @@
remove java
javanano {
option "java_package=launcher_log.proto|com.android.launcher3.userevent.nano"
+ option "java_package=launcher_dump.proto|com.android.launcher3.model.nano"
option "enum_style=java"
}
}
diff --git a/proguard.flags b/proguard.flags
index 8ee6ccd..51abcca 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -82,6 +82,10 @@
*;
}
+-keep class com.android.launcher3.graphics.ShadowDrawable {
+ public <init>(...);
+}
+
# Proguard will strip methods required for talkback to properly scroll to
# next row when focus is on the last item of last row when using a RecyclerView
# Keep optimized and shrunk proguard to prevent issues like this when using
diff --git a/res/color-v24/all_apps_bg_hand_fill.xml b/res/color-v24/all_apps_bg_hand_fill.xml
new file mode 100644
index 0000000..1b0b538
--- /dev/null
+++ b/res/color-v24/all_apps_bg_hand_fill.xml
@@ -0,0 +1,27 @@
+<?xml 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.
+-->
+<gradient
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:startX="158.5"
+ android:startY="141.5"
+ android:endX="196.0"
+ android:endY="206.5"
+ android:type="linear" >
+ <item android:offset="0" android:color="#E1E1E1" />
+ <item android:offset="0.3317" android:color="#E1E1E1" />
+ <item android:offset="0.493" android:color="#C1E5E5E5" />
+ <item android:offset="1" android:color="#00EEEEEE" />
+</gradient>
\ No newline at end of file
diff --git a/res/color-v24/all_apps_bg_hand_fill_dark.xml b/res/color-v24/all_apps_bg_hand_fill_dark.xml
new file mode 100644
index 0000000..bb9c71c
--- /dev/null
+++ b/res/color-v24/all_apps_bg_hand_fill_dark.xml
@@ -0,0 +1,28 @@
+<?xml 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.
+-->
+<gradient
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:startX="158.5"
+ android:startY="141.5"
+ android:endX="196.0"
+ android:endY="206.5"
+ android:type="linear" >
+ <item android:offset="0" android:color="#9AA0A6" />
+ <item android:offset="0.1013" android:color="#E5A5ABB0" />
+ <item android:offset="0.4946" android:color="#81CDCFD1" />
+ <item android:offset="0.8079" android:color="#31E5E6E6" />
+ <item android:offset="1" android:color="#00EEEEEE" />
+</gradient>
\ No newline at end of file
diff --git a/res/drawable-hdpi/all_apps_alpha_mask.png b/res/drawable-hdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..01e9e56
--- /dev/null
+++ b/res/drawable-hdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_hand.png b/res/drawable-hdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 437fd37..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_info_launcher.png b/res/drawable-hdpi/ic_info_launcher.png
deleted file mode 100644
index 11162e1..0000000
--- a/res/drawable-hdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove_launcher.png b/res/drawable-hdpi/ic_remove_launcher.png
deleted file mode 100644
index ad2b9af..0000000
--- a/res/drawable-hdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_uninstall_launcher.png b/res/drawable-hdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 426683c..0000000
--- a/res/drawable-hdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml b/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
deleted file mode 100644
index 2bbf5cd..0000000
--- a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
+++ /dev/null
@@ -1,27 +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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent" />
- <size
- android:width="64dp"
- android:height="64dp" />
- <corners
- android:topLeftRadius="64dp"
- android:topRightRadius="64dp"
- android:bottomRightRadius="64dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable-mdpi/all_apps_alpha_mask.png b/res/drawable-mdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..f24e71d
--- /dev/null
+++ b/res/drawable-mdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_hand.png b/res/drawable-mdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 0a00241..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_info_launcher.png b/res/drawable-mdpi/ic_info_launcher.png
deleted file mode 100644
index 6fbe5e3..0000000
--- a/res/drawable-mdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove_launcher.png b/res/drawable-mdpi/ic_remove_launcher.png
deleted file mode 100644
index 2bb281d..0000000
--- a/res/drawable-mdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_uninstall_launcher.png b/res/drawable-mdpi/ic_uninstall_launcher.png
deleted file mode 100644
index bfcbc6df..0000000
--- a/res/drawable-mdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bg_white_round_rect.xml b/res/drawable-v24/ic_info_shadow.xml
similarity index 69%
rename from res/drawable/bg_white_round_rect.xml
rename to res/drawable-v24/ic_info_shadow.xml
index c7f786f..1fe2c46 100644
--- a/res/drawable/bg_white_round_rect.xml
+++ b/res/drawable-v24/ic_info_shadow.xml
@@ -5,7 +5,7 @@
you may not use this 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,9 +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="#FFFFFF" />
- <corners android:radius="@dimen/bg_round_rect_radius" />
-</shape>
\ No newline at end of file
+<com.android.launcher3.graphics.ShadowDrawable
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_info_no_shadow"
+ android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/layout/qsb_blocker_view.xml b/res/drawable-v24/ic_remove_shadow.xml
similarity index 69%
rename from res/layout/qsb_blocker_view.xml
rename to res/drawable-v24/ic_remove_shadow.xml
index 453eebe..48abc10 100644
--- a/res/layout/qsb_blocker_view.xml
+++ b/res/drawable-v24/ic_remove_shadow.xml
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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,
@@ -14,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.qsb.QsbBlockerView
+<com.android.launcher3.graphics.ShadowDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
\ No newline at end of file
+ android:src="@drawable/ic_remove_no_shadow"
+ android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/layout/qsb_blocker_view.xml b/res/drawable-v24/ic_uninstall_shadow.xml
similarity index 69%
copy from res/layout/qsb_blocker_view.xml
copy to res/drawable-v24/ic_uninstall_shadow.xml
index 453eebe..b441b0e 100644
--- a/res/layout/qsb_blocker_view.xml
+++ b/res/drawable-v24/ic_uninstall_shadow.xml
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2016 The Android Open Source Project
+<!-- Copyright (C) 2017 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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,
@@ -14,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.qsb.QsbBlockerView
+<com.android.launcher3.graphics.ShadowDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
\ No newline at end of file
+ android:src="@drawable/ic_uninstall_no_shadow"
+ android:elevation="@dimen/drop_target_shadow_elevation" />
diff --git a/res/drawable-xhdpi/all_apps_alpha_mask.png b/res/drawable-xhdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..f479433
--- /dev/null
+++ b/res/drawable-xhdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_hand.png b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 1acb378..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_info_launcher.png b/res/drawable-xhdpi/ic_info_launcher.png
deleted file mode 100644
index 041f2b3..0000000
--- a/res/drawable-xhdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove_launcher.png b/res/drawable-xhdpi/ic_remove_launcher.png
deleted file mode 100644
index ff94eb8..0000000
--- a/res/drawable-xhdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_uninstall_launcher.png b/res/drawable-xhdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 2c7ab56..0000000
--- a/res/drawable-xhdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/all_apps_alpha_mask.png b/res/drawable-xxhdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..d5facd7
--- /dev/null
+++ b/res/drawable-xxhdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 09c6c8d..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_info_launcher.png b/res/drawable-xxhdpi/ic_info_launcher.png
deleted file mode 100644
index 8e602da..0000000
--- a/res/drawable-xxhdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove_launcher.png b/res/drawable-xxhdpi/ic_remove_launcher.png
deleted file mode 100644
index 78ca080..0000000
--- a/res/drawable-xxhdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_uninstall_launcher.png b/res/drawable-xxhdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 43aba6e..0000000
--- a/res/drawable-xxhdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/all_apps_alpha_mask.png b/res/drawable-xxxhdpi/all_apps_alpha_mask.png
new file mode 100755
index 0000000..ed53ff9
--- /dev/null
+++ b/res/drawable-xxxhdpi/all_apps_alpha_mask.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
deleted file mode 100644
index 49c004d..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_info_launcher.png b/res/drawable-xxxhdpi/ic_info_launcher.png
deleted file mode 100644
index 3540de1..0000000
--- a/res/drawable-xxxhdpi/ic_info_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_remove_launcher.png b/res/drawable-xxxhdpi/ic_remove_launcher.png
deleted file mode 100644
index 418d81a..0000000
--- a/res/drawable-xxxhdpi/ic_remove_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_uninstall_launcher.png b/res/drawable-xxxhdpi/ic_uninstall_launcher.png
deleted file mode 100644
index 724437a..0000000
--- a/res/drawable-xxxhdpi/ic_uninstall_launcher.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/container_fastscroll_popup_bg.xml b/res/drawable/container_fastscroll_popup_bg.xml
deleted file mode 100644
index 3dc7680..0000000
--- a/res/drawable/container_fastscroll_popup_bg.xml
+++ /dev/null
@@ -1,27 +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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent" />
- <size
- android:width="64dp"
- android:height="64dp" />
- <corners
- android:topLeftRadius="64dp"
- android:topRightRadius="64dp"
- android:bottomLeftRadius="64dp" />
-</shape>
\ No newline at end of file
diff --git a/res/drawable/deep_shortcuts_drag_handle.xml b/res/drawable/deep_shortcuts_drag_handle.xml
index 82e844d..8fc3779 100644
--- a/res/drawable/deep_shortcuts_drag_handle.xml
+++ b/res/drawable/deep_shortcuts_drag_handle.xml
@@ -22,6 +22,7 @@
android:tint="?android:attr/textColorHint" >
<path
- android:pathData="M20 9H4v2h16V9zM4 15h16v-2H4v2z"
+ android:pathData="M19,9H5c-0.55,0-1,0.45-1,1l0,0c0,0.55,0.45,1,1,1h14c0.55,0,1-0.45,1-1l0,0C20,9.45,19.55,9,19,9z M5,
+ 15h14c0.55,0,1-0.45,1-1l0,0c0-0.55-0.45-1-1-1H5c-0.55,0-1,0.45-1,1l0,0C4,14.55,4.45,15,5,15z"
android:fillColor="@android:color/white" />
</vector>
\ No newline at end of file
diff --git a/res/drawable/gutter_horizontal.xml b/res/drawable/gutter_horizontal.xml
new file mode 100644
index 0000000..95b03df
--- /dev/null
+++ b/res/drawable/gutter_horizontal.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This "gutter" has a shadow at the top and a subtler shadow on the bottom. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient android:type="linear"
+ android:angle="-90"
+ android:startColor="#ffE0E0E0"
+ android:centerColor="#ffffffff"
+ android:endColor="#ffF5F5F5" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_hand.xml b/res/drawable/ic_all_apps_bg_hand.xml
new file mode 100644
index 0000000..7f3fe14
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_hand.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="208dp"
+ android:height="212dp"
+ android:viewportWidth="208.0"
+ android:viewportHeight="212.0">
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M89.4,61.8H85l-1.6-1.5c5.5-6.4,8.8-14.7,8.8-23.7C92.2,16.6,76,0.3,55.9,0.3
+ S19.5,16.6,19.5,36.6S35.8,73,55.9,73c9,0,17.3-3.3,23.7-8.8l1.5,1.6v4.4l40.5,40.4l8.3-8.3L89.4,61.8z M54,66.6
+ c-13.9,0-28.8-16-28.8-30S41.5,8.9,55.4,8.9S81,22.7,81,36.6S67.9,66.6,54,66.6z"/>
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M33.4,29.2l-0.3-1.8l-4.2-3.1L18.1,26l-3.1,4.2l0.3,1.8L4.5,33.7L9,62.5
+ c0.2,1.5,1.6,2.5,3.1,2.3l34.2-5.3c1.5-0.2,2.5-1.6,2.3-3.1l-4.4-28.8L33.4,29.2z"/>
+ <path
+ android:fillColor="#3367D6"
+ android:pathData="M30.2,27.9l-0.3-1.8l-4.1-3L15,24.7l-3,4.1l0.3,1.8L1.6,32.3L6,60.9
+ c0.2,1.5,1.6,2.5,3.1,2.3L43,57.9c1.5-0.2,2.5-1.6,2.3-3.1l-4.4-28.6L30.2,27.9z M26.6,28.4l-10.7,1.6l-0.3-1.8l10.7-1.6L26.6,28.4z"/>
+ <group>
+ <clip-path
+ android:pathData="M25.1,37.7a28.9,28.9 0 1,0 57.8,0a28.9,28.9 0 1,0 -57.8,0"/>
+ <path
+ android:fillColor="#4285F4"
+ android:pathData="M41.7,23l-0.3-2.1l-4.9-3.6l-12.6,1.9l-3.6,4.9l0.3,2.1L8.1,28.1l5.2,33.7
+ c0.3,1.7,1.9,2.9,3.6,2.7l40-6.1c1.7-0.3,2.9-1.9,2.7-3.6L54.4,21L41.7,23z M37.5,23.6l-12.6,1.9l-0.3-2.1l12.6-1.9L37.5,23.6z"/>
+ </group>
+ <path
+ android:fillColor="?android:attr/colorControlHighlight"
+ android:pathData="M87.5,62.9h-4.4l-1.6-1.5c5.5-6.4,8.8-14.7,8.8-23.7C90.3,17.7,74,1.4,54,1.4
+ S17.6,17.7,17.6,37.7S33.9,74.1,54,74.1c9,0,17.3-3.3,23.7-8.8l1.5,1.6v4.4l40.5,40.4l8.3-8.3L87.5,62.9z M54,64.2
+ c-14.7,0-26.5-11.8-26.5-26.5S39.3,11.2,54,11.2s26.5,11.8,26.5,26.5S68.6,64.2,54,64.2z"/>
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M153.4,112.9c-14.9-17.2-38.6-9.1-38.6-9.1l-10.2-11.3c0,0-4.8-5.9-9-3.7
+ c-7,3.6-0.6,10.7-0.6,10.7s12.3,15.1,15.4,20.1c2.1,3.4,8.4,4.5,10.1,4.9l17.1,3.7l-0.9-0.7l-1-0.7L153.4,112.9z"/>
+ <path
+ android:fillColor="#FFDBA6"
+ android:pathData="M152.1,113.9c-14.9-17.2-37.6-8-37.6-8l-11.1-12.3c0,0-4.8-5.9-9-3.7
+ c-7,3.6-0.6,10.7-0.6,10.7s12.3,15.1,15.4,20.1c2.1,3.4,8.4,4.5,10.1,4.9l19,4.1"/>
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M148.6,77.9c0.6,0.7,2,2.5,2.1,2.6c1.1,1.7,6.2,13.6,11.8,35.1c0,0.1,1.9,3,1.9,3.1
+ c0,0,0.1,0.1,0.1,0.2c0,0,0,0,0-0.1c0.9,1.3,4.4,6.6,8.9,13.7c0.1,0.2,0.3,0.5,0.4,0.7c0,0.1,0.1,0.1,0.1,0.2
+ c0.2,0.3,0.4,0.6,0.6,0.9c0.1-0.1,0.2-0.2,0.3-0.3c0.2-0.3,0.6-0.3,0.8,0c2.9,4.8,21.2,35,26.7,49c2.1,5.3,3.2,8.4,3.6,11.6
+ c0.3,2.3,0,4.4-1.2,6c1.5-1.9,3.5-6.8-1.5-19c-1.2-2.9-2.8-6.5-4.8-10.5c-7.5-15.2-20-35.6-22.4-39.6c-0.2-0.3-0.6-0.3-0.8,0
+ c-0.2,0.2-0.3,0.4-0.5,0.6c-4.5-7.1-8.2-12.6-8.8-13.5c-0.1-0.1-1.9-3-1.9-3.1c-5.7-21.6-10.7-33.4-11.8-35.1
+ c-0.1-0.1-1.5-1.9-2.1-2.6l-6.5-8.3c-1.9-2.3-4.2-4.1-6.7-2.3c-2.5,1.8-1.6,4.5-0.1,7.1l3.3,5.2l7-2
+ C147.7,77.4,148.1,77.3,148.6,77.9z"/>
+ <path
+ android:fillColor="#FFDBA6"
+ android:pathData="M148.6,77.9l-6.5-8.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l3.8,6L148.6,77.9C148.6,77.9,148.6,77.9,148.6,77.9z"/>
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M151.1,92.5l-19.7-25.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l17.1,27.2L151.1,92.5z"/>
+ <path
+ android:fillColor="#FFDBA6"
+ android:pathData="M149.7,92.9l-19.7-25.3c-1.9-2.3-4.2-4.1-6.7-2.3l0,0l0,0c-2.5,1.8-1.6,4.5-0.1,7.1l17.1,27.2L149.7,92.9z"/>
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M141.6,94.6l-20.8-26.7c-2.1-2.5-4.4-4.3-7.1-2.5l0,0l0,0c-2.6,1.9-1.7,4.7-0.1,7.5l18,28.6L141.6,94.6z"/>
+ <path
+ android:fillColor="#FFDBA6"
+ android:pathData="M140.1,95l-20.8-26.7c-2.1-2.5-4.4-4.3-7.1-2.5l0,0h0c-2.6,1.9-1.7,4.7-0.1,7.5l18,28.6L140.1,95z"/>
+ <path
+ android:fillColor="#1A000000"
+ android:pathData="M140.4,99.1c-0.5-0.6-2.1-7.5-2.8-7.3l-15.9-6.9c-0.3,0-0.5-0.1-0.7-0.3l-2.3-3.5
+ l-0.4-0.6L100,54.5c-1.5-2-3.3-4-5.3-4.3c-0.7-0.1-1.3-0.2-2,0.2v0c-1,0.6-1.2,1.5-1.3,2.4c-0.2,1.8,0.6,3.9,1.6,5.9L108.5,87
+ l0.2,0.4l6.6,11.7l0,0c2.5,4.5,4.4,10.5,4.4,10.7L140.4,99.1"/>
+ <path
+ android:fillColor="#FFDBA6"
+ android:pathData="M129.7,125.1c3,0.7,8.1,4,11.8,9.1c2.7,3.7,5.5,8.3,8.2,13
+ c7.6-2.3,19.9-6.8,24.9-12.9c-0.2-0.3-0.4-0.6-0.6-0.9c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.2-0.3-0.5-0.4-0.7c-4.5-7.1-8-12.4-8.9-13.7
+ c0,0,0,0,0,0.1c0-0.1-0.1-0.1-0.1-0.2c-0.1-0.1-1.9-3-1.9-3.1c-5.7-21.5-10.7-33.4-11.8-35.1c-0.1-0.1-1.5-1.9-2.1-2.6
+ c-0.5-0.6-0.9-0.5-1.6-0.3l-26.8,7.7c-0.3,0-0.5-0.1-0.7-0.3l-2.3-3.5l-0.4-0.6L98.5,54.8c-1.5-2-3.3-4-5.3-4.3
+ c-0.7-0.1-1.3-0.2-2,0.2c-1,0.6-1.2,1.5-1.3,2.4c-0.2,1.8,0.6,3.9,1.6,5.9L107,87.3l0.2,0.4l6.6,11.7l0,0
+ c2.5,4.5,4.4,10.5,4.4,10.7L129.7,125.1"/>
+ <path
+ android:fillColor="?android:attr/colorForeground"
+ android:pathData="M202.3,183.1c-5.4-14.1-23.8-44.3-26.7-49c-0.2-0.3-0.6-0.3-0.8,0
+ c-5.1,6.6-19,11.4-26.5,13.6c-0.3,0.1-1,0.1-1.2,0.6c-0.2,0.4,0.1,1,0.2,1.1c7.8,12.9,14.7,27.9,15.3,29.3c0,0.1,0.1,0.1,0.1,0.2
+ l9.6,22.9c0,0,0,0,0,0l1.7,4.1c1.4,2.7,3,4.3,5.3,5.1c1.5,0.5,2.1,0.6,3.2,0.6c4.8,0.1,15.2-6.1,20.5-9.4c2.7-1.7,3.3-4.4,2.9-7.6
+ C205.5,191.5,204.4,188.4,202.3,183.1z"/>
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_1.xml b/res/drawable/ic_all_apps_bg_icon_1.xml
index c9c0a75..d226ac6 100644
--- a/res/drawable/ic_all_apps_bg_icon_1.xml
+++ b/res/drawable/ic_all_apps_bg_icon_1.xml
@@ -25,7 +25,7 @@
android:pathData="M44.28,30.96c4.84-10.68,0.09-23.27-10.59-28.11S10.42,2.74,5.58,13.42
C1,23.54,6.5,35.92,16.62,40.51l0,0l-3.23,7.12C27.84,47,39.79,40.86,44.28,30.96z" />
<path
- android:fillColor="#E0E0E0"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M41.75,30.05c4.84-10.68,0.09-23.27-10.59-28.11S7.9,1.83,3.06,12.51
c-4.59,10.12,0.92,22.5,11.03,27.09l0,0l-3.23,7.12C25.31,46.09,37.26,39.94,41.75,30.05z" />
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_2.xml b/res/drawable/ic_all_apps_bg_icon_2.xml
index b6269e3..5966d99 100644
--- a/res/drawable/ic_all_apps_bg_icon_2.xml
+++ b/res/drawable/ic_all_apps_bg_icon_2.xml
@@ -26,7 +26,7 @@
c-0.25-0.58-0.9-0.99-1.89-1.1L6.2,5.99C5.39,5.91,4.74,6.08,4.32,6.44l0,0C3.7,6.97,3.55,7.88,4.01,8.96l14.54,34.09
C19,44.13,19.75,44.65,20.54,44.59L20.54,44.59z" />
<path
- android:fillColor="#E0E0E0"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M18.49,43.22c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
c-0.25-0.58-0.9-0.99-1.89-1.1L4.15,4.62C3.34,4.54,2.69,4.71,2.27,5.08l0,0C1.65,5.6,1.5,6.52,1.96,7.6L16.5,41.69
C16.96,42.76,17.7,43.28,18.49,43.22L18.49,43.22z" />
diff --git a/res/drawable/ic_all_apps_bg_icon_3.xml b/res/drawable/ic_all_apps_bg_icon_3.xml
index 4c255a9..b18f8bc 100644
--- a/res/drawable/ic_all_apps_bg_icon_3.xml
+++ b/res/drawable/ic_all_apps_bg_icon_3.xml
@@ -26,11 +26,11 @@
s22.31-9.99,22.31-22.31S37.5,1.27,25.18,1.27z M25.18,33.55c-5.5,0-14.35-5.1-14.35-10.6s8.32-12.19,13.82-12.19
c5.5,0,10.49,7.33,10.49,12.83S30.68,33.55,25.18,33.55z" />
<path
- android:fillColor="#E0E0E0"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M22.93,0.22c-12.32,0-22.31,9.99-22.31,22.31s9.99,22.31,22.31,22.31
s22.31-9.99,22.31-22.31S35.25,0.22,22.93,0.22z M22.93,32.5c-5.5,0-9.97-4.46-9.97-9.97s4.46-9.97,9.97-9.97
c5.5,0,9.97,4.46,9.97,9.97S28.43,32.5,22.93,32.5z" />
<path
- android:fillColor="#E0E0E0"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M14.81,22.53a8.12,8.12 0 1,0 16.24,0a8.12,8.12 0 1,0 -16.24,0z" />
</vector>
diff --git a/res/drawable/ic_all_apps_bg_icon_4.xml b/res/drawable/ic_all_apps_bg_icon_4.xml
index 12e05bc..8eb4d90 100644
--- a/res/drawable/ic_all_apps_bg_icon_4.xml
+++ b/res/drawable/ic_all_apps_bg_icon_4.xml
@@ -25,7 +25,7 @@
android:pathData="M11.53,8.02l23.39-5.73c1.61-0.39,3.25,0.6,3.64,2.21l7.64,31.19
c0.39,1.61-0.6,3.25-2.21,3.64L12.8,46.97c-1.61,0.39-3.25-0.6-3.64-2.21L3.43,21.37L11.53,8.02z" />
<path
- android:fillColor="#E0E0E0"
+ android:fillColor="?android:attr/colorPrimary"
android:pathData="M9.2,6.53L32.59,0.8C34.2,0.4,35.84,1.4,36.23,3l7.64,31.19c0.39,1.61-0.6,3.25-2.21,3.64
l-31.19,7.64c-1.61,0.39-3.25-0.6-3.64-2.21L1.11,19.87L9.2,6.53z" />
<path
diff --git a/res/drawable/ic_info_no_shadow.xml b/res/drawable/ic_info_no_shadow.xml
index 3d0c6d6..b5512c3 100644
--- a/res/drawable/ic_info_no_shadow.xml
+++ b/res/drawable/ic_info_no_shadow.xml
@@ -17,8 +17,11 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorPrimary" >
<path
- android:fillColor="@color/workspace_icon_text_color"
- android:pathData="M11 17h2v-6h-2v6zm1-15C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zM11 9h2V7h-2v2z"/>
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M12,17L12,17c0.55,0,1-0.45,1-1v-4c0-0.55-0.45-1-1-1l0,0c-0.55,0-1,0.45-1,1v4C11,16.55,11.45,17,12,17z M12,2C6.48,
+ 2,2,6.48,2,12s4.48,10,10,10s10-4.48,10-10S17.52,2,12,2z M12,20c-4.41,0-8-3.59-8-8s3.59-8,8-8s8,3.59,8,8S16.41,20,12,20zM12,
+ 9.1L12,9.1c0.61,0,1.1-0.49,1.1-1.1l0,0c0-0.61-0.49-1.1-1.1-1.1l0,0c-0.61,0-1.1,0.49-1.1,1.1l0,0C10.9,8.61,11.39,9.1,12,9.1z"/>
</vector>
diff --git a/res/drawable/ic_remove_no_shadow.xml b/res/drawable/ic_remove_no_shadow.xml
new file mode 100644
index 0000000..be7f9f3
--- /dev/null
+++ b/res/drawable/ic_remove_no_shadow.xml
@@ -0,0 +1,27 @@
+<?xml 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorPrimary" >
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.41,12l5.29-5.29c0.39-0.39,0.39-1.02,0-1.41c-0.39-0.39-1.02-0.39-1.41,0L12,10.59L6.71,
+ 5.29c-0.39-0.39-1.02-0.39-1.41,0c-0.39,0.39-0.39,1.02,0,1.41L10.59,12l-5.29,5.29c-0.39,0.39-0.39,1.02,
+ 0,1.41c0.39,0.39,1.02,0.39,1.41,0L12,13.41l5.29,5.29c0.39,0.39,1.02,0.39,1.41,0c0.39-0.39,0.39-1.02,0-1.41L13.41,12z"/>
+</vector>
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
index e89c158..1bab189 100644
--- a/res/drawable/ic_setting.xml
+++ b/res/drawable/ic_setting.xml
@@ -19,6 +19,12 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/workspace_icon_text_color"
- android:pathData="M38.86 25.95c.08-.64 .14-1.29 .14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3 .49-.84 .24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91 .37-.99 .84l-.75 5.3c-1.22 .51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22 .43l-4 6.93c-.25 .43-.14 .97 .24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31 .14 1.95l-4.22 3.31c-.38 .3-.49 .84-.24 1.28l4 6.93c.25 .43 .77 .61 1.22 .43l4.98-2.01c1.03 .79 2.16 1.46 3.38 1.97l.75 5.3c.08 .47 .49 .84 .99 .84h8c.5 0 .91-.37 .99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45 .17 .97 0 1.22-.43l4-6.93c.25-.43 .14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
+ android:fillColor="?android:attr/textColorPrimary"
+ android:pathData="M42.8,28.4l-3.88-2.9c0.06-0.5,0.08-1,0.08-1.52s-0.02-1.02-0.08-1.52l3.88-2.86c0.84-0.62,1.04-1.88,
+ 0.48-2.82l-3.2-5.52c-0.56-0.96-1.76-1.4-2.72-1l-4.28,1.82c-0.96-0.74-2.02-1.36-3.14-1.84l-0.54-4.4C29.28,4.8,28.28,4,
+ 27.18,4h-6.36c-1.1,0-2.1,0.8-2.22,1.84l-0.52,4.38c-1.14,0.48-2.2,1.1-3.16,1.84l-4.28-1.82c-0.96-0.4-2.16,0.04-2.72,
+ 1l-3.2,5.52c-0.56,0.96-0.36,2.2,0.48,2.84l3.88,2.9C9.02,22.98,9,23.48,9,24s0.02,1.02,0.08,1.52L5.2,28.4c-0.84,0.62-1.04,
+ 1.88-0.48,2.82l3.2,5.52c0.56,0.96,1.76,1.4,2.72,1l4.28-1.82c0.96,0.74,2.02,1.36,3.14,1.84l0.54,4.38C18.72,43.2,19.72,
+ 44,20.82,44h6.36c1.1,0,2.08-0.8,2.22-1.84l0.54-4.38c1.12-0.48,2.18-1.1,3.14-1.84l4.28,1.82c0.96,0.4,2.16-0.04,
+ 2.72-1l3.2-5.52C43.84,30.28,43.64,29.04,42.8,28.4z M24,31c-3.86,0-7-3.14-7-7s3.14-7,7-7s7,3.14,7,7S27.86,31,24,31z"/>
</vector>
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
new file mode 100644
index 0000000..2a86e10
--- /dev/null
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -0,0 +1,26 @@
+<?xml 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="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorPrimary" >
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,
+ 3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4L18,4z"/>
+</vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
index b7fcfbf..9e9222f 100644
--- a/res/drawable/ic_wallpaper.xml
+++ b/res/drawable/ic_wallpaper.xml
@@ -19,6 +19,11 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/workspace_icon_text_color"
- android:pathData="M8 8h14V4H8C5.79 4 4 5.79 4 8v14h4V8zm12 18l-8 10h24l-6-8-4.06 5.42L20 26zm14-9c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm6-13H26v4h14v14h4V8c0-2.21-1.79-4-4-4zm0 36H26v4h14c2.21 0 4-1.79 4-4V26h-4v14zM8 26H4v14c0 2.21 1.79 4 4 4h14v-4H8V26z"/>
+ android:fillColor="?android:attr/textColorPrimary"
+ android:pathData="M8,8h13c0.56,0,1-0.44,1-1V5c0-0.56-0.44-1-1-1H8C5.8,4,4,5.8,4,8v13c0,0.56,0.44,1,1,1h2c0.56,0,1-0.44,
+ 1-1V8zM18.44,27.96L12,36h24l-4.4-5.86c-0.8-1.06-2.4-1.06-3.2,0l-2.46,3.28l-4.38-5.46C20.76,26.96,19.24,26.96,18.44,27.96z M34,
+ 17c0-1.66-1.34-3-3-3s-3,1.34-3,3s1.34,3,3,3S34,18.66,34,17z M40,4H27c-0.56,0-1,0.44-1,1v2c0,0.56,0.44,1,1,1h13v13c0,
+ 0.56,0.44,1,1,1h2c0.56,0,1-0.44,1-1V8C44,5.8,42.2,4,40,4z M40,40H27c-0.56,0-1,0.44-1,1v2c0,0.56,0.44,1,1,1h13c2.2,
+ 0,4-1.8,4-4V27c0-0.56-0.44-1-1-1h-2c-0.56,0-1,0.44-1,1V40z M7,26H5c-0.56,0-1,0.44-1,1v13c0,2.2,1.8,4,4,4h13c0.56,0,1-0.44,
+ 1-1v-2c0-0.56-0.44-1-1-1H8V27C8,26.44,7.56,26,7,26z"/>
</vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
index 97706e3..de2980f 100644
--- a/res/drawable/ic_widget.xml
+++ b/res/drawable/ic_widget.xml
@@ -19,6 +19,8 @@
android:viewportWidth="48.0"
android:viewportHeight="48.0">
<path
- android:fillColor="@color/workspace_icon_text_color"
- android:pathData="M26 26v16h16V26H26zM6 42h16V26H6v16zM6 6v16h16V6H6zm27.31-2.63L22 14.69 33.31 26l11.31-11.31L33.31 3.37z"/>
+ android:fillColor="#FFFFFFFF"
+ 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"/>
</vector>
diff --git a/res/interpolator/folder_interpolator.xml b/res/interpolator/folder_interpolator.xml
new file mode 100644
index 0000000..b95d454
--- /dev/null
+++ b/res/interpolator/folder_interpolator.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.2"
+ android:controlY1="0"
+ android:controlX2="0"
+ android:controlY2="1"/>
diff --git a/res/interpolator/large_folder_preview_item_close_interpolator.xml b/res/interpolator/large_folder_preview_item_close_interpolator.xml
new file mode 100644
index 0000000..d28af63
--- /dev/null
+++ b/res/interpolator/large_folder_preview_item_close_interpolator.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0.3"
+ android:controlY1="0"
+ android:controlX2="1"
+ android:controlY2="1"/>
diff --git a/res/interpolator/large_folder_preview_item_open_interpolator.xml b/res/interpolator/large_folder_preview_item_open_interpolator.xml
new file mode 100644
index 0000000..b5661ab
--- /dev/null
+++ b/res/interpolator/large_folder_preview_item_open_interpolator.xml
@@ -0,0 +1,24 @@
+<?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.
+*/
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:controlX1="0"
+ android:controlY1="1"
+ android:controlX2="0.5"
+ android:controlY2="1"/>
diff --git a/res/layout-land/all_apps_fast_scroller.xml b/res/layout-land/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..957c331
--- /dev/null
+++ b/res/layout-land/all_apps_fast_scroller.xml
@@ -0,0 +1,38 @@
+<?xml 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="-5dp" />
+
+ <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_alignTop="@+id/apps_list_view"
+ android:layout_marginEnd="-48dp"
+ android:layout_marginTop="-8dp"
+ launcher:canThumbDetach="true" />
+
+</merge>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index ef0dfdc..2bbd76d 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -27,7 +27,7 @@
android:id="@+id/drag_layer"
android:clipChildren="false"
android:clipToPadding="false"
- android:background="@drawable/workspace_bg"
+ android:background="?attr/workspaceStatusBarScrim"
android:importantForAccessibility="no"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -35,12 +35,15 @@
<!-- 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" />
+ <include layout="@layout/gradient_scrim" />
+
<!-- DO NOT CHANGE THE ID -->
<include layout="@layout/hotseat"
android:id="@+id/hotseat"
@@ -59,16 +62,11 @@
<com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
android:id="@+id/page_indicator"
- android:layout_width="@dimen/dynamic_grid_page_indicator_height"
- android:layout_height="@dimen/dynamic_grid_page_indicator_height"
+ android:theme="@style/HomeScreenElementTheme"
+ android:layout_width="@dimen/dynamic_grid_page_indicator_size"
+ android:layout_height="@dimen/dynamic_grid_page_indicator_size"
android:layout_gravity="bottom|left"/>
- <!-- A place holder view instead of the QSB in transposed layout -->
- <View
- android:layout_width="0dp"
- android:layout_height="10dp"
- android:id="@+id/workspace_blocked_row" />
-
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index dd981dd..93c9078 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -29,13 +29,14 @@
android:clipChildren="false"
android:importantForAccessibility="no"
android:clipToPadding="false"
- android:background="@drawable/workspace_bg"
+ 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:id="@+id/workspace"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -43,6 +44,8 @@
launcher:pageIndicator="@+id/page_indicator">
</com.android.launcher3.Workspace>
+ <include layout="@layout/gradient_scrim" />
+
<!-- DO NOT CHANGE THE ID -->
<include layout="@layout/hotseat"
android:id="@+id/hotseat"
@@ -63,10 +66,6 @@
android:id="@+id/drop_target_bar"
layout="@layout/drop_target_bar_horz" />
- <include
- layout="@layout/qsb_container"
- android:id="@+id/qsb_container" />
-
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
diff --git a/res/layout-sw720dp/all_apps_fast_scroller.xml b/res/layout-sw720dp/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..12c15cc
--- /dev/null
+++ b/res/layout-sw720dp/all_apps_fast_scroller.xml
@@ -0,0 +1,37 @@
+<?xml 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
index 06cb550..ca4d846 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -28,13 +28,14 @@
android:clipChildren="false"
android:clipToPadding="false"
android:importantForAccessibility="no"
- android:background="@drawable/workspace_bg"
+ 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"
@@ -42,6 +43,8 @@
launcher:pageIndicator="@id/page_indicator">
</com.android.launcher3.Workspace>
+ <include layout="@layout/gradient_scrim" />
+
<!-- DO NOT CHANGE THE ID -->
<include layout="@layout/hotseat"
android:id="@+id/hotseat"
@@ -62,10 +65,6 @@
<include layout="@layout/page_indicator"
android:id="@+id/page_indicator" />
- <include
- layout="@layout/qsb_container"
- android:id="@+id/qsb_container" />
-
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
diff --git a/res/layout/add_item_confirmation_activity.xml b/res/layout/add_item_confirmation_activity.xml
index 35bcac3..6c316e6 100644
--- a/res/layout/add_item_confirmation_activity.xml
+++ b/res/layout/add_item_confirmation_activity.xml
@@ -17,57 +17,73 @@
*/
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:paddingLeft="24dp"
- android:paddingRight="24dp"
- android:paddingTop="4dp"
- android:paddingBottom="20dp"
- android:text="@string/add_item_request_drag_hint"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:clipToPadding="false">
- <FrameLayout
- android:theme="@style/WidgetContainerTheme"
- android:layout_width="match_parent"
- android:background="?android:attr/colorPrimaryDark"
- android:layout_height="wrap_content">
-
- <com.android.launcher3.dragndrop.LivePreviewWidgetCell
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:orientation="vertical"
- android:focusable="true"
- android:background="?android:attr/colorPrimaryDark"
- android:gravity="center_horizontal"
- android:id="@+id/widget_cell"
- android:layout_gravity="center_horizontal" >
+ android:orientation="vertical">
- <include layout="@layout/widget_cell_content" />
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="20dp"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:paddingTop="4dp"
+ android:text="@string/add_item_request_drag_hint" />
- </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
- </FrameLayout>
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorPrimaryDark"
+ android:theme="@style/WidgetContainerTheme">
+
+ <com.android.launcher3.dragndrop.LivePreviewWidgetCell
+ android:id="@+id/widget_cell"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_weight="1"
+ android:background="?android:attr/colorPrimaryDark"
+ android:focusable="true"
+ android:gravity="center_horizontal"
+ android:orientation="vertical" >
+
+ <include layout="@layout/widget_cell_content" />
+
+ </com.android.launcher3.dragndrop.LivePreviewWidgetCell>
+ </FrameLayout>
+ </LinearLayout>
+ </ScrollView>
<LinearLayout
+ style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="?android:attr/buttonBarStyle"
- android:gravity="end" >
+ android:gravity="end"
+ android:paddingBottom="4dp"
+ android:paddingEnd="12dp"
+ android:paddingStart="12dp"
+ android:paddingTop="4dp" >
<Button
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@android:string/cancel"
android:onClick="onCancelClick"
- style="?android:attr/buttonBarButtonStyle" />
+ android:text="@android:string/cancel" />
<Button
+ style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/place_automatically"
android:onClick="onPlaceAutomaticallyClick"
- style="?android:attr/buttonBarButtonStyle" />
+ android:text="@string/place_automatically" />
</LinearLayout>
</LinearLayout>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index d6bdac2..93662fc 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -16,7 +16,8 @@
<!-- The top and bottom paddings are defined in this container, but since we want
the list view to span the full width (for touch interception purposes), we
will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.allapps.AllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.AllAppsContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/apps_view"
android:layout_width="match_parent"
@@ -39,6 +40,8 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:focusable="true"
+ android:clipToPadding="false"
+ android:clipChildren="true"
android:focusableInTouchMode="true"
android:saveEnabled="false"
android:visibility="gone">
@@ -51,46 +54,17 @@
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|top"
android:clipToPadding="false"
+ android:overScrollMode="never"
android:descendantFocusability="afterDescendants"
- android:focusable="true"
- android:paddingStart="@dimen/container_fastscroll_thumb_max_width"
- android:paddingEnd="@dimen/container_fastscroll_thumb_max_width" />
+ android:focusable="true" />
- <!-- Fast scroller popup -->
- <TextView
- style="@style/FastScrollerPopup"
- android:layout_below="@+id/search_container"
- android:id="@+id/fast_scroller_popup"
- android:layout_alignParentEnd="true"
- android:layout_marginEnd="@dimen/container_fastscroll_popup_margin" />
+ <!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
+ platform bug, which prevents using custom attributes in <include> tag -->
+ <include
+ layout="?android:attr/keyboardLayout"
+ android:id="@+id/search_container" />
- <FrameLayout
- android:id="@+id/search_container"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_height"
- android:layout_gravity="center|top"
- android:gravity="center|bottom"
- android:orientation="horizontal"
- android:saveEnabled="false">
-
- <com.android.launcher3.ExtendedEditText
- android:id="@+id/search_box_input"
- android:layout_width="match_parent"
- android:layout_height="@dimen/all_apps_search_bar_field_height"
- android:background="@android:color/transparent"
- android:layout_gravity="bottom"
- android:focusableInTouchMode="true"
- android:gravity="center"
- android:imeOptions="actionSearch|flagNoExtractUi"
- android:inputType="text|textNoSuggestions|textCapWords"
- android:maxLines="1"
- android:scrollHorizontally="true"
- android:singleLine="true"
- android:textColor="?android:attr/textColorSecondary"
- android:hint="@string/all_apps_search_bar_hint"
- android:textColorHint="@drawable/all_apps_search_hint"
- android:textSize="16sp" />
- </FrameLayout>
+ <include layout="@layout/all_apps_fast_scroller" />
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
<View
diff --git a/res/layout/all_apps_discovery_item.xml b/res/layout/all_apps_discovery_item.xml
index fb1755c..728283f 100644
--- a/res/layout/all_apps_discovery_item.xml
+++ b/res/layout/all_apps_discovery_item.xml
@@ -94,8 +94,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
- android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
- android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+ 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" />
diff --git a/res/layout/all_apps_discovery_loading_divider.xml b/res/layout/all_apps_discovery_loading_divider.xml
index 1ad5521..005847c 100644
--- a/res/layout/all_apps_discovery_loading_divider.xml
+++ b/res/layout/all_apps_discovery_loading_divider.xml
@@ -16,8 +16,8 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="6dp"
- android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
- android:paddingRight="@dimen/container_fastscroll_thumb_max_width">
+ android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+ android:paddingRight="@dimen/dynamic_grid_edge_margin">
<ProgressBar
android:id="@+id/loadingProgressBar"
diff --git a/res/layout/all_apps_divider.xml b/res/layout/all_apps_divider.xml
index 1eaf685..8a4f646 100644
--- a/res/layout/all_apps_divider.xml
+++ b/res/layout/all_apps_divider.xml
@@ -19,8 +19,8 @@
android:layout_height="wrap_content"
android:paddingTop="@dimen/all_apps_divider_margin_vertical"
android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
- android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
- android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+ 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" />
\ No newline at end of file
diff --git a/res/layout/all_apps_fast_scroller.xml b/res/layout/all_apps_fast_scroller.xml
new file mode 100644
index 0000000..12c15cc
--- /dev/null
+++ b/res/layout/all_apps_fast_scroller.xml
@@ -0,0 +1,37 @@
+<?xml 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/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index ca0cbcc..79fb612 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -23,7 +23,6 @@
android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
launcher:iconDisplay="all_apps"
launcher:centerVertically="true"
- android:paddingLeft="4dp"
- android:paddingRight="4dp"
- android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding" />
+ android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
+ android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
diff --git a/res/layout/all_apps_search_container.xml b/res/layout/all_apps_search_container.xml
new file mode 100644
index 0000000..2528034
--- /dev/null
+++ b/res/layout/all_apps_search_container.xml
@@ -0,0 +1,68 @@
+<?xml 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.allapps.search.AppsSearchContainerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/all_apps_search_bar_height"
+ android:layout_gravity="center|top"
+ android:gravity="center|bottom"
+ android:saveEnabled="false"
+ android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+ android:paddingRight="@dimen/dynamic_grid_edge_margin"
+ android:layout_marginBottom="-8dp" >
+
+ <!--
+ Note: The following relation should always be true so that the shadows are aligned properly
+ search_box_input.layout_marginBottom
+ == search_divider.layout_marginBottom
+ == - (search_container.layout_marginBottom)
+ >= 5dp
+ -->
+ <com.android.launcher3.ExtendedEditText
+ android:id="@+id/search_box_input"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/all_apps_search_bar_field_height"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="8dp"
+ android:background="@android:color/transparent"
+ android:focusableInTouchMode="true"
+ android:gravity="center"
+ android:hint="@string/all_apps_search_bar_hint"
+ android:imeOptions="actionSearch|flagNoExtractUi"
+ android:inputType="text|textNoSuggestions|textCapWords"
+ android:maxLines="1"
+ android:scrollHorizontally="true"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textColorHint="@drawable/all_apps_search_hint"
+ android:textSize="16sp" />
+
+ <!-- This needs to be a container with a view, to simulate a scrolling effect for the header.
+ We translate the header down, and its content up by the same amount, so that it gets
+ clipped by the parent, making it look like the divider was scrolled out of the view. -->
+ <FrameLayout
+ android:id="@+id/search_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_gravity="bottom"
+ android:layout_marginBottom="8dp" >
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@drawable/all_apps_search_divider"/>
+ </FrameLayout>
+</com.android.launcher3.allapps.search.AppsSearchContainerLayout>
\ No newline at end of file
diff --git a/res/layout/all_apps_search_divider.xml b/res/layout/all_apps_search_divider.xml
deleted file mode 100644
index d2ef691..0000000
--- a/res/layout/all_apps_search_divider.xml
+++ /dev/null
@@ -1,25 +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.
--->
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
- android:importantForAccessibility="no"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
- android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
- android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
- android:src="@drawable/all_apps_search_divider"
- android:scaleType="fitXY"
- android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index 3f19b25..6f2dd3d 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -19,8 +19,8 @@
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center"
- android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
- android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+ android:paddingLeft="@dimen/dynamic_grid_edge_margin"
+ android:paddingRight="@dimen/dynamic_grid_edge_margin"
android:fontFamily="sans-serif-medium"
android:textSize="14sp"
android:textColor="?android:attr/colorAccent"
diff --git a/res/layout/app_icon.xml b/res/layout/app_icon.xml
index fa6eb89..52df694 100644
--- a/res/layout/app_icon.xml
+++ b/res/layout/app_icon.xml
@@ -14,4 +14,4 @@
limitations under the License.
-->
-<com.android.launcher3.BubbleTextView style="@style/BaseIcon.Workspace" />
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace" />
diff --git a/res/layout/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index 91a1e45..874fecc 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -20,38 +20,43 @@
android:layout_height="match_parent"
android:background="@drawable/widget_resize_shadow"
android:foreground="@drawable/widget_resize_frame"
+ android:foregroundTint="?attr/workspaceTextColor"
android:padding="0dp" >
<!-- Left -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/ic_widget_resize_handle"
android:layout_gravity="left|center_vertical"
- android:layout_marginLeft="@dimen/widget_handle_margin" />
+ android:layout_marginLeft="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
<!-- Top -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/ic_widget_resize_handle"
android:layout_gravity="top|center_horizontal"
- android:layout_marginTop="@dimen/widget_handle_margin" />
+ android:layout_marginTop="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
<!-- Right -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/ic_widget_resize_handle"
android:layout_gravity="right|center_vertical"
- android:layout_marginRight="@dimen/widget_handle_margin" />
+ android:layout_marginRight="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
<!-- Bottom -->
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:src="@drawable/ic_widget_resize_handle"
android:layout_gravity="bottom|center_horizontal"
- android:layout_marginBottom="@dimen/widget_handle_margin" />
+ android:layout_marginBottom="@dimen/widget_handle_margin"
+ android:src="@drawable/ic_widget_resize_handle"
+ android:tint="?attr/workspaceTextColor" />
</com.android.launcher3.AppWidgetResizeFrame>
\ No newline at end of file
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
index 85caba4..4a2ad42 100644
--- a/res/layout/deep_shortcut.xml
+++ b/res/layout/deep_shortcut.xml
@@ -18,7 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
- android:layout_height="@dimen/bg_popup_item_height" >
+ android:layout_height="@dimen/bg_popup_item_height"
+ android:theme="@style/PopupItem" >
<com.android.launcher3.shortcuts.DeepShortcutTextView
style="@style/BaseIcon"
@@ -50,6 +51,6 @@
android:layout_height="@dimen/popup_item_divider_height"
android:layout_gravity="end|bottom"
android:visibility="gone"
- android:background="?android:attr/listDivider" />
+ android:background="?attr/popupColorTertiary" />
</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/drop_target_bar_horz.xml b/res/layout/drop_target_bar_horz.xml
index ee22d1e..ed18192 100644
--- a/res/layout/drop_target_bar_horz.xml
+++ b/res/layout/drop_target_bar_horz.xml
@@ -17,8 +17,10 @@
<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">
diff --git a/res/layout/drop_target_bar_vert.xml b/res/layout/drop_target_bar_vert.xml
index 10b1d7c..2394d0d 100644
--- a/res/layout/drop_target_bar_vert.xml
+++ b/res/layout/drop_target_bar_vert.xml
@@ -15,11 +15,13 @@
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" >
@@ -30,8 +32,7 @@
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"
- android:textColor="@android:color/white" />
+ android:id="@+id/delete_target_text" />
<!-- Uninstall target -->
<com.android.launcher3.UninstallDropTarget
@@ -41,7 +42,6 @@
android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
android:id="@+id/uninstall_target_text"
- android:textColor="@android:color/white"
android:layout_marginTop="@dimen/vert_drop_target_vertical_gap"/>
<Space
@@ -57,7 +57,6 @@
android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
android:id="@+id/info_target_text"
- android:textColor="@android:color/white"
android:layout_marginBottom="64dp"/>
</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index ccc6b01..4093744 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -20,7 +20,7 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:focusable="true" >
- <com.android.launcher3.BubbleTextView
+ <com.android.launcher3.views.DoubleShadowBubbleTextView
style="@style/BaseIcon.Workspace"
android:id="@+id/folder_icon_name"
android:focusable="false"
diff --git a/res/layout/gradient_scrim.xml b/res/layout/gradient_scrim.xml
new file mode 100644
index 0000000..c40c5fc
--- /dev/null
+++ b/res/layout/gradient_scrim.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <com.android.launcher3.graphics.GradientView
+ android:id="@+id/gradient_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ app:layout_ignoreInsets="true"/>
+
+ <com.android.launcher3.graphics.ScrimView
+ android:id="@+id/scrim_bg"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ app:layout_ignoreInsets="true"/>
+</merge>
\ No newline at end of file
diff --git a/res/layout/horizontal_divider.xml b/res/layout/horizontal_divider.xml
new file mode 100644
index 0000000..167f8f5
--- /dev/null
+++ b/res/layout/horizontal_divider.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/popup_item_divider_height"
+ android:background="?attr/popupColorTertiary" />
\ No newline at end of file
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index f5b5bbf..582a83f 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<com.android.launcher3.Hotseat
+ android:theme="@style/HomeScreenElementTheme"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto">
<com.android.launcher3.CellLayout
diff --git a/res/layout/notification.xml b/res/layout/notification.xml
index f955c6b..4a02aa1 100644
--- a/res/layout/notification.xml
+++ b/res/layout/notification.xml
@@ -19,9 +19,8 @@
android:id="@+id/notification_view"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="wrap_content"
- android:elevation="@dimen/deep_shortcuts_elevation"
- android:background="@drawable/bg_white_round_rect"
- android:backgroundTint="@color/notification_color_beneath">
+ android:theme="@style/PopupItem"
+ android:elevation="@dimen/deep_shortcuts_elevation">
<RelativeLayout
android:layout_width="match_parent"
@@ -29,29 +28,37 @@
android:orientation="vertical"
android:clipChildren="false">
+ <View
+ android:id="@+id/gutter_top"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:theme="@style/PopupGutter"
+ android:visibility="gone" />
+
<FrameLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="@dimen/notification_header_height"
android:paddingStart="@dimen/notification_padding_start"
android:paddingEnd="@dimen/notification_padding_end"
- android:background="@color/popup_header_background_color"
- android:elevation="@dimen/notification_elevation">
+ android:background="?attr/popupColorPrimary"
+ android:elevation="@dimen/notification_elevation"
+ android:layout_below="@id/gutter_top" >
<TextView
android:id="@+id/notification_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
- android:gravity="center_vertical"
+ android:gravity="bottom"
android:text="@string/notifications_header"
android:textSize="@dimen/notification_header_text_size"
- android:textColor="?android:attr/textColorSecondary" />
+ android:textColor="?android:attr/textColorPrimary" />
<TextView
android:id="@+id/notification_count"
android:layout_width="@dimen/notification_icon_size"
android:layout_height="match_parent"
android:layout_gravity="end"
- android:gravity="center"
+ android:gravity="bottom|center_horizontal"
android:textSize="@dimen/notification_header_count_text_size"
android:fontFamily="sans-serif-medium"
android:textColor="?android:attr/textColorPrimary" />
@@ -67,8 +74,9 @@
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="@dimen/popup_item_divider_height"
- android:background="?android:attr/listDivider"
- android:layout_below="@id/main_view"/>
+ android:background="?attr/popupColorTertiary"
+ android:layout_below="@id/main_view"
+ android:visibility="gone" />
<include layout="@layout/notification_footer"
android:id="@+id/footer"
@@ -76,6 +84,14 @@
android:layout_height="@dimen/notification_footer_height"
android:layout_below="@id/divider" />
+ <View
+ android:id="@+id/gutter_bottom"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:theme="@style/PopupGutter"
+ android:visibility="gone"
+ android:layout_below="@id/footer" />
+
</RelativeLayout>
</com.android.launcher3.notification.NotificationItemView>
diff --git a/res/layout/notification_footer.xml b/res/layout/notification_footer.xml
index ed2212b..86280e0 100644
--- a/res/layout/notification_footer.xml
+++ b/res/layout/notification_footer.xml
@@ -22,7 +22,7 @@
android:elevation="@dimen/notification_elevation"
android:clipChildren="false"
android:layout_gravity="center_vertical"
- android:background="@color/popup_background_color">
+ android:background="?attr/popupColorPrimary">
<LinearLayout
android:id="@+id/icon_row"
diff --git a/res/layout/notification_main.xml b/res/layout/notification_main.xml
index ce4e137..f681e8b 100644
--- a/res/layout/notification_main.xml
+++ b/res/layout/notification_main.xml
@@ -28,16 +28,17 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
- android:background="@color/popup_background_color"
+ android:background="?attr/popupColorPrimary"
android:paddingStart="@dimen/notification_padding_start"
- android:paddingEnd="@dimen/notification_main_text_padding_end">
+ android:paddingEnd="@dimen/notification_main_text_padding_end"
+ android:paddingBottom="16dp">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:fontFamily="sans-serif"
- android:textSize="@dimen/notification_main_text_size"
+ android:textSize="@dimen/notification_main_title_size"
android:textColor="?android:attr/textColorPrimary"
android:lines="1"
android:ellipsize="end" />
@@ -58,6 +59,7 @@
android:layout_width="@dimen/notification_icon_size"
android:layout_height="@dimen/notification_icon_size"
android:layout_marginEnd="@dimen/notification_padding_end"
+ android:layout_marginBottom="8dp"
android:layout_gravity="center_vertical|end" />
</com.android.launcher3.notification.NotificationMainView>
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 2091721..d1ac56c 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -14,8 +14,10 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:theme="@style/HomeScreenElementTheme"
launcher:layout_ignoreInsets="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -30,12 +32,13 @@
android:layout_weight="1"
android:drawablePadding="4dp"
android:drawableTop="@drawable/ic_wallpaper"
+ android:drawableTint="?attr/workspaceTextColor"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:stateListAnimator="@animator/overview_button_anim"
android:text="@string/wallpaper_button_text"
android:textAllCaps="true"
- android:textColor="@android:color/white"
+ android:textColor="?attr/workspaceTextColor"
android:textSize="12sp" />
<TextView
@@ -45,12 +48,13 @@
android:layout_weight="1"
android:drawablePadding="4dp"
android:drawableTop="@drawable/ic_widget"
+ android:drawableTint="?attr/workspaceTextColor"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:stateListAnimator="@animator/overview_button_anim"
android:text="@string/widget_button_text"
android:textAllCaps="true"
- android:textColor="@android:color/white"
+ android:textColor="?attr/workspaceTextColor"
android:textSize="12sp" />
<TextView
@@ -60,12 +64,13 @@
android:layout_weight="1"
android:drawablePadding="4dp"
android:drawableTop="@drawable/ic_setting"
+ android:drawableTint="?attr/workspaceTextColor"
android:fontFamily="sans-serif-condensed"
android:gravity="center_horizontal"
android:stateListAnimator="@animator/overview_button_anim"
android:text="@string/settings_button_text"
android:textAllCaps="true"
- android:textColor="@android:color/white"
+ android:textColor="?attr/workspaceTextColor"
android:textSize="12sp" />
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
index 2e1b57f..14ff2bd 100644
--- a/res/layout/page_indicator.xml
+++ b/res/layout/page_indicator.xml
@@ -16,12 +16,13 @@
<com.android.launcher3.pageindicators.PageIndicatorLineCaret
xmlns:android="http://schemas.android.com/apk/res/android"
+ android:theme="@style/HomeScreenElementTheme"
android:layout_width="match_parent"
- android:layout_height="@dimen/dynamic_grid_page_indicator_height">
+ android:layout_height="@dimen/dynamic_grid_page_indicator_size">
<ImageView
android:id="@+id/all_apps_handle"
android:layout_width="48dp"
android:layout_height="match_parent"
- android:layout_gravity="center"
+ android:layout_gravity="top|center"
android:scaleType="centerInside"/>
</com.android.launcher3.pageindicators.PageIndicatorLineCaret>
diff --git a/res/layout/qsb_default_view.xml b/res/layout/qsb_default_view.xml
index 3075f80..04fe236 100644
--- a/res/layout/qsb_default_view.xml
+++ b/res/layout/qsb_default_view.xml
@@ -21,7 +21,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
- android:layout_margin="16dp"
+ android:layout_margin="8dp"
android:layout_gravity="center_vertical"
android:background="@drawable/round_rect_primary"
android:elevation="2dp"
diff --git a/res/layout/shortcuts_item.xml b/res/layout/shortcuts_item.xml
index 8b20bcb..7cd996d 100644
--- a/res/layout/shortcuts_item.xml
+++ b/res/layout/shortcuts_item.xml
@@ -19,14 +19,21 @@
android:id="@+id/shortcuts_view"
android:layout_width="@dimen/bg_popup_item_width"
android:layout_height="wrap_content"
- android:elevation="@dimen/deep_shortcuts_elevation"
- android:background="@drawable/bg_white_round_rect">
+ android:elevation="@dimen/deep_shortcuts_elevation">
<LinearLayout
- android:id="@+id/deep_shortcuts"
+ android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
+
+ <!-- The shortcuts header is added at runtime when necessary. -->
+
+ <LinearLayout
+ android:id="@+id/shortcuts"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" />
</LinearLayout>
</com.android.launcher3.shortcuts.ShortcutsItemView>
diff --git a/res/layout/system_shortcut.xml b/res/layout/system_shortcut.xml
index bf849aa..04f3d02 100644
--- a/res/layout/system_shortcut.xml
+++ b/res/layout/system_shortcut.xml
@@ -18,7 +18,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="@dimen/bg_popup_item_width"
- android:layout_height="@dimen/bg_popup_item_height" >
+ android:layout_height="@dimen/bg_popup_item_height"
+ android:theme="@style/PopupItem" >
<com.android.launcher3.BubbleTextView
style="@style/BaseIcon"
@@ -40,7 +41,8 @@
android:layout_width="@dimen/system_shortcut_icon_size"
android:layout_height="@dimen/system_shortcut_icon_size"
android:layout_marginStart="@dimen/system_shortcut_margin_start"
- android:layout_gravity="start|center_vertical" />
+ android:layout_gravity="start|center_vertical"
+ android:backgroundTint="?android:attr/textColorTertiary"/>
<View
android:id="@+id/divider"
@@ -48,6 +50,6 @@
android:layout_height="@dimen/popup_item_divider_height"
android:layout_gravity="end|bottom"
android:visibility="gone"
- android:background="?android:attr/listDivider" />
+ android:background="?attr/popupColorTertiary" />
</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/system_shortcut_icon_only.xml b/res/layout/system_shortcut_icon_only.xml
index 313c69c..c59cb53 100644
--- a/res/layout/system_shortcut_icon_only.xml
+++ b/res/layout/system_shortcut_icon_only.xml
@@ -19,4 +19,6 @@
android:layout_width="@dimen/system_shortcut_header_icon_touch_size"
android:layout_height="@dimen/system_shortcut_header_icon_touch_size"
android:background="?android:attr/selectableItemBackgroundBorderless"
- android:padding="@dimen/system_shortcut_header_icon_padding" />
+ android:tint="?android:attr/textColorHint"
+ android:padding="@dimen/system_shortcut_header_icon_padding"
+ android:theme="@style/PopupItem" />
diff --git a/res/layout/system_shortcut_icons.xml b/res/layout/system_shortcut_icons.xml
index 676be8e..34d63e7 100644
--- a/res/layout/system_shortcut_icons.xml
+++ b/res/layout/system_shortcut_icons.xml
@@ -21,4 +21,7 @@
android:layout_height="@dimen/system_shortcut_header_height"
android:orientation="horizontal"
android:gravity="end|center_vertical"
- android:background="@color/popup_header_background_color" />
+ android:background="?attr/popupColorSecondary"
+ android:elevation="1dp"
+ android:outlineProvider="none" />
+ <!-- We have elevation so this is drawn on top, but no outline provider to remove shadow -->
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index cde6540..4d80aac 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -24,6 +24,7 @@
<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"
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 2063f32..e91f966 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -24,6 +24,7 @@
<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"
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index c2270d2..e8c6961 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -22,14 +22,15 @@
android:paddingTop="28dp"
android:background="?android:attr/colorPrimary"
android:elevation="@dimen/deep_shortcuts_elevation"
- android:layout_gravity="bottom">
+ android:layout_gravity="bottom"
+ android:theme="?attr/widgetsTheme">
<TextView
+ style="@style/TextTitle"
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- android:fontFamily="sans-serif"
android:textColor="?android:attr/textColorPrimary"
android:textSize="24sp"/>
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index b6e0a0b..4cd03ce 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -41,7 +41,7 @@
android:singleLine="true"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
- launcher:customShadows="false"
+ android:textAlignment="viewStart"
launcher:deferShadowGeneration="true"
launcher:iconDisplay="widget_section"
launcher:iconSizeOverride="@dimen/widget_section_icon_size"
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 2f11c28..4f3c7c8 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -23,25 +23,25 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="afterDescendants"
- launcher:revealBackground="@drawable/round_rect_primary"
- android:theme="@style/WidgetContainerTheme">
+ android:theme="?attr/widgetsTheme"
+ launcher:revealBackground="@drawable/round_rect_primary">
<View
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
- android:focusable="false"
android:elevation="2dp"
+ android:focusable="false"
android:visibility="invisible" />
<FrameLayout
android:id="@+id/main_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:layout_gravity="center"
android:elevation="15dp"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:visibility="gone">
<com.android.launcher3.widget.WidgetsRecyclerView
android:id="@+id/widgets_list_view"
@@ -50,18 +50,24 @@
<!-- Fast scroller popup -->
<TextView
- style="@style/FastScrollerPopup"
- android:layout_below="@+id/search_container"
android:id="@+id/fast_scroller_popup"
+ style="@style/FastScrollerPopup"
android:layout_gravity="top|end"
- android:layout_marginEnd="@dimen/container_fastscroll_popup_margin" />
+ android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
<ProgressBar
+ android:id="@+id/loader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/loader"
android:layout_gravity="center" />
- </FrameLayout>
+ <com.android.launcher3.views.RecyclerViewFastScroller
+ android:id="@+id/fast_scroller"
+ android:layout_width="@dimen/fastscroll_width"
+ android:layout_height="match_parent"
+ android:layout_gravity="end"
+ android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+ </FrameLayout>
</com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
new file mode 100644
index 0000000..3c1b64f
--- /dev/null
+++ b/res/raw/downgrade_schema.json
@@ -0,0 +1,20 @@
+{
+ // Note: Comments are not supported in JSON schema, but android parser is lenient.
+
+ // Maximum DB version supported by this schema
+ "version" : 27,
+
+ // Downgrade from 27 to 26. Empty array indicates, the DB is compatible
+ "downgrade_to_26" : [],
+ "downgrade_to_25" : [],
+ "downgrade_to_24" : [],
+ "downgrade_to_23" : [],
+ "downgrade_to_22" : [
+ "ALTER TABLE favorites RENAME TO temp_favorites;",
+ "CREATE TABLE favorites(_id INTEGER PRIMARY KEY, title TEXT, intent TEXT, container INTEGER, screen INTEGER, cellX INTEGER, cellY INTEGER, spanX INTEGER, spanY INTEGER, itemType INTEGER, appWidgetId INTEGER NOT NULL DEFAULT - 1, iconPackage TEXT, iconResource TEXT, icon BLOB, appWidgetProvider TEXT, modified INTEGER NOT NULL DEFAULT 0, restored INTEGER NOT NULL DEFAULT 0, profileId INTEGER DEFAULT 0, rank INTEGER NOT NULL DEFAULT 0);",
+ "INSERT INTO favorites SELECT _id, title, intent, container, screen, cellX, cellY, spanX, spanY, itemType, appWidgetId, iconPackage, iconResource, icon, appWidgetProvider, modified, restored, profileId, rank FROM temp_favorites;",
+ "DROP TABLE temp_favorites;"
+ ]
+
+ // Missing values indicate the DB is not compatible
+}
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index e3f0868..fe00d7a 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Instellings"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home-instellings"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Oorsig"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Laat toe dat tuisskerm gedraai word"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gebruik stelselverstek"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Sirkelvierkant"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Sirkel"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Traandruppel"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 490a43b..aebb428 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ቅንብሮች"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"አጠቃላይ ዕይታ"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"የመነሻ ማያ ገጽ ማሽከርከርን ይፍቀዱ"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"የሥርዓቱን ነባሪ ተጠቀም"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ካሬ"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"ክብ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"የእንባ ጠብታ"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 0bf7fd6..a45e40e 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"الإعدادات"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"إعدادات الصفحة الرئيسية"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"عطَّل المشرف هذه الميزة"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"نظرة عامة"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"السماح بتدوير الشاشة الرئيسية"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"استخدام الإعداد الافتراضي للنظام"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"مربّع"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"رمز دائري مربّع"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"دائرة"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"رمز على شكل دمعة"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 85e715c..a8791b7 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"İcmal"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem defoltu istifadə edin"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kənarları dairəvi kvadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Çevrə"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
new file mode 100644
index 0000000..5cbe408
--- /dev/null
+++ b/res/values-az/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Tətbiq quraşdırılmayıb."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Tətbiq əlçatmazdır"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Güvənli rejimdə icazə verilməyən tətbiq endirildi"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Vidcetlər Güvənli rejimdə deaktiv edilib"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Qısayol əlçatan deyil"</string>
+ <string name="home_screen" msgid="806512411299847073">"Əsas ekran"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Fərdi əməliyyatlar"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidceti götürmək üçün toxunub saxlayın."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Vidceti götürmək üçün & iki dəfə toxunub saxlayın və ya fərdi fəaliyyətləri istifadə edin."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Manual olaraq yerləşdirmək üçün toxunaraq basıb saxlayın"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Avtomatik əlavə edin"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tətbiq Axtarın"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" sorğusuna uyğun Tətbiqlər tapılmadı"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Daha çox tətbiq üçün axtarış edin"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Bildirişlər"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Tətbiq siyahısı"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Əsas səhifə"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Silin"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq məlumatı"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Əsas Səhifə ayarlarını və qısayolları oxuyun"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Tətbiqə Əsas Səhifədə parametrləri və qısayolları oxumağa icazə verir."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Əsas Səhifə ayarlarını və qısayolları yazın"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Tətbiqə Əsas Səhifədə ayarları və qısayolları dəyişməyə icazə verir."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinə telefon zəngləri etmək üçün icazə verilmir"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Vidcet yükləmə problemi"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Quraşdırma"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Qovluq"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiv edildi"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Qovluq açıldı, <xliff:g id="HEIGHT">%2$d</xliff:g> hündürlük ilə <xliff:g id="WIDTH">%1$d</xliff:g> enində"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Qovluq bağlamaq üçün toxunun"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Qovluq bağlıdır"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Qovluq adı <xliff:g id="NAME">%1$s</xliff:g> ilə dəyişdirildi"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"İcmal"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon çevrilən zaman"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cari Ekran ayarı fırlatmağa icazə vermir"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Əsas ekrana ikona əlavə edin"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yeni tətbiqlər üçün"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Axtarış"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Bu tətbiq quraşdırılmayıb"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Bu ikona üçün tətbiq quraşdırılmayıb. Onu silə bilərsiniz, və ya tətbiqi taparaq manual yol ilə quraşdıra bilərsiniz."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> endirilir, <xliff:g id="PROGRESS">%2$s</xliff:g> tamamlandı"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> yüklənmək üçün gözləyir"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> vidcetləri"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Əsas ekrana əlavə edin"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Elementi bura köçürün"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Element əsas ekrana əlavə edildi"</string>
+ <string name="item_removed" msgid="851119963877842327">"Element silindi"</string>
+ <string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Sıra <xliff:g id="NUMBER_0">%1$s</xliff:g> sütun <xliff:g id="NUMBER_1">%2$s</xliff:g> köçürün"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> mövqeyinə köçürün"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> sevimlilər mövqeyinə köçürün"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Elementin yeri dəyişildi"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Qovluğa əlavə edin: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> adlı qovluğa əlavə edin"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Element qovluğa əlavə edildi"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Qovluq yaradın: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Qovluq yaradıldı"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Əsas ekrana köçürün"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Ekranı sola köçürün"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Ekranı sağa köçürün"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Ekran köçürülüb"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Ölçüsünü dəyişin"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Eni artırın"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Hündürlüyü artırın"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Eni azaldın"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Hündürlüyü azaldın"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Vidcetin eni <xliff:g id="NUMBER_0">%1$s</xliff:g> hündürlüyü <xliff:g id="NUMBER_1">%2$s</xliff:g> kimi ölçüləndirildi"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Qısa yollar"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> üçün <xliff:g id="APP_NAME">%2$s</xliff:g> qısa yolu"</string>
+</resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 79add48..cb296f4 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Podešavanja"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Podešavanja početnog ekrana"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator je onemogućio"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotaciju početnog ekrana"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Koristi podrazumevano sistemsko podešavanje"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index ad8054f..0a055a4 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Налады"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Агляд"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Выкарыстоўваць стандартныя формы"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Прамавугольнік са скругленымі вугламі"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сляза"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index fc5390b..8eb7e84 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -19,188 +19,103 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for application_name (5181331383435256801) -->
- <skip />
- <!-- no translation found for home (7658288663002113681) -->
- <skip />
- <!-- no translation found for uid_name (7820867637514617527) -->
- <skip />
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
- <!-- no translation found for wallpaper_instructions (563973358787555519) -->
+ <string name="work_folder_name" msgid="3753320833950115786">"Працоўная"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Праграма не ўсталявана."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Праграма недаступная"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Спампаваная праграма адключана ў Бяспечным рэжыме"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"Дакраніцеся і ўтрымлiвайце віджэт, каб выбр. яго."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць віджэт або выкарыстоўваць карыстальніцкія дзеянні."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Каб размясціць уручную, дакраніцеся і ўтрымлівайце"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Дадаць аўтаматычна"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук у Праграмах"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Ідзе загрузка праграм…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукаць іншыя праграмы"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Апавяшчэнні"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"На гэтым Галоўным экране больш няма месца."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"У латку \"Абранае\" больш няма месца"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Спіс праграм"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Галоўная"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Выдаліць"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Выдаліць"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Звесткі пра праграму"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"счытваць налады і ярлыкі на Галоўнай старонцы"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Дазваляе праграме счытваць налады і ярлыкі на Галоўнай старонцы."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"запісваць налады і ярлыкі на галоўнай старонцы"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Дазваляе праграме змяняць налады і ярлыкі на Галоўнай старонцы."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> не мае дазволу на здзяйсненне тэлефонных званкоў"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Праблема загрузкі віджэта"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Наладжванне"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Гэта сістэмная праграма, яе нельга выдаліць."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назвы"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> адключана"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"Старонка %1$d з %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Галоўны экран %1$d з %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Новая старонка галоўнага экрана"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Папка адкрыта, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Краніце, каб закрыць папку"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Краніце, каб захаваць новую назву"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Папка закрыта"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Папка перайменавана ў <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Налады"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Агляд"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Пры павароце тэлефона"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Бягучая налада дысплэя не прадугледжвае паварот"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Дадаць значок на Галоўны экран"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Для новых праграм"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
- <!-- no translation found for image_load_fail (2821429163328561136) -->
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
<skip />
- <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
- <!-- no translation found for number_of_items_selected:zero (7464587177007785408) -->
- <!-- no translation found for number_of_items_selected:one (142482526010824029) -->
- <!-- no translation found for number_of_items_selected:other (1418352074806573570) -->
- <!-- no translation found for wallpaper_accessibility_name (1655953108132967972) -->
- <skip />
- <!-- no translation found for announce_selection (8338254712932127413) -->
- <skip />
- <!-- no translation found for wallpaper_delete (8095005658756613921) -->
- <skip />
- <!-- no translation found for pick_image (1272073934062909527) -->
- <skip />
- <!-- no translation found for pick_wallpaper (8179698221502010609) -->
- <skip />
- <!-- no translation found for crop_wallpaper (8334345984491368009) -->
- <skip />
- <!-- no translation found for activity_not_found (8071924732094499514) -->
- <skip />
- <!-- no translation found for widgets_tab_label (2921133187116603919) -->
- <skip />
- <!-- no translation found for widget_adder (3201040140710381657) -->
- <skip />
- <!-- no translation found for toggle_weight_watcher (5645299835184636119) -->
- <skip />
- <!-- no translation found for long_press_widget_to_add (7699152356777458215) -->
- <skip />
- <!-- no translation found for market (2619650989819296998) -->
- <skip />
- <!-- no translation found for widget_dims_format (2370757736025621599) -->
- <skip />
- <!-- no translation found for external_drop_widget_error (3165821058322217155) -->
- <skip />
- <!-- no translation found for external_drop_widget_pick_title (3486317258037690630) -->
- <skip />
- <!-- no translation found for rename_folder_label (3727762225964550653) -->
- <skip />
- <!-- no translation found for rename_folder_title (3771389277707820891) -->
- <skip />
- <!-- no translation found for rename_action (5559600076028658757) -->
- <skip />
- <!-- no translation found for cancel_action (7009134900002915310) -->
- <skip />
- <!-- no translation found for menu_item_add_item (1264911265836810421) -->
- <skip />
- <!-- no translation found for group_applications (3797214114206693605) -->
- <skip />
- <!-- no translation found for group_shortcuts (6012256992764410535) -->
- <skip />
- <!-- no translation found for group_widgets (1569030723286851002) -->
- <skip />
- <!-- no translation found for completely_out_of_space (6106288382070760318) -->
- <skip />
- <!-- no translation found for out_of_space (4691004494942118364) -->
- <skip />
- <!-- no translation found for hotseat_out_of_space (9139760413395605841) -->
- <skip />
- <!-- no translation found for invalid_hotseat_item (1211534262129849507) -->
- <skip />
- <!-- no translation found for shortcut_installed (1701742129426969556) -->
- <skip />
- <!-- no translation found for shortcut_uninstalled (8176767991305701821) -->
- <skip />
- <!-- no translation found for shortcut_duplicate (9167217446062498127) -->
- <skip />
- <!-- no translation found for title_select_shortcut (6680642571148153868) -->
- <skip />
- <!-- no translation found for title_select_application (3280812711670683644) -->
- <skip />
- <!-- no translation found for all_apps_button_label (9110807029020582876) -->
- <skip />
- <!-- no translation found for all_apps_home_button_label (252062713717058851) -->
- <skip />
- <!-- no translation found for delete_zone_label_workspace (4009607676751398685) -->
- <skip />
- <!-- no translation found for delete_zone_label_all_apps (8083826390278958980) -->
- <skip />
- <!-- no translation found for delete_target_label (1822697352535677073) -->
- <skip />
- <!-- no translation found for delete_target_uninstall_label (5100785476250872595) -->
- <skip />
- <!-- no translation found for info_target_label (8053346143994679532) -->
- <skip />
- <!-- no translation found for accessibility_search_button (1628520399424565142) -->
- <skip />
- <!-- no translation found for accessibility_voice_search_button (4637324840434406584) -->
- <skip />
- <!-- no translation found for accessibility_all_apps_button (2603132375383800483) -->
- <skip />
- <!-- no translation found for accessibility_delete_button (6466114477993744621) -->
- <skip />
- <!-- no translation found for delete_zone_label_all_apps_system_app (449755632749610895) -->
- <skip />
- <!-- no translation found for cab_menu_delete_app (7435191475867183689) -->
- <skip />
- <!-- no translation found for cab_menu_app_info (8593722221450362342) -->
- <skip />
- <!-- no translation found for cab_app_selection_text (374688303047985416) -->
- <skip />
- <!-- no translation found for cab_widget_selection_text (1833458597831541241) -->
- <skip />
- <!-- no translation found for cab_folder_selection_text (7999992513806132118) -->
- <skip />
- <!-- no translation found for cab_shortcut_selection_text (2103811025667946450) -->
- <skip />
- <!-- no translation found for permlab_install_shortcut (5632423390354674437) -->
- <skip />
- <!-- no translation found for permdesc_install_shortcut (923466509822011139) -->
- <skip />
- <!-- no translation found for permlab_uninstall_shortcut (864595034498083837) -->
- <skip />
- <!-- no translation found for permdesc_uninstall_shortcut (5134129545001836849) -->
- <skip />
- <!-- no translation found for permlab_read_settings (1941457408239617576) -->
- <skip />
- <!-- no translation found for permdesc_read_settings (5833423719057558387) -->
- <skip />
- <!-- no translation found for permlab_write_settings (3574213698004620587) -->
- <skip />
- <!-- no translation found for permdesc_write_settings (5440712911516509985) -->
- <skip />
- <!-- no translation found for gadget_error_text (6081085226050792095) -->
- <skip />
- <!-- no translation found for uninstall_system_app_text (4172046090762920660) -->
- <skip />
- <!-- no translation found for dream_name (1530253749244328964) -->
- <skip />
- <!-- no translation found for folder_hint_text (6617836969016293992) -->
- <skip />
- <!-- no translation found for workspace_description_format (2950174241104043327) -->
- <skip />
- <!-- no translation found for default_scroll_format (7475544710230993317) -->
- <skip />
- <!-- no translation found for workspace_scroll_format (8458889198184077399) -->
- <skip />
- <!-- no translation found for apps_customize_apps_scroll_format (370005296147130238) -->
- <skip />
- <!-- no translation found for apps_customize_widgets_scroll_format (3106209519974971521) -->
- <skip />
- <!-- no translation found for first_run_cling_title (7257389003637362144) -->
- <skip />
- <!-- no translation found for first_run_cling_description (6447072552696253358) -->
- <skip />
- <!-- no translation found for first_run_cling_create_screens_hint (6950729526680114157) -->
- <skip />
- <!-- no translation found for workspace_cling_title (5626202359865825661) -->
- <skip />
- <!-- no translation found for workspace_cling_move_item (528201129978005352) -->
- <skip />
- <!-- no translation found for folder_cling_title (3894908818693254164) -->
- <skip />
- <!-- no translation found for folder_cling_create_folder (6158215559475836131) -->
- <skip />
- <!-- no translation found for cling_dismiss (8962359497601507581) -->
- <skip />
- <!-- no translation found for folder_opened (94695026776264709) -->
- <skip />
- <!-- no translation found for folder_tap_to_close (1884479294466410023) -->
- <skip />
- <!-- no translation found for folder_tap_to_rename (9191075570492871147) -->
- <skip />
- <!-- no translation found for folder_closed (4100806530910930934) -->
- <skip />
- <!-- no translation found for folder_renamed (1794088362165669656) -->
- <skip />
- <!-- no translation found for folder_name_format (6629239338071103179) -->
- <skip />
- <!-- no translation found for widget_button_text (2880537293434387943) -->
- <skip />
- <!-- no translation found for wallpaper_button_text (8404103075899945851) -->
- <skip />
- <!-- no translation found for settings_button_text (8119458837558863227) -->
- <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Выдаліць"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Шукаць"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Гэта праграма не ўсталявана"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Праграма для гэтага значка не ўсталявана. Вы можаце выдаліць яе або выканаць пошук і ўсталяваць яе ўручную."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Ідзе спампоўка <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завершана"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чакае ўсталёўкі"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Віджэты <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Дадаць на Галоўны экран"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Перамясціць элемент сюды"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент дададзены на галоўны экран"</string>
+ <string name="item_removed" msgid="851119963877842327">"Элемент выдалены"</string>
+ <string name="action_move" msgid="4339390619886385032">"Перамясціць элемент"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Перамясціць у радок <xliff:g id="NUMBER_0">%1$s</xliff:g> слупок <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Перамясціць у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Перамясціць у абранае, у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Элемент перамешчаны"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Дадаць у папку: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Дадаць у папку з <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Элемент дададзены ў папку"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Стварыць папку з: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Папка створана"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Перамясціць на Галоўны экран"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Перамясціць экран налева"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Перамясціць экран направа"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Экран перамешчаны"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Змяніць памер"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Павялічыць шырыню"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Павялічыць вышыню"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Паменшыць шырыню"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Паменшыць вышыню"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Памеры віджэта зменены на: шырыня <xliff:g id="NUMBER_0">%1$s</xliff:g>, вышыня <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Ярлыкі"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"Ярлыкі (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) для <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index fe02507..d5df3f2 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Настройки за Google Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Общ преглед"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Разрешаване на завъртането на началния екран"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Използване на стандартната системна настройка"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Комбинация от кръг и квадрат"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Кръг"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сълза"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 90c3a43..d4c5210 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"উইজেটগুলি"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"সেটিংস"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"হোম এর সেটিংস"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"এক নজরে"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"হোমস্ক্রীন ঘোরানোর অনুমতি দিন"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপ্লিকেশানগুলির জন্যে"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"আইকনের আকৃতি পরিবর্তন করুন"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"সিস্টেমের ডিফল্ট মান ব্যবহার করুন"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"চৌকো"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"চৌকো-গোলাকার"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"গোলাকার"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"চোখের জল"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"আইকনের আকৃতি পরিবর্তন করা হচ্ছে"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
new file mode 100644
index 0000000..065019a
--- /dev/null
+++ b/res/values-bn/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"কাজ"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"অ্যাপ্লিকেশান ইনস্টল করা নেই৷"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"অ্যাপ্লিকেশান অনুপলব্ধ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷"</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">"%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"নিজে যোগ করতে টাচ করে ধরে রাখুন"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"স্বয়ংক্রিয়ভাবে যোগ করুন"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"অ্যাপ্লিকেশানগুলি লোড হচ্ছে..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"বিজ্ঞপ্তি"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"অ্যাপ্লিকেশানগুলির তালিকা"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"হোম"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"সরান"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনস্টল করুন"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"অ্যাপ্লিকেশানের তথ্য"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"হোম সেটিংস এবং শর্টকাটগুলি পড়ে"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"হোমে অ্যাপ্লিকেশানটিকে সেটিংস এবং শর্টকাটগুলি পড়তে দেয়৷"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"হোম সেটিংস এবং শর্টকাটগুলি লেখে"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"হোমে অ্যাপ্লিকেশানটিকে সেটিংস এবং শর্টকাটগুলি পরিবর্তন করতে দেয়৷"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"ফোন কলগুলি করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g> এর অনুমতি নেই"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"উইজেট লোড হতে সমস্যা হয়েছে"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"সেটআপ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফোল্ডার"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম করা হয়েছে"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রীন"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ফোল্ডার খোলা হয়েছে, <xliff:g id="WIDTH">%1$d</xliff:g> বাই <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ফোল্ডার বন্ধ করতে আলতো চাপ দিন"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"পুনঃনামকরণ সংরক্ষণ করতে আলতো চাপ দিন"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ফোল্ডার বন্ধ করা হয়েছে"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"উইজেটগুলি"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"সেটিংস"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"এক নজরে"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"হোমস্ক্রীন ঘোরানোর অনুমতি দিন"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"যখন ফোনটি ঘোরানো হয়"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"বর্তমান প্রদর্শনের সেটিংস ঘোরানোর মঞ্জুরি দেয় না"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"হোম স্ক্রিনে আইকন যোগ করুন"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"নতুন অ্যাপ্লিকেশানগুলির জন্যে"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"আইকনের আকৃতি পরিবর্তন করুন"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"পরিবর্তন করবেন না"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"আইকনের আকৃতি পরিবর্তন করা হচ্ছে"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"অনুসন্ধান"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ডাউনলোড হচ্ছে <xliff:g id="PROGRESS">%2$s</xliff:g> সম্পন্ন হয়েছে"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ইনস্টলের অপেক্ষায় রয়েছে"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> উইজেট"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"হোম স্ক্রীনে যোগ করুন"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"এখানে আইটেম সরান"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"হোম স্ক্রীনে আইটেম যোগ করা হয়েছে"</string>
+ <string name="item_removed" msgid="851119963877842327">"আইটেম সরানো হয়েছে"</string>
+ <string name="action_move" msgid="4339390619886385032">"আইটেম সরান"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> এ সরান"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দসই অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"আইটেম সরানো হয়েছে"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ফোল্ডারে যোগ করুন: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> সহ ফোল্ডারে যোগ করা হয়েছে"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"আইটেম ফোল্ডারে যোগ করা হয়েছে"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"এর সাথে ফোল্ডার তৈরি করুন: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ফোল্ডার তৈরি করা হয়েছে"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"হোম স্ক্রীনে সরান"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"স্ক্রীন বাঁ দিকে সরান"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"স্ক্রীন ডান দিকে সরান"</string>
+ <string name="screen_moved" msgid="266230079505650577">"স্ক্রীন সরানো হয়েছে"</string>
+ <string name="action_resize" msgid="1802976324781771067">"আবার আকার দিন"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"প্রস্থ বাড়ান"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"উচ্চতা বাড়ান"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"প্রস্থ কমান"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"উচ্চতা কমান"</string>
+ <string name="widget_resized" msgid="9130327887929620">"উইজেটের আকার প্রস্থ <xliff:g id="NUMBER_0">%1$s</xliff:g> উচ্চতা <xliff:g id="NUMBER_1">%2$s</xliff:g> তে পরিবর্তন করা হয়েছে"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"শর্টকাট"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> এর <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>টি শর্টকার্ট"</string>
+</resources>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index 5e26525..d91106b 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Dodaci"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Postavke za Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Koristite sistemski zadano"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
new file mode 100644
index 0000000..8e1234e
--- /dev/null
+++ b/res/values-bs/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Posao"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija je onemogućena u sigurnom načinu rada"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Vidžeti su onemogućeni u sigurnom načinu rada."</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
+ <string name="home_screen" msgid="806512411299847073">"Početni ekran"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Prilagođene akcije"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite & i držite da biste uzeli dodatak."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite & i držite da biste uzeli vidžet ili koristite prilagođene radnje."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Dodirnite i držite da postavite ručno"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Dodaj automatski"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija koja odgovara upitu \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Obavještenja"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Favoriti"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Spisak aplikacija"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Tipka za početak"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacije o aplikaciji"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"čitaj postavke na početnom ekranu i prečice"</string>
+ <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="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">"Neimenovana fascikla"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Fascikla je otvorena, (š) <xliff:g id="WIDTH">%1$d</xliff:g> (v) <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da zatvorite folder"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da sačuvate promjenu imena"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Fascikla je zatvorena"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Ime fascikle je promijenjeno u <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Fascikla: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Dodaci"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutne postavke ekrana ne dozvoljavaju rotiranje"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Dodajte ikonu na početni ekran"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Za nove aplikacije"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ova aplikacija nije instalirana"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija za ovu ikonu nije instalirana. Možete je ukloniti ili potražiti aplikaciju i ručno je instalirati."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se preuzima, završeno <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> čeka da se instalira"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Vidžeti za aplikaciju <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Dodaj na početni ekran"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Premjesti stavku ovdje"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Stavka je dodana na Početni ekran."</string>
+ <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
+ <string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među favoritima"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Dodaj u folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Dodaj u folder sa aplikacijom <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Stavka je dodana u folder"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Kreirajte folder sa stavkom: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Folder je kreiran"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Pomjeri na početni ekran"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Pomjeri ekran ulijevo"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Pomjeri ekran udesno"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Ekran je pomjeren"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Promijeni veličinu"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Povećaj širinu"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Povećaj visinu"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Smanji širinu"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Smanji visinu"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Veličina vidžeta je promijenjena na širinu <xliff:g id="NUMBER_0">%1$s</xliff:g> visinu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Prečice"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> prečica za aplikaciju <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index f91c144..88b02bb 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -70,20 +70,24 @@
<string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Configuració"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configuració de la pantalla d\'inici"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Visió general"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permet la rotació de la pantalla d\'inici"</string>
<string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
<string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuració actual de la pantalla no permet la rotació"</string>
<string name="icon_badging_title" msgid="874121399231955394">"Punts de notificació"</string>
- <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activat"</string>
- <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivat"</string>
+ <string name="icon_badging_desc_on" msgid="2627952638544674079">"Activada"</string>
+ <string name="icon_badging_desc_off" msgid="5503319969924580241">"Desactivada"</string>
<string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Afegeix la icona a la pantalla d\'inici"</string>
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Per a les aplicacions noves"</string>
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utilitza l\'opció predeterminada del sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrat arrodonit"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Llàgrima"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index cd21d4e..1a80e39 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Složka: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Nastavení"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Přehled"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Povolit otáčení plochy"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Použít výchozí nastavení systému"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Čtverec"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kruh/čtverec"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Kruh"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Slza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index ef7ee50..17316b3 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Indstillinger"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærmen"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Oversigt"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Brug systemstandarden"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrat med runde hjørner"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dråbeform"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 58576ca..72f7399 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Einstellungen"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen für den Startbildschirm"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Übersicht"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Drehung des Startbildschirms zulassen"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Systemstandardeinstellung verwenden"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Quadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Superkreis"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Kreis"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Träne"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index dc8d6ba..406101c 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Ρυθμίσεις"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχικής σελίδας"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Επισκόπηση"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Χρήση προεπιλογής συστήματος"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Τετράγωνο"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Στρογγυλεμένο τετράγωνο"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Κύκλος"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Δάκρυ"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 8827cf5..4e0ba03 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 8827cf5..4e0ba03 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 8827cf5..4e0ba03 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Use system default"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Square"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Circle"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index b728a6d..bef4757 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configuración de Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Recientes"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir la rotación de la pantalla principal"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar el sistema predeterminado"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gota"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 431b019..e49ac3f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Ajustes"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Ajustes de Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Visión general"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotación de la pantalla de inicio"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar opción predeterminada del sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Cuadrado"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Cuadrado con esquinas redondeadas"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index bd1f94d..f5ea456 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Seaded"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Avalehe seaded"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Ülevaade"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Kasuta süsteemi vaikeseadet"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Ruut"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Ümarate nurkadega ruut"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Ring"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tilk"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index afed3b5..91a7333 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -19,35 +19,103 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name" msgid="8424725141379931883">"Käivitaja"</string>
- <string name="folder_name" msgid="8551881338202938211"></string>
- <string name="wallpaper_instructions" msgid="4215640646180727542">"Määra taustapilt"</string>
- <string name="pick_wallpaper" msgid="5630222540525626723">"Taustapildid"</string>
- <string name="activity_not_found" msgid="217823393239365967">"Rakendus pole installitud."</string>
- <string name="long_press_widget_to_add" msgid="7395697462851217506">"Vidina valimiseks puudutage seda pikalt."</string>
- <string name="widget_dims_format" msgid="1386418557719032947">"%1$d × %2$d"</string>
- <string name="out_of_space" msgid="8365249326091984698">"Sellel avalehel pole enam ruumi."</string>
- <string name="hotseat_out_of_space" msgid="6304886797358479361">"Kohandataval dokialal pole rohkem ruumi."</string>
- <string name="all_apps_button_label" msgid="2578400570124163469">"Rakendused"</string>
- <string name="all_apps_home_button_label" msgid="1022222300329398558">"Kodu"</string>
- <string name="delete_target_label" msgid="665300185123139530">"Eemalda"</string>
- <string name="delete_target_uninstall_label" msgid="748894921183769150">"Desinstalli"</string>
- <string name="info_target_label" msgid="4019495079517426980">"Rakenduse teave"</string>
- <string name="permlab_install_shortcut" msgid="1201690825493376489">"otseteede installimine"</string>
- <string name="permdesc_install_shortcut" msgid="8634424803272077038">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
- <string name="permlab_read_settings" msgid="3452408290738106747">"avalehe seadete ja otseteede lugemine"</string>
- <string name="permdesc_read_settings" msgid="5788109303585403679">"Võimaldab rakendusel lugeda avalehe seadeid ja otseteid."</string>
- <string name="permlab_write_settings" msgid="1360567537236705628">"avalehe seadete ja otseteede kirjutamine"</string>
- <string name="permdesc_write_settings" msgid="8530105489115785531">"Võimaldab rakendusel muuta avalehel seadeid ja otseteid."</string>
- <string name="gadget_error_text" msgid="8359351016167075858">"Probleem vidina laadimisel"</string>
- <string name="uninstall_system_app_text" msgid="6429814133777046491">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
- <string name="folder_hint_text" msgid="8633351560105748141">"Nimeta kaust"</string>
- <string name="default_scroll_format" msgid="4057140866420001240">"Leht %1$d/%2$d"</string>
- <string name="workspace_scroll_format" msgid="1704767047951143301">"Avakuva %1$d/%2$d"</string>
- <string name="folder_opened" msgid="1262064100943801533">"Kaust on avatud, <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="1335478160661137579">"Puudutage kausta sulgemiseks"</string>
- <string name="folder_tap_to_rename" msgid="5201612989905472442">"Puudutage uue nime salvestamiseks"</string>
- <string name="folder_closed" msgid="3130534551370511932">"Kaust suletud"</string>
- <string name="folder_renamed" msgid="7951233572858053642">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format" msgid="3051680259794759037">"<xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Töö"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Rakendus pole installitud."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Rakendus ei ole saadaval"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Allalaetud rakendus on turvarežiimis keelatud"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Turvarežiimis on vidinad keelatud"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Otsetee pole saadaval"</string>
+ <string name="home_screen" msgid="806512411299847073">"Avaekraan"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Kohandatud toimingud"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidina valimiseks vajutage ja hoidke seda all."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Topeltpuudutage ja hoidke vidina valimiseks või kohandatud toimingute kasutamiseks."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Puudutage pikalt, et käsitsi asetada"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Lisa automaatselt"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Otsige rakendustest"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Rakenduste laadimine ..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Otsi rohkem rakendusi"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Märguanded"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Sellel avaekraanil pole enam ruumi."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Rakenduste loend"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Avaekraan"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"loe avaekraani seadeid ja otseteid"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Võimaldab rakendusel lugeda avaekraanil seadeid ja otseteid."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"kirjuta avaekraani seaded ja otseteed"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Võimaldab rakendusel muuta avaekraanil seadeid ja otseteid."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"Rakendusel <xliff:g id="APP_NAME">%1$s</xliff:g> pole lubatud helistada"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Probleem vidina laadimisel"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Seadistamine"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Nimetu kaust"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on keelatud"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Kaust on avatud, <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">"Puudutage kausta sulgemiseks"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Kaust on suletud"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Seaded"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Ülevaade"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Praegune kuvaseade ei luba pööramist"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Lisa ikoon avaekraanile"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Uute rakenduste puhul"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Otsing"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"See rakendus ei ole installitud"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Rakenduse <xliff:g id="NAME">%1$s</xliff:g> allalaadimine, <xliff:g id="PROGRESS">%2$s</xliff:g> on valmis"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> on installimise ootel"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Teenuse <xliff:g id="NAME">%1$s</xliff:g> vidinad"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Lisa avaekraanile"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Teisalda üksus siia"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Üksus lisati avaekraanile"</string>
+ <string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string>
+ <string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Teisaldamine <xliff:g id="NUMBER_0">%1$s</xliff:g>. rea <xliff:g id="NUMBER_1">%2$s</xliff:g>. veergu"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Teisaldamine <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Teisaldamine lemmikute <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Üksus teisaldati"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Lisamine kausta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Lisamine kausta nimega <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Üksus lisati kausta"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Kausta loomine nimega <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Kaust on loodud"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Teisalda avaekraanile"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Teisalda ekraan vasakule"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Teisalda ekraan paremale"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Ekraan teisaldati"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Muuda suurust"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Suurenda laiust"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Suurenda kõrgust"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Vähenda laiust"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Vähenda kõrgust"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Vidina suurust muudeti. Laius: <xliff:g id="NUMBER_0">%1$s</xliff:g>. Kõrgus: <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Otseteed"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> otseteed rakenduse <xliff:g id="APP_NAME">%2$s</xliff:g> jaoks"</string>
</resources>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 57353e1..59fc221 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Ezarpenak"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Ikuspegi orokorra"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Erabili sistemaren balio lehenetsiak"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Karratua"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Ertz biribilduko karratua"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Zirkulua"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Malkoa"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
new file mode 100644
index 0000000..1a4d809
--- /dev/null
+++ b/res/values-eu/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Lana"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Aplikazioa instalatu gabe dago."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Ez dago erabilgarri aplikazioa"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Deskargatutako aplikazioa modu seguruan desgaitu da"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Widgetak desgaitu egin dira modu seguruan"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Lasterbideak ez daude erabilgarri"</string>
+ <string name="home_screen" msgid="806512411299847073">"Hasierako pantaila"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Ekintza pertsonalizatuak"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Eduki sakatuta widgeta aukeratzeko."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Sakatu birritan eta eduki sakatuta widgeta aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Eduki sakatuta eskuz gehitzeko"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Gehitu automatikoki"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Bilatu aplikazioetan"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikazioak kargatzen…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketarekin bat datorren aplikaziorik"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Bilatu aplikazio gehiago"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Jakinarazpenak"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Aplikazioen zerrenda"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Hasiera"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Kendu"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Hasierako pantailako ezarpenak eta lasterbideak irakurtzea baimentzen die aplikazioei."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Idatzi hasierako ezarpenak eta lasterbideak"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Hasierako pantailako ezarpenak eta lasterbideak aldatzea baimentzen die aplikazioei."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak ez du telefono-deiak egiteko baimenik"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Arazo bat izan da widgeta kargatzean"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurazioa"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Izenik gabeko karpeta"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desgaituta dago"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <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">"Karpeta ixteko, sakatu hau"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Izen berria gordetzeko, sakatu hau"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Karpeta itxi da"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Ezarpenak"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Ikuspegi orokorra"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzen denean"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Uneko pantaila-ezarpenak ez du onartzen ikuspegia biratzea"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Gehitu ikonoa hasierako pantailan"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Aplikazio berrietan"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Bilatu"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikazio hau ez dago instalatuta"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> deskargatzen, <xliff:g id="PROGRESS">%2$s</xliff:g> osatuta"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> instalatzeko zain"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> widgetak"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Gehitu hasierako pantailan"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Ekarri elementua hona"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Gehitu da elementua hasierako pantailan"</string>
+ <string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string>
+ <string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Eraman <xliff:g id="NUMBER_0">%1$s</xliff:g>. errenkadara, <xliff:g id="NUMBER_1">%2$s</xliff:g>. zutabera"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Eraman <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Eraman gogokoen <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Elementua mugitu da"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Gehitu <xliff:g id="NAME">%1$s</xliff:g> karpetan"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Gehitu <xliff:g id="NAME">%1$s</xliff:g> duen karpetan"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Elementua karpetan gehitu da"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Sortu karpeta <xliff:g id="NAME">%1$s</xliff:g> elementuarekin"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Karpeta sortu da"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Eraman hasierako pantailara"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Eraman pantaila ezkerrera"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Eraman pantaila eskuinera"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Mugitu da pantaila"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Aldatu tamaina"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Handitu zabalera"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Handitu altuera"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Txikitu zabalera"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Txikitu altuera"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Aldatu da widgetaren tamaina. Zabalera: <xliff:g id="NUMBER_0">%1$s</xliff:g>. Altuera: <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Lasterbideak"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioaren <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> lasterbide"</string>
+</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 3be4cb3..4edc4e7 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ابزارکها"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواریها"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"تنظیمات"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"نمای کلی"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"امکان دادن به چرخش صفحه اصلی"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"استفاده از پیشفرض سیستم"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"مربع با گوشههای گرد"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"دایره"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"قطره اشک"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index b2805e5..f24cbae 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Asetukset"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Kotiasetukset"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Yleiskatsaus"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Salli aloitusnäytön kiertäminen"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Käytä järjestelmän oletusarvoa"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Neliö"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Ympyräneliö"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Ympyrä"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pisara"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index e4a3d2e..5a3bd54 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Présentation"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser les valeurs système par défaut"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Carré aux coins ronds"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goutte"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 6b01d40..2897af0 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Dossier \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Paramètres du domicile"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Vue d\'ensemble"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utiliser la valeur système par défaut"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Carré"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cercle"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goutte"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 1e10384..9211ba1 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configuración de inicio"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Visión xeral"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar valores predeterminados do sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Cadrado"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Cadrado de bordos redondeados"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Bágoa"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
new file mode 100644
index 0000000..e8213b5
--- /dev/null
+++ b/res/values-gl/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Traballo"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"A aplicación non está instalada"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"A aplicación non está dispoñible"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"A aplicación que descargaches está desactivada no modo seguro"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Os widgets están desactivados no modo seguro"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"O atallo non está dispoñible"</string>
+ <string name="home_screen" msgid="806512411299847073">"Pantalla de inicio"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Accións personalizadas"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premido un widget para seleccionalo."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dúas veces e mantén premido para seleccionar un widget ou utiliza accións personalizadas."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén premido o elemento para colocalo manualmente"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Engadir automaticamente"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Aplicacións de busca"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicacións..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar máis aplicacións"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Notificacións"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicacións"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminar"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite a unha aplicación ler a configuración e os atallos da páxina de inicio."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"modificar a configuración e os atallos da pantalla de inicio"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite a unha aplicación cambiar a configuración e os atallos da pantalla de inicio."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> non ten permiso para facer chamadas telefónicas"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Produciuse un problema ao cargar o widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Cartafol sen nome"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"Desactivouse <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Abriuse o cartafol, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca fóra para pechar o cartafol"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca fóra para cambiar o nome do cartafol"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Pechouse o cartafol"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión xeral"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuración de visualización actual non permite xirar a pantalla"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Engadir icona á pantalla de inicio"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Para novas aplicacións"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación non está instalada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Engadir á pantalla de inicio"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Engadiuse o elemento á pantalla de inicio"</string>
+ <string name="item_removed" msgid="851119963877842327">"Eliminouse o elemento"</string>
+ <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g> columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Mover á posición <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover á posición dos favoritos <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Moveuse o elemento"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Engadir ao cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Engadir ao cartafol con <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Engadiuse o elemento ao cartafol"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Crear cartafol con: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Creouse o cartafol"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Mover á pantalla de inicio"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Mover pantalla á esquerda"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Mover pantalla á dereita"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Moveuse a pantalla"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Cambiar tamaño"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Aumentar ancho"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Aumentar altura"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Reducir ancho"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Reducir altura"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Cambiouse o tamaño do widget polo ancho <xliff:g id="NUMBER_0">%1$s</xliff:g> e a altura <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Atallos"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atallos para <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index aeb1e3d..b491e42 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"સેટિંગ્સ"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ્સ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"વિહંગાવલોકન"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"નવી ઍપ્લિકેશનો માટે"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"આઇકનનો આકાર બદલો"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"સિસ્ટમ ડિફૉલ્ટનો ઉપયોગ કરો"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ચોરસ"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ચોરસ જેવું ગોળ"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"વર્તુળ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"ટિઅરડ્રોપ"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"આઇકનના આકારમાં કરેલ ફેરફારો લાગુ કરી રહ્યા છીએ"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"અજાણ્યો"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
new file mode 100644
index 0000000..aa4992d
--- /dev/null
+++ b/res/values-gu/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ઍપ્લિકેશન ઇન્સ્ટોલ થઈ નથી."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"ઍપ્લિકેશન ઉપલબ્ધ નથી"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"સુરક્ષિત મોડમાં ડાઉનલોડ કરેલ ઍપ્લિકેશન અક્ષમ કરી"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"વિજેટ ચૂંટવા માટે ટચ કરો અને પકડી રાખો."</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 પહોળાઈ X %2$d ઊંચાઈ"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"મેન્યુઅલી મૂકવા માટે ટચ કરી દબાવી રાખો"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"આપમેળે ઉમેરો"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"શોધ ઍપ્લિકેશનો"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"વધુ ઍપ્લિકેશનો શોધો"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"સૂચનાઓ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ઍપ્લિકેશનોની સૂચિ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"હોમ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"દૂર કરો"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"અનઇન્સ્ટોલ કરો"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ઍપ્લિકેશન માહિતી"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"એપ્લિકેશનને હોમમાં સેટિંગ્સ અને શોર્ટકટ્સ વાંચવાની મંજૂરી આપે છે."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ લખો"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"એપ્લિકેશનને હોમમાં સેટિંગ્સ અને શોર્ટકટ્સ બદલવાની મંજૂરી આપે છે."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ને ફોન કૉલ્સ કરવાની મંજૂરી નથી"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"વિજેટ લોડ કરવામાં સમસ્યા"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"સેટઅપ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ ઍપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"અનામી ફોલ્ડર"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> અક્ષમ કરી"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="WIDTH">%1$d</xliff:g> બાય <xliff:g id="HEIGHT">%2$d</xliff:g> નું ફોલ્ડર ખોલ્યું"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ફોલ્ડર બંધ કરવા માટે ટૅપ કરો"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"નામ બદલવાનું સાચવવા માટે ટૅપ કરો"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ફોલ્ડર બંધ કર્યું"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ફોલ્ડરનું નામ બદલીને <xliff:g id="NAME">%1$s</xliff:g> કર્યું"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"સેટિંગ્સ"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"વિહંગાવલોકન"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"હોમ સ્ક્રીન પર આઇકન ઉમેરો"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"નવી ઍપ્લિકેશનો માટે"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"આઇકનનો આકાર બદલો"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"બદલશો નહીં"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"આઇકનના આકારમાં કરેલ ફેરફારો લાગુ કરી રહ્યા છીએ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"અજાણ્યો"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"શોધો"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"આ ઍપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"આ આયકન માટેની ઍપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી. તમે તેને દૂર કરી શકો છો અથવા ઍપ્લિકેશન માટે શોધ કરી અને તેને મેન્યુઅલી ઇન્સ્ટોલ કરી શકો છો."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> વિજેટ"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"હોમ સ્ક્રીન પર ઉમેરો"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"આઇટમ અહીં ખસેડો"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"હોમ સ્ક્રીનમાં આઇટમ ઉમેરી"</string>
+ <string name="item_removed" msgid="851119963877842327">"આઇટમ દૂર કરી"</string>
+ <string name="action_move" msgid="4339390619886385032">"આઇટમ ખસેડો"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> પંક્તિ <xliff:g id="NUMBER_1">%2$s</xliff:g> કૉલમ પર ખસેડો"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> સ્થિતિ પર ખસેડો"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"મનપસંદ સ્થિતિ <xliff:g id="NUMBER">%1$s</xliff:g> પર ખસેડો"</string>
+ <string name="item_moved" msgid="4606538322571412879">"આઇટમ ખસેડી"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ફોલ્ડરમાં ઉમેરો: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> સાથે ફોલ્ડરમાં ઉમેરો"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ફોલ્ડરમાં આઇટમ ઉમેરી"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"આની સાથે ફોલ્ડર બનાવો: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ફોલ્ડર બનાવ્યું"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"હોમ સ્ક્રીન પર ખસેડો"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"સ્ક્રીનને ડાબી બાજુ ખસેડો"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"સ્ક્રીનને જમણી બાજુ ખસેડો"</string>
+ <string name="screen_moved" msgid="266230079505650577">"સ્ક્રીન ખસેડી"</string>
+ <string name="action_resize" msgid="1802976324781771067">"આકાર બદલો"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"પહોળાઈ વધારો"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"ઊંચાઈ વધારો"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"પહોળાઈ ઘટાડો"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"ઊંચાઈ ઘટાડો"</string>
+ <string name="widget_resized" msgid="9130327887929620">"વિજેટનો આકાર બદલીને <xliff:g id="NUMBER_0">%1$s</xliff:g> પહોળાઈ <xliff:g id="NUMBER_1">%2$s</xliff:g> ઊંચાઈ કર્યો"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"શૉર્ટકટ્સ"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> માટે <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> શૉર્ટકટ"</string>
+</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index d4d3533..371f6b7 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"शॉर्टकट"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके व्यवस्थापक द्वारा अक्षम"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"अवलोकन"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"होमस्क्रीन घुमाने की अनुमति दें"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टम डिफ़ॉल्ट का उपयोग करें"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"गोल कोनों वाला वर्ग"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"मंडली"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"आंसू की बूंद"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index e1ff1df..0912677 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Postavke Homea"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Dopusti zakretanje početnog zaslona"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Upotrijebi zadane postavke sustava"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljeni kvadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Krug"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Suza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 4884633..3ffd508 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Beállítások"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"A Home beállításai"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Áttekintés"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"A kezdőképernyő elforgatásának engedélyezése"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Alapértelmezett érték használata"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Négyzet"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Kör"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Könnycsepp"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 0195d6e..1646f9e 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Կարգավորումներ"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Գլխավոր էջի կարգավորումներ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Համատեսք"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Օգտագործել համակարգի կանխադրված կարգավորումը"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Քառակուսի"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Քառանկյուն"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Օղակ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Արցունքաձև"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
new file mode 100644
index 0000000..7c39a3f
--- /dev/null
+++ b/res/values-hy/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Աշխատանքային"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Ծրագիրը տեղադրված չէ:"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Հավելվածը հասանելի չէ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"Հպեք և պահեք՝ վիջեթն ընտրելու համար:"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Կրկնակի հպեք և պահեք՝ վիջեթ ավելացնելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Լայնությունը՝ %1$d, բարձրությունը՝ %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Հպեք և պահեք՝ ձեռքով տեղադրելու համար"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Ավելացնել ավտոմատ կերպով"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Հավելվածների որոնում"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Հավելվածների բեռնում…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Որոնել այլ հավելվածներ"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Ծանուցումներ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Այլևս տեղ չկա այս հիմնական էկրանին:"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Հավելվածների ցանկ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Հիմնական"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Հեռացնել"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Հեռացնել"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի տվյալներ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"տեղադրել դյուրանցումներ"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Ծրագրին թույլ է տալիս կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները:"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ստեղծել հիմնաէջի կարգավորումներ ու դյուրանցումներ"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Ծրագրին թույլ է տալիս փոփոխել հիմնաէջի կարգավորումներն ու դյուրանցումները:"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածին չի թույլատրվում հեռախոսազանգեր կատարել"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Վիջեթի բեռնման խնդիր կա"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Կարգավորում"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Անանուն պանակ"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն անջատված է"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"Էջ %1$d՝ %2$d-ից"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Հիմնական էկրան %1$d` %2$d-ից"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Հիմնական էկրանի նոր էջ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Պանակը բաց է, <xliff:g id="WIDTH">%1$d</xliff:g>-ից <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Հպեք՝ պանակը փակելու համար"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Հպեք՝ նոր անվանումը պահելու համար"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Պանակը փակ է"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Կարգավորումներ"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Համատեսք"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Հեռախոսը պտտելու դեպքում"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ցուցադրման ընթացիկ կարգավորումներն արգելում են պտտումը"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Ավելացնել պատկերակը Հիմնական էկրանին"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Նոր հավելվածների համար"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Հեռացնել"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Գտնել"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Այս ծրագիրը տեղադրված չէ:"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>–ի ներբեռնում (<xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>-ի տեղադրման սպասում"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> վիջեթներ"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Ավելացնել Հիմնական էկրանին"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Տեղափոխել տարրն այստեղ"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Տարրն ավելացվեց հիմնական էկրանին"</string>
+ <string name="item_removed" msgid="851119963877842327">"Տարրը հեռացվեց"</string>
+ <string name="action_move" msgid="4339390619886385032">"Տեղափոխել տարրը"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Տեղափոխել տող <xliff:g id="NUMBER_0">%1$s</xliff:g> սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Տեղափոխել դիրք <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Տեղափոխել նախընտրած դիրք՝ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Տարրը տեղափոխվեց"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Ավելացնել թղթապանակում՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Ավելացնել «<xliff:g id="NAME">%1$s</xliff:g>» պանակին"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Տարրն ավելացվեց թղթապանակում"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Ստեղծել թղթապանակ, օգտագործելով՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Պանակը ստեղծվեց"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Տեղափոխել Հիմնական էկրան"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Տեղափոխել էկրանը ձախ"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Տեղափոխել էկրանը աջ"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Էկրանը տեղափոխվեց"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Չափափոխել"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Ավելացնել լայնությունը"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Ավելացնել բարձրությունը"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Նվազեցնել լայնությունը"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Նվազեցնել բարձրությունը"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Վիջեթի լայնությունը փոխվել է <xliff:g id="NUMBER_0">%1$s</xliff:g>-ի, իսկ բարձրությունը՝ <xliff:g id="NUMBER_1">%2$s</xliff:g>-ի"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Դյուրանցումներ"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> դյուրանցումներ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածի համար"</string>
+</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 44548ff..5634963 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Setelan"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Setelan layar Utama"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Ringkasan"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan layar Utama diputar"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan default sistem"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Persegi"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Persegi bundar"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Lingkaran"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Butiran Air"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 36b92c4..853cd3b 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Stillingar"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Heimastillingar"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Yfirlit"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning fyrir heimaskjá"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Nota sjálfgildi kerfis"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Ferningur"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Ferhringur"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Hringur"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dropi"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
new file mode 100644
index 0000000..3694a10
--- /dev/null
+++ b/res/values-is/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Vinna"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Forritið er ekki uppsett."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Forritið er ekki í boði"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Sótt forrit er óvirkt í öryggisstillingu"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Græjur eru óvirkar í öruggri stillingu"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Flýtileið er ekki tiltæk"</string>
+ <string name="home_screen" msgid="806512411299847073">"Heimaskjár"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Sérsniðnar aðgerðir"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Haltu fingri á græju til að grípa hana."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ýttu tvisvar og haltu fingri á græju til að grípa hana eða notaðu sérsniðnar aðgerðir."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Haltu inni til að staðsetja handvirkt"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Bæta sjálfkrafa við"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Leita í forritum"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Hleður forrit…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Leita að fleiri forritum"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Tilkynningar"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Forritalisti"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Heim"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjarlægja"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Leyfir forriti að lesa stillingar og flýtileiðir heimaskjás."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"skrifa stillingar og flýtileiðir heimaskjás"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Leyfir forriti að breyta stillingum og flýtileiðum heimaskjás."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> hefur ekki leyfi til að hringja símtöl"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Vandamál við að hlaða græju"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Uppsetning"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Ónefnd mappa"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Mappa opnuð, <xliff:g id="WIDTH">%1$d</xliff:g> sinnum <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Ýttu til að loka möppunni"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ýttu til að vista breytt heiti"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Möppu lokað"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Stillingar"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Yfirlit"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning fyrir heimaskjá"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Núverandi skjástilling leyfir ekki snúning"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bæta tákni á heimaskjáinn"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Fyrir ný forrit"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Leita"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Þetta forrit er ekki uppsett"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> í niðurhali, <xliff:g id="PROGRESS">%2$s</xliff:g> lokið"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> bíður uppsetningar"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-græjur"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Bæta á heimaskjá"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Færa atriði hingað"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Atriði bætt á heimaskjáinn"</string>
+ <string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string>
+ <string name="action_move" msgid="4339390619886385032">"Færa atriði"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Færa í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g> á festisvæði"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Atriði fært"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Setja í möppu: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Setja í möppu með <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Atriði sett í möppu"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Búa til möppu með: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Mappa búin til"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Færa á heimaskjá"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Færa skjá til vinstri"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Færa skjá til hægri"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Skjár færður"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Breyta stærð"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Auka breidd"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Auka hæð"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Minnka breidd"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Minnka hæð"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Stærð græju breytt í <xliff:g id="NUMBER_0">%1$s</xliff:g> á breidd og <xliff:g id="NUMBER_1">%2$s</xliff:g> á hæð"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Flýtileiðir"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> flýtileiðir fyrir <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 0a015d8..8af098d 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Impostazioni"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Impostazioni Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Panoramica"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Consenti rotazione della schermata Home"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usa impostazione predefinita di sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Quadrato"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Supercerchio"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cerchio"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Goccia"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 6203c18..2425254 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"תיקיה: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"רכיבי ווידג\'ט"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"הגדרות"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"הגדרות דף הבית"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"סקירה"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"אפשרות סיבוב של מסך דף הבית"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"השתמש בברירת המחדל של המערכת"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ריבוע"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ריבוע בעל פינות מעוגלות"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"מעגל"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"טיפה"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 4c1d953..cb5fa15 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ホームの設定"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"概要"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ホーム画面の回転を許可"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"システムのデフォルトを使用"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"スクエア"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"スクワークル"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"サークル"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"ティアドロップ"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index c40c050..2634cdf 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"პარამეტრები"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"მთავარი გვერდის პარამეტრები"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"მიმოხილვა"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"ნაგულისხმევი სისტემური პარამეტრების გამოყენება"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"კვადრატი"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"წრეკუთხედი"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"წრე"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"წვეთი"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
new file mode 100644
index 0000000..1979449
--- /dev/null
+++ b/res/values-ka/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"სამუშაო"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"აპი არ არის დაყენებული."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"აპი მიუწვდომელია"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ორმაგად შეეხეთ და გეჭიროთ ვიჯეტის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"სიგრძე: %1$d, სიგანე: %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ხანგრძლივად შეეხეთ ხელით განსათავსებლად"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"ავტომატურად დამატება"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"აპების ძიება"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"აპები იტვირთება..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"მეტი აპის პოვნა"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"შეტყობინებები"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"აპების სია"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"მთავარი"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ამოშლა"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"დეინსტალაცია"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"აპის შესახებ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"აპისთვის მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვის უფლების მიცემა."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების ჩაწერა"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"აპისთვის მთავარი ეკრანის პარამეტრებისა და მალსახმობების შეცვლის უფლების მიცემა."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ს არ აქვს სატელეფონო ზარების განხორციელების უფლება"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"პრობლემა ვიჯეტის ჩატვირთვისას"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"დაყენება"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"უსახელო საქაღალდე"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაითიშა"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"გვერდი %1$d %2$d-დან"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"მთავარი ეკრანი %1$d, %2$d-დან"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"მთავარი ეკრანის ახალი გვერდი"</string>
+ <string name="folder_opened" msgid="94695026776264709">"საქაღალდე გახსნილია, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"შეეხეთ საქაღალდის დასახურად"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"შეეხეთ გადარქმეული სახელის შესანახად"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"საქაღალდე დაიხურა"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"საქაღალდეს შეეცვალა სახელი „<xliff:g id="NAME">%1$s</xliff:g>“-ად"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"პარამეტრები"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"მიმოხილვა"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ტელეფონის შეტრიალებისას"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ბრუნვა დაუშვებელია ჩვენების მიმდინარე პარამეტრებით"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ხატულას მთავარ ეკრანზე დამატება"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ახალი აპებისთვის"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ძიება"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ეს აპი დაყენებული არ არის"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"მიმდინარეობს <xliff:g id="NAME">%1$s</xliff:g>-ის ჩამოტვირთვა, <xliff:g id="PROGRESS">%2$s</xliff:g> დასრულდა"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ელოდება ინსტალაციას"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g>-ის ვიჯეტები"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"მთავარ ეკრანზე დამატება"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"ერთეულის გადაადგილება აქ"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ერთეული დაემატა მთავარ ეკრანს"</string>
+ <string name="item_removed" msgid="851119963877842327">"ერთეული წაიშალა"</string>
+ <string name="action_move" msgid="4339390619886385032">"ერთეულის გადაადგილება"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"გადატანა რიგში <xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტში <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"გადატანა <xliff:g id="NUMBER">%1$s</xliff:g> პოზიციაზე"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"გადატანა რჩეულთა პოზიციაზე <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"ერთეული გადაადგილდა"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"საქაღალდეში დამატება: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"საქაღალდეში დამატება <xliff:g id="NAME">%1$s</xliff:g>-ით"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ერთეული დაემატა საქაღალდეს"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"საქაღალდის შექმნა ერთეულით: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"საქაღალდე შექმნილია"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"მთავარ ეკრანზე გადატანა"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"ეკრანის გადატანა მარცხნივ"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"ეკრანის გადატანა მარჯვნით"</string>
+ <string name="screen_moved" msgid="266230079505650577">"ეკრანი გადაადგილდა"</string>
+ <string name="action_resize" msgid="1802976324781771067">"ზომის შეცვლა"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"სიგანის გაზრდა"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"სიმაღლის გაზრდა"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"სიგანის შემცირება"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"სიმაღლის შემცირება"</string>
+ <string name="widget_resized" msgid="9130327887929620">"ვიჯეტის ზომები შეიცვალა: სიგანე <xliff:g id="NUMBER_0">%1$s</xliff:g> სიმაღლე <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"მალსახმობები"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>-ს აქვს <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> მალსახმობი"</string>
+</resources>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index c9264d6..66766b6 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Параметрлер"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Шолу"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Негізгі экранның бұрылуына рұқсат ету"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Жүйенің әдепкі параметрін пайдалану"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Шаршы"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Жұмыр төртбұрыш"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Шеңбер"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Тамшы"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
new file mode 100644
index 0000000..5df503a
--- /dev/null
+++ b/res/values-kk/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Жұмыс"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Қолданба орнатылмаған."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Қолданба қол жетімді емес"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Жүктелген қолданба қауіпсіз режимде өшірілген"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"Виджетті таңдау үшін түртіп, мықтап ұстаңыз."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджетті таңдау немесе арнаулы әрекеттерді таңдау үшін екі рет түртіп, ұстап тұрыңыз."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ені: %1$d, биіктігі: %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Қолмен қою үшін басып тұрыңыз"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Автоматты енгізу"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Қолданбаларды іздеу"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Қолданбалар жүктелуде…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» сұрауына сәйкес келетін қолданбалар жоқ"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Қосымша қолданбалар іздеу"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Хабарландырулар"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Қолданбалар тізімі"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Негізгі"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Жою"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Жою"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Қолданба ақпараты"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"төте пернелерді орнату"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Негізгі экрандағы параметрлер мен төте пернелерді оқу"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Қолданбаға Негізгі экрандағы параметрлер мен төте пернелерді оқу мүмкіндігін береді."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Негізгі экран параметрлері мен төте пернелерін жазу"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Қолданбаға Негізгі экрандағы параметрлер мен төте пернелерді өзгерту мүмкіндігін береді."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> арқылы телефон қоңырауларын соғуға рұқсат етілмеген"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Виджетті жүктеу барысында мәселе орын алды"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Орнату"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Атауы жоқ қалта"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өшірілді"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%1$d бет, барлығы %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d негізгі экран, барлығы %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Жаңа негізгі экран беті"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Қалта ашылды, <xliff:g id="WIDTH">%1$d</xliff:g> және <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Қалтаны жабу үшін түртіңіз"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Қайта атауды сақтау үшін түртіңіз"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Қалта жабылды"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Параметрлер"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Шолу"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Негізгі экранның бұрылуына рұқсат ету"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бұрылғанда"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранның ағымдағы параметрі айналуға рұқсат бермейді"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Негізгі экранға белгіше енгізу"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңа қолданбаларға арналған"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Іздеу"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Бұл қолданба орнатылмаған"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктелуде, <xliff:g id="PROGRESS">%2$s</xliff:g> аяқталды"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнату күтілуде"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> виджеті"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Негізгі экранға қосу"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Элементті мұнда жылжыту"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент негізгі экранға қосылды"</string>
+ <string name="item_removed" msgid="851119963877842327">"Элемент жойылды"</string>
+ <string name="action_move" msgid="4339390619886385032">"Элементті жылжыту"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>-жол, <xliff:g id="NUMBER_1">%2$s</xliff:g>-бағанға жылжыту"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-орынға жылжыту"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> нөмірлі таңдаулы орынға жылжыту"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Элемент жылжытылды"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Қалтаға қосу: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> бар қалтаға қосу"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Элемент қалтаға қосылды"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Мына бар қалтаны жасау: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Қалта жасалды"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Негізгі экранға жылжыту"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Экранды солға жылжыту"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Экранды оңға жылжыту"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Экран жылжытылды"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Өлшемін өзгерту"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Енін арттыру"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Биіктігін арттыру"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Енін азайту"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Биіктігін азайту"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Виджет өлшемінің ені <xliff:g id="NUMBER_0">%1$s</xliff:g>, биіктігі <xliff:g id="NUMBER_1">%2$s</xliff:g> болып өзгертілді"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Таңбашалар"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасына арналған <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> таңбаша"</string>
+</resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 822912f..ee44302 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ធាតុក្រាហ្វិក"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំងរូបភាព"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ការកំណត់"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់ទំព័រដើម"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"សង្ខេប"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"ប្រើលំនាំដើមរបស់ប្រព័ន្ធ"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ការ៉េ"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ការ៉េជ្រុងកោង"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"រង្វង់"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"តំណក់ទឹកភ្នែក"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"មិនស្គាល់"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
new file mode 100644
index 0000000..a64b604
--- /dev/null
+++ b/res/values-km/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ការងារ"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"មិនបានដំឡើងកម្មវិធី។"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"មិនមានកម្មវិធី"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"បានបិទកម្មវិធីដែលបានទាញយកក្នុងរបៀបសុវត្ថិភាព"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"ប៉ះ & សង្កត់ ដើម្បីជ្រើសធាតុក្រាហ្វិក។"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ប៉ះពីរដង ហើយចុចឲ្យជាប់ដើម្បីជ្រើសយកធាតុក្រាហ្វិក ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ទទឺង %1$d គុណនឹងកម្ពស់ %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ចុចឲ្យជាប់ដើម្បីបញ្ចូលវាដោយផ្ទាល់"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"បញ្ចូលដោយស្វ័យប្រវត្តិ"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ស្វែងរកកម្មវិធី"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"កំពុងដំណើរការកម្មវិធី..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"គ្មានកម្មវិធីដែលត្រូវជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"ស្វែងរកកម្មវិធីច្រើនទៀត"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"ការជូនដំណឹង"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"គ្មានបន្ទប់នៅលើអេក្រង់ដើមនេះទៀតទេ។"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មានបន្ទប់ក្នុងថាសនិយមប្រើ"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"បញ្ជីកម្មវិធី"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ដើម"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"យកចេញ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"លុបការដំឡើង"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ព័ត៌មានកម្មវិធី"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើងផ្លូវកាត់"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាតឲ្យកម្មវិធីបន្ថែមផ្លូវកាត់ ដោយមិនចាំបាច់អំពើពីអ្នកប្រើ។"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"អានការកំណត់ និងផ្លូវកាត់អេក្រង់ដើម"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"អនុញ្ញាតឲ្យកម្មវិធីអានការកំណត់ និងផ្លូវកាត់ក្នុងអេក្រង់ដើម។"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"សរសេរការកំណត់ និងផ្លូវកាត់លើអេក្រង់ដើម"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"អនុញ្ញាតឲ្យកម្មវិធីប្ដូរការកំណត់ និងផ្លូវកាត់ក្នុងអេក្រង់ដើម។"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> មិនត្រូវបានអនុញ្ញាតឲ្យធ្វើការហៅទូរស័ព្ទទេ"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"បញ្ហាក្នុងការផ្ទុកធាតុក្រាហ្វិក"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"រៀបចំ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះជាកម្មវិធីប្រព័ន្ធ មិនអាចលុបបានទេ។"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ថតគ្មានឈ្មោះ"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"បានបិទដំណើរការ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"ទំព័រ %1$d នៃ %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"អេក្រង់ដើម %1$d នៃ %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ទំព័រអេក្រង់ដើមថ្មី"</string>
+ <string name="folder_opened" msgid="94695026776264709">"បានបើកថត <xliff:g id="WIDTH">%1$d</xliff:g> ដោយ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ប៉ះ ដើម្បីបិទថត"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"ប៉ះដើម្បីរក្សាទុកឈ្មោះដែលបានប្តូរ"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"បានបិទថត"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"បានប្ដូរឈ្មោះថតជា <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ធាតុក្រាហ្វិក"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំងរូបភាព"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ការកំណត់"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"សង្ខេប"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"នៅពេលដែលបង្វិលទូរស័ព្ទរបស់អ្នក"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ការកំណត់អេក្រង់បច្ចុប្បន្នមិនអនុញ្ញាតការបង្វិលទេ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"បញ្ចូលរូបតំណាងទៅអេក្រង់ដើម"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"សម្រាប់កម្មវិធីថ្មី"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"មិនស្គាល់"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ស្វែងរក"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"មិនបានដំឡើងកម្មវិធីនេះ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"មិនបានដំឡើងកម្មវិធីសម្រាប់រូបតំណាងនេះ។ អ្នកអាចលុបវា ឬស្វែងរកកម្មវិធី និងដំឡើងវាដោយដៃ។"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"កំពុងដោនឡូត <xliff:g id="NAME">%1$s</xliff:g> បានបញ្ចប់ <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> កំពុងរង់ចាំការដំឡើង"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"ធាតុក្រាហ្វិក <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"បន្ថែមទៅអេក្រង់ដើម"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"ផ្លាស់ធាតុមកទីនេះ"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ធាតុដែលត្រូវបានបន្ថែមទៅអេក្រង់ដើម"</string>
+ <string name="item_removed" msgid="851119963877842327">"ធាតុដែលបានដកចេញ"</string>
+ <string name="action_move" msgid="4339390619886385032">"ផ្លាស់ទីធាតុ"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"ផ្លាស់ទីទៅជួរដេកទី <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរទី <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"ផ្លាស់ទីទៅទីតាំង <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ផ្លាស់ទីទៅការចូលចិត្តទីតាំងទី <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"បានផ្លាស់ទីធាតុ"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"បន្ថែមទៅថតឯកសារ៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"បន្ថែមទៅថតឯកសារដែលមានឈ្មោះ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"បានបន្ថែមធាតុទៅថតឯកសារ"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"បង្កើតថតឯកសារជាមួយ៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"បានបង្កើតថតឯកសារ"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ផ្លាស់ទៅអេក្រង់ដើម"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"រំកិលអេក្រង់ទៅខាងឆ្វេង"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"រំកិលអេក្រង់ទៅខាងស្តាំ"</string>
+ <string name="screen_moved" msgid="266230079505650577">"អេក្រង់ដែលបានផ្លាស់ទី"</string>
+ <string name="action_resize" msgid="1802976324781771067">"ប្ដូរទំហំ"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"បង្កើនទទឹង"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"បង្កើនកម្ពស់"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"បន្ថយទទឹង"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"បន្ថយកម្ពស់"</string>
+ <string name="widget_resized" msgid="9130327887929620">"ធាតុក្រាហ្វិកដែលបានប្តូរទំហំទៅទទឹងប្រវែង <xliff:g id="NUMBER_0">%1$s</xliff:g> កម្ពស់ប្រវែង <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"ផ្លូវកាត់"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ផ្លូវកាត់សម្រាប់ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 1d5f7bc..6c89b7b 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್ಗಳು"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್ಪೇಪರ್ಗಳು"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"ಅವಲೋಕನ"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ಹೊಸ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ಸಿಸ್ಟಂ ಡಿಫಾಲ್ಟ್ ಬಳಸಿ"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ಚೌಕ"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ಚೌಕವೃತ್ತ"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"ವೃತ್ತ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"ಕಂಬನಿ"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"ಐಕಾನ್ ಆಕಾರ ಬದಲಾವಣೆಯನ್ನು ಅನ್ವಯಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"ಅಪರಿಚಿತ"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
new file mode 100644
index 0000000..b22af0d
--- /dev/null
+++ b/res/values-kn/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ಕೆಲಸ"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"ಅಪ್ಲಿಕೇಶನ್ ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ಡೌನ್ಲೋಡ್ ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ & ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ವಿಜೆಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ಹಸ್ತಚಾಲಿತವಾಗಿ ಸೇರಿಸಲು ಸ್ಪರ್ಶಿಸಿ ಮತ್ತು ಹೋಲ್ಡ್ ಮಾಡಿ"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸೇರಿಸಿ"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ಅಪ್ಲಿಕೇಷನ್ಗಳನ್ನು ಹುಡುಕಿ"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಹುಡುಕಿ"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"ಅಧಿಸೂಚನೆಗಳು"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ಅಪ್ಲಿಕೇಶನ್ಗಳ ಪಟ್ಟಿ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ಮುಖಪುಟ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ಅಸ್ಥಾಪಿಸು"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಓದಿ"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ಮುಖಪುಟದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಬರೆಯಿರಿ"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ಮುಖಪುಟದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಶಾರ್ಟ್ಕಟ್ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"ಫೋನ್ ಕರೆಗಳನ್ನು ಮಾಡಲು <xliff:g id="APP_NAME">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ಗೆ ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ವಿಜೆಟ್ ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ಸೆಟಪ್"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ಹೊಸ ಮುಖಪುಟ ಪರದೆ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ಫೋಲ್ಡರ್ ತೆರೆಯಲಾಗಿದೆ, <xliff:g id="WIDTH">%1$d</xliff:g> ಬೈ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ಫೋಲ್ಡರ್ ಮುಚ್ಚಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ಫೋಲ್ಡರ್ ಮುಚ್ಚಿದೆ"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್ಗಳು"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್ಪೇಪರ್ಗಳು"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"ಅವಲೋಕನ"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್ ತಿರುಗಿಸಿದಾಗ"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ಪ್ರಸ್ತುತ ಪ್ರದರ್ಶನ ಸೆಟ್ಟಿಂಗ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ಮುಖಪುಟದ ಪರದೆಗೆ ಐಕಾನ್ ಸೇರಿಸಿ"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ಹೊಸ ಅಪ್ಲಿಕೇಶನ್ಗಳಿಗೆ"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"ಐಕಾನ್ ಆಕಾರವನ್ನು ಬದಲಿಸಿ"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"ಬದಲಿಸಬೇಡಿ"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"ಐಕಾನ್ ಆಕಾರ ಬದಲಾವಣೆಯನ್ನು ಅನ್ವಯಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"ಅಪರಿಚಿತ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ಹುಡುಕಿ"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ಸ್ಥಾಪಿಸಲು ಕಾಯಲಾಗುತ್ತಿದೆ"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ವಿಜೆಟ್ಗಳು"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"ಐಟಂ ಇಲ್ಲಿಗೆ ಸರಿಸಿ"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ಮುಖಪುಟ ಪರದೆಗೆ ಐಟಂ ಸೇರಿಸಲಾಗಿದೆ"</string>
+ <string name="item_removed" msgid="851119963877842327">"ಐಟಂ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
+ <string name="action_move" msgid="4339390619886385032">"ಐಟಂ ಸರಿಸಿ"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> ಸಾಲು <xliff:g id="NUMBER_1">%2$s</xliff:g> ಕಾಲಮ್ಗೆ ಸರಿಸಿ"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ಮೆಚ್ಚಿನ <xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="item_moved" msgid="4606538322571412879">"ಐಟಂ ಸರಿಸಲಾಗಿದೆ"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ಫೋಲ್ಡರ್ಗೆ ಸೇರಿಸಿ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> ಮೂಲಕ ಫೋಲ್ಡರ್ಗೆ ಸೇರಿಸಿ"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ಐಟಂ ಅನ್ನು ಫೋಲ್ಡರ್ಗೆ ಸೇರಿಸಲಾಗಿದೆ"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ಇದನ್ನು ಬಳಸಿಕೊಂಡು ಫೋಲ್ಡರ್ ರಚಿಸಿ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ಫೋಲ್ಡರ್ ರಚಿಸಲಾಗಿದೆ"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ಮುಖಪುಟಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"ಪರದೆಯನ್ನು ಎಡಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"ಪರದೆಯನ್ನು ಬಲಕ್ಕೆ ಸರಿಸಿ"</string>
+ <string name="screen_moved" msgid="266230079505650577">"ಪರದೆ ಸರಿಸಲಾಗಿದೆ"</string>
+ <string name="action_resize" msgid="1802976324781771067">"ಮರುಗಾತ್ರ"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"ಅಗಲವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"ಎತ್ತರವನ್ನು ಹೆಚ್ಚು ಮಾಡಿ"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"ಅಗಲವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"ಎತ್ತರವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
+ <string name="widget_resized" msgid="9130327887929620">"ವಿಜೆಟ್ ಅನ್ನು <xliff:g id="NUMBER_0">%1$s</xliff:g> ಅಗಲ <xliff:g id="NUMBER_1">%2$s</xliff:g> ಎತ್ತರಕ್ಕೆ ಮರುಗಾತ್ರಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ಗೆ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ಶಾರ್ಟ್ಕಟ್ಗಳು"</string>
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index ce2d222..26535f0 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"폴더: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"설정"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"개요"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"홈 화면 회전 허용"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"시스템 기본값 사용"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"정사각형"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"모서리가 둥근 정사각형"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"원"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"눈물방울"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index dc06196..cc15f04 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Жөндөөлөр"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Башкы беттин жөндөөлөрү"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Көз жүгүртүү"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Тутум сушунтаган демейкисин колдонуу"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Чарчы"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Бурчтары жумуру төрт бурчтук"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Тегерек"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Тамчы"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
new file mode 100644
index 0000000..2162292
--- /dev/null
+++ b/res/values-ky/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Жумуш"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Колдонмо орнотулган эмес."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Колдонмо жеткиликтүү эмес"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Жүктөп алынган колдонмо Коопсуз режиминде иштен чыгарылды"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"Виджетти тандаш үчүн, басып туруңуз"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджет тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Кол менен жайгаштыруу үчүн басып туруп, таштаңыз"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Автоматтык түрдө кошуу"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Колдонмолорду издөө"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Колдонмолор жүктөлүүдө…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" дал келген колдонмолор табылган жок"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Көбүрөөк колдонмолорду издөө"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Эскертмелер"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Колдонмолор тизмеси"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Үйгө"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Алып салуу"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Үйдүн тууралоолорун жана тез чакырмаларын окуу"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Колдонмого Үйдүн тууралоолорун жана тез чакырмаларын окууга уруксат берет."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Үйдүн тууралоолорун жана тез чакырмаларын жазуу"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Колдонмого Үйдүн тууралоолорун жана тез чакырмаларын өзгөртүүгө уруксат берет."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> телефон чалууларды аткарууга уруксаты жок"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Виджетти жүктөөдө маселе бар"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Орнотуу"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Аты жок фолдер"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Жаңы башкы экран барагы"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Фолдер ачылды, туурасы <xliff:g id="WIDTH">%1$d</xliff:g>, бийиктиги <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Куржунду жабуу үчүн таптаңыз"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Өзгөртүлгөн аталышын сактоо үчүн таптаңыз"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Фолдер жабык"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Тууралоолор"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Көз жүгүртүү"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон айланганда"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранды айлантуу параметри өчүрүлгөн"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Башкы экранга сүрөтчө кошуу"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Жаңы колдонмолор үчүн"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Издөө"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Бул колдонмо орнотулган эмес"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> жүктөлүп алынууда, <xliff:g id="PROGRESS">%2$s</xliff:g> аяктады"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> орнотулушу күтүлүүдө"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> виджеттери"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Башкы экранга кошуу"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Бул нерсени бул жерге жылдыруу"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Башкы экранга кошулду"</string>
+ <string name="item_removed" msgid="851119963877842327">"Жоюлду"</string>
+ <string name="action_move" msgid="4339390619886385032">"Муну жылдыруу"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> катарга <xliff:g id="NUMBER_1">%2$s</xliff:g> тилкеге жылдыруу"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> орунга жылдыруу"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Сүйүктүүлөргө <xliff:g id="NUMBER">%1$s</xliff:g> жылдыруу"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Нерсе жылдырылды"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Куржунга кошуу: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> куржунуна кошуу"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Нерсе куржунга кошулду"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Төмөнкү менен куржун түзүү: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Куржун түзүлдү"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Башкы экранга жылдыруу"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Экранды солго жылдыруу"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Экранды оңго жылдыруу"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Экран жылдырылды"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Өлчөмүн өзгөртүү"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Кеңейтүү"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Бийиктетүү"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Ичкертүү"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Жапыздатуу"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Виджеттин кеңдиги <xliff:g id="NUMBER_0">%1$s</xliff:g> бийиктиги <xliff:g id="NUMBER_1">%2$s</xliff:g> болду"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Кыска жолдор"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосуна <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кыска жол бар"</string>
+</resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 3915759..0db0e61 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -4,9 +4,9 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
-
+
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -15,6 +15,23 @@
-->
<resources>
-<!-- Container -->
- <item name="container_margin" format="fraction" type="fraction">12%</item>
+ <!-- Container -->
+ <item name="container_margin" format="fraction" type="fraction">12%</item>
+
+ <!-- Fast scroll -->
+ <dimen name="fastscroll_popup_width">58dp</dimen>
+ <dimen name="fastscroll_popup_height">48dp</dimen>
+ <dimen name="fastscroll_popup_padding">10dp</dimen>
+ <dimen name="fastscroll_popup_text_size">24dp</dimen>
+
+ <!-- Dynamic grid -->
+ <dimen name="dynamic_grid_overview_bar_item_width">120dp</dimen>
+ <dimen name="dynamic_grid_page_indicator_size">24dp</dimen>
+ <dimen name="folder_preview_padding">5dp</dimen>
+
+ <!-- Hotseat -->
+ <dimen name="dynamic_grid_hotseat_land_left_nav_bar_right_padding">18dp</dimen>
+ <dimen name="dynamic_grid_hotseat_land_right_nav_bar_right_padding">6dp</dimen>
+ <dimen name="dynamic_grid_hotseat_land_left_nav_bar_gutter_width">24dp</dimen>
+ <dimen name="dynamic_grid_hotseat_land_right_nav_bar_gutter_width">32dp</dimen>
</resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 823f9bb..0b29434 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ການຕັ້ງຄ່າ"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"ພາບຮວມ"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"ໃຊ້ຄ່າເລີ່ມຕົ້ນລະບົບ"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ສີ່ຫຼ່ຽມຈັດຕຸລັດ"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ສີ່ຫຼ່ຽມຂອບມົນ"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"ວົງມົນ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"ນ້ຳຢອດ"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"ບໍ່ຮູ້ຈັກ"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
new file mode 100644
index 0000000..334305a
--- /dev/null
+++ b/res/values-lo/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ວຽກ"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"ແອັບຯໃຊ້ບໍ່ໄດ້"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ແອັບຯທີ່ດາວໂຫລດແລ້ວຖືກປິດການນຳໃຊ້ໃນ Safe mode"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"ວິດເຈັດຖືກປິດໃນ Safe mode"</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="long_press_widget_to_add" msgid="7699152356777458215">"ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ແຕະຄ້າງໄວ້ ເພື່ອເລືອກວິດເຈັດ ຫຼື ໃຊ້ການດຳເນີນການກຳນົດເອງ."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ກວ້າງ %1$d ຄູນສູງ %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ແຕະຄ້າງໄວ້ເພື່ອວາງດ້ວຍຕົນເອງ"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"ເພີ່ມໂດຍອັດຕະໂນມັດ"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ຊອກຫາແອັບ"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"ກຳລັງໂຫລດແອັບ..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"ບໍ່ພົບແອັບໃດທີ່ກົງກັນ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"ຊອກຫາແອັບເພີ່ມເຕີມ"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"ການແຈ້ງເຕືອນ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ລາຍຊື່ແອັບ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ໜ້າຫຼັກ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ເອົາອອກ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ຖອນການຕິດຕັ້ງ"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ຂໍ້ມູນແອັບ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ອະນຸຍາດໃຫ້ແອັບຯດັ່ງກ່າວອ່ານການຕັ້ງຄ່າ ແລະທາງລັດໃນໜ້າຫຼັກ."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ຂຽນການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ອະນຸຍາດໃຫ້ແອັບຯດັ່ງກ່າວ ປ່ຽນການຕັ້ງຄ່າ ແລະທາງລັດໃນໜ້າຫຼັກ."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ໂທ"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ມີບັນຫາໃນການໂຫລດວິດເຈັດ"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ຕິດຕັ້ງ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"ປິດການນຳໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"ໜ້າ %1$d ຈາກ %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ໜ້າຂອງໜ້າຈໍຫຼັກໃໝ່"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ເປີດໂຟນເດີແລ້ວ, <xliff:g id="WIDTH">%1$d</xliff:g> ຄູນ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ແຕະເພື່ອປິດໂຟນເດີ"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"ແຕະເພື່ອບັນທຶກການປ່ຽນຊື່"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ປິດໂຟນເດີແລ້ວ"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ປ່ຽນຊື່ໂຟນເດີເປັນ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ການຕັ້ງຄ່າ"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"ພາບຮວມ"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ເມື່ອໝຸນໂທລະສັບ"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ການຕັ້ງຄ່າສະແດງຜົນປັດຈຸບັນບໍ່ອະນຸຍາດໃຫ້ໝຸນໄດ້"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ເພີ່ມໄອຄອນໃສ່ໜ້າຈໍຫຼັກ"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ສຳລັບແອັບໃໝ່"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"ບໍ່ຮູ້ຈັກ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ຊອກຫາ"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ແອັບຯນີ້ຍັງບໍ່ໄດ້ຕິດຕັ້ງເທື່ອ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ແອັບຯສຳລັບໄອຄອນນີ້ຍັງບໍ່ໄດ້ຕິດຕັ້ງເທື່ອ. ທ່ານສາມາດລຶບມັນອອກ ຫຼືຊອກຫາແອັບຯ ແລ້ວຕິດຕັ້ງມັນໄດ້ດ້ວຍຕົນເອງ."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ກຳລັງດາວໂຫຼດ, <xliff:g id="PROGRESS">%2$s</xliff:g> ສຳເລັດ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ກຳລັງລໍຖ້າຕິດຕັ້ງ"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"ວິດເຈັດ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"ເພີ່ມໃສ່ໜ້າຈໍຫຼັກ"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ເພີ່ມລາຍການໃສ່ໜ້າຈໍຫຼັກແລ້ວ"</string>
+ <string name="item_removed" msgid="851119963877842327">"ເອົາລາຍການອອກໄປແລ້ວ"</string>
+ <string name="action_move" msgid="4339390619886385032">"ຍ້າຍລາຍການ"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"ຍ້າຍໄປໃສ່ແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"ຍ້າຍໄປໃສ່ຕຳແໜ່ງ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ຍ້າຍໄປໃສ່ຕຳແໜ່ງທີ່ມັກ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"ຍ້າຍລາຍການແລ້ວ"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ເພີ່ມໃສ່ໂຟລເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"ເພີ່ມໃສ່ໂຟລເດີດ້ວຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ເພີ່ມລາຍການໃສ່ໂຟລເດີແລ້ວ"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ສ້າງໂຟລເດີກັບ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ສ້າງໂຟລເດີແລ້ວ"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ຍ້າຍໄປໃສ່ໜ້າຈໍຫຼັກ"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"ຍ້າຍໜ້າຈໍໄປທາງຊ້າຍ"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"ຍ້າຍໜ້າຈໍໄປທາງຂວາ"</string>
+ <string name="screen_moved" msgid="266230079505650577">"ຍ້າຍໜ້າຈໍແລ້ວ"</string>
+ <string name="action_resize" msgid="1802976324781771067">"ປັບຂະໜາດ"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"ເພີ່ມລວງກ້ວາງຂຶ້ນ"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"ເພີ່ມລວງສູງຂຶ້ນ"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"ຫຼຸດລວງກ້ວາງລົງ"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"ຫຼຸດລວງສູງລົງ"</string>
+ <string name="widget_resized" msgid="9130327887929620">"ປ່ຽນຂະໜາດວິດເຈັດເປັນລວງກ້ວາງ <xliff:g id="NUMBER_0">%1$s</xliff:g> ລວງສູງ <xliff:g id="NUMBER_1">%2$s</xliff:g> ແລ້ວ"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"ທາງລັດ"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ທາງລັດສຳລັບ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 0615112..945cbf6 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Nustatymai"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"„Home“ nustatymai"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Apžvalga"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Leisti pasukti pagrindinį ekraną"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Naudoti numatytuosius sistemos nustatymus"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadratas"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadratais suapvalintais kampais"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Apskritimas"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Ašara"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index fb48a74..e3c3482 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mape: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Iestatījumi"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Sākumlapas iestatījumi"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Kopsavilkums"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Atļaut sākuma ekrāna pagriešanu"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Izmantot sistēmas noklusējumu"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrāts"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvadrāts ar noapaļotiem stūriem"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Aplis"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lāse"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 8655476..abd1d35 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Додатоци"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Позадини"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Поставки"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Краток преглед"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Користи ја стандардната поставка на системот"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Заоблен квадрат"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Солза"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
new file mode 100644
index 0000000..267e75b
--- /dev/null
+++ b/res/values-mk/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Стартер3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Работа"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Апликацијата не е инсталирана."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Апликацијата не е достапна"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Преземената апликација е оневозможена во безбеден режим"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Додатоците се оневозможени во безбеден режим"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Кратенката не е достапна"</string>
+ <string name="home_screen" msgid="806512411299847073">"Почетен екран"</string>
+ <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="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d широк на %2$d висок"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Допрете и задржете за рачно поставување"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Додај автоматски"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пребарување апликации"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Се вчитуваат апликации…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Пребарај други апликации"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Известувања"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Список со апликации"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна страница"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Отстрани"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Инф. за апликација"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирај кратенки"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"чита поставки и кратенки на почетна страница"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"пишува поставки и кратенки на почетна страница"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> нема дозвола за телефонски повици"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Проблем при вчитувањето на виџетот"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Поставување"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Неименувана папка"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> е оневозможена"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d од %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Екран на почетна страница %1$d од %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Нова страница на почетен екран"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Отворена е папка, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Допрете за да ја затворите папката"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Допрете за да го зачувате преименувањето"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Папката е затворена"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Додатоци"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадини"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Поставки"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Краток преглед"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Кога телефонот се ротира"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Тековната поставка на Екранот не дозволува ротација"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Додајте икона на почетниот екран"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"За нови апликации"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Барај"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Апликацијата не е инсталирана"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Се презема <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> завршено"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> чека да се инсталира"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Виџети за <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Додај на Почетен екран"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Премести ја ставката овде"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Ставката е додадена на почетниот екран"</string>
+ <string name="item_removed" msgid="851119963877842327">"Ставката е отстранета"</string>
+ <string name="action_move" msgid="4339390619886385032">"Премести ја ставката"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести во ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g> во омилени"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Ставката е преместена"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Додај во папката: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Додај во папка со <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Ставката е додадена во папката"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Создај папка со: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Папката е создадена"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Премести на Почетен екран"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Движи го екранот налево"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Движи го екранот надесно"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Екранот е преместен"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Промени големина"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Зголеми ширина"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Зголеми висина"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Намали ширина"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Намали висина"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Големината на виџетот е променета на ширина <xliff:g id="NUMBER_0">%1$s</xliff:g> висина <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Кратенки"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кратенки за <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 6ce3024..bad7e48 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ക്രമീകരണം"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ഹോം ക്രമീകരണം"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"കാഴ്ച"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"പുതിയ ആപ്പുകൾക്ക്"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ഐക്കണിന്റെ ആകാരം മാറ്റുക"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"സിസ്റ്റം ഡിഫോൾട്ട് ഉപയോഗിക്കുക"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ചതുരം"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ചതുരവൃത്തം"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"വൃത്തം"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"കണ്ണുനീര് തുള്ളി"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"ഐക്കൺ ആകാര മാറ്റങ്ങൾ പ്രയോഗിക്കുന്നു"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
new file mode 100644
index 0000000..a92fc03
--- /dev/null
+++ b/res/values-ml/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"ലോഞ്ചർ3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ഔദ്യോഗികം"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"അപ്ലിക്കേഷൻ ലഭ്യമല്ല"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ഡൗൺലോഡുചെയ്ത അപ്ലിക്കേഷൻ സുരക്ഷാ മോഡിൽ പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"സുരക്ഷിത മോഡിൽ വിജറ്റുകൾ പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"കുറുക്കുവഴി ലഭ്യമല്ല"</string>
+ <string name="home_screen" msgid="806512411299847073">"ഹോം സ്ക്രീൻ"</string>
+ <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="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"സ്വമേധയാ സ്ഥാപിക്കുന്നതിന് സ്പർശിച്ചുപിടിക്കുക"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"സ്വയമേവ ചേർക്കുക"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പുകളെ തിരയുക"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"ആപ്പ്സ് ലോഡുചെയ്യുന്നു..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്സൊന്നും കണ്ടെത്തിയില്ല"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"അറിയിപ്പുകൾ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"അപ്ലിക്കേഷനുകളുടെ ലിസ്റ്റ്"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ഹോം"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"നീക്കംചെയ്യുക"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ആപ്പ് വിവരം"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ഹോമിലെ ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റൈറ്റുചെയ്യുക"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ഹോമിലെ ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും മാറ്റാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"ഫോൺ കോൾ ചെയ്യാൻ <xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിനെ അനുവദിച്ചിട്ടില്ല"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"വിജറ്റ് ലോഡുചെയ്യുന്നതിൽ പ്രശ്നമുണ്ട്"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"സജ്ജീകരിക്കുക"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്റ്റാളുചെയ്യാനാവില്ല."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"പേരുനൽകാത്ത ഫോൾഡർ"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്ക്രീൻ %1$d / %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"പുതിയ ഹോം സ്ക്രീൻ പേജ്"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ഫോൾഡർ തുറന്നു, <xliff:g id="WIDTH">%1$d</xliff:g> / <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ഫോൾഡർ അടയ്ക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ഫോൾഡർ അടച്ചു"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ക്രമീകരണം"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"കാഴ്ച"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ഹോം സ്ക്രീനിലേക്ക് ഐക്കൺ ചേർക്കുക"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"പുതിയ ആപ്പുകൾക്ക്"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"ഐക്കണിന്റെ ആകാരം മാറ്റുക"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"മാറ്റരുത്"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"ഐക്കൺ ആകാര മാറ്റങ്ങൾ പ്രയോഗിക്കുന്നു"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"തിരയുക"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ഡൗൺലോഡ് ചെയ്യുന്നു, <xliff:g id="PROGRESS">%2$s</xliff:g> പൂർത്തിയായി"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"ഇൻസ്റ്റാൾ ചെയ്യാൻ <xliff:g id="NAME">%1$s</xliff:g> കാക്കുന്നു"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> വിജറ്റുകൾ"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"ഹോം സ്ക്രീനിൽ ചേർക്കുക"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"ഇനം ഇവിടേക്ക് നീക്കുക"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ഹോം സ്ക്രീനിൽ ഇനം ചേർത്തു"</string>
+ <string name="item_removed" msgid="851119963877842327">"ഇനം നീക്കംചെയ്തു"</string>
+ <string name="action_move" msgid="4339390619886385032">"ഇനം നീക്കുക"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"വരി <xliff:g id="NUMBER_0">%1$s</xliff:g> നിര <xliff:g id="NUMBER_1">%2$s</xliff:g>-ലേക്ക് നീക്കുക"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-ലേക്ക് നീക്കുക"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ഇഷ്ടമുള്ള <xliff:g id="NUMBER">%1$s</xliff:g> സ്ഥാനത്തേക്ക് നീക്കുക"</string>
+ <string name="item_moved" msgid="4606538322571412879">"ഇനം നീക്കി"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ഫോൾഡറിൽ ചേർക്കുക: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> ഉള്ള ഫോൾഡറിൽ ചേർക്കുക"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ഫോൾഡറിൽ ഇനം ചേർത്തു"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ഇതുപയോഗിച്ച് ഫോൾഡർ സൃഷ്ടിക്കുക: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ഫോൾഡർ സൃഷ്ടിച്ചു"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ഹോം സ്ക്രീനിലേക്ക് നീക്കുക"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"സ്ക്രീൻ ഇടത്തേക്ക് നീക്കുക"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"സ്ക്രീൻ വലത്തേക്ക് നീക്കുക"</string>
+ <string name="screen_moved" msgid="266230079505650577">"സ്ക്രീൻ നീക്കി"</string>
+ <string name="action_resize" msgid="1802976324781771067">"വലുപ്പംമാറ്റുക"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"വീതി കൂട്ടുക"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"ഉയരം കൂട്ടുക"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"വീതി കുറയ്ക്കുക"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"ഉയരം കുറയ്ക്കുക"</string>
+ <string name="widget_resized" msgid="9130327887929620">"വീതി <xliff:g id="NUMBER_0">%1$s</xliff:g> ഉയരം <xliff:g id="NUMBER_1">%2$s</xliff:g>-ലേക്ക് വിഡ്ജെറ്റിന്റെ വലുപ്പം മാറ്റി"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"കുറുക്കുവഴികൾ"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ആപ്പിനുള്ള <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> കുറുക്കുവഴികൾ"</string>
+</resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index b3a1fa1..264e55a 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Тохиргоо"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Тойм"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Системийн өгөгдмөл тохиргоог ашиглах"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Дөрвөлжин"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Мохоо өнцөгтэй дөрвөлжин"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Дугуй"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Дусал"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
new file mode 100644
index 0000000..5b74fd2
--- /dev/null
+++ b/res/values-mn/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Ажил"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Апп суугаагүй байна."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Апп-г ашиглах боломжгүй"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Safe горимд виджетүүдийг идэвхгүйжүүлсэн"</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="long_press_widget_to_add" msgid="7699152356777458215">"Виджетийг авах бол хүрээд барина уу."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Жижиг хэрэгсэл авах болон тохируулсан үйлдлийг ашиглахын тулд 2 удаа товшоод барина уу."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d өргөн %2$d өндөр"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Гараар байршуулахын тулд дараад хүлээнэ үү"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Автоматаар нэмэх"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Апп хайх"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Аппликейшныг ачаалж байна..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д нийцэх апп олдсонгүй"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Бусад апп-г хайх"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Мэдэгдэл"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Энэ Нүүр дэлгэц зайгүй."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Апп-н жагсаалт"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Нүүр"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Арилгах"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Устгах"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Апп-н мэдээлэл"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Нүүрний тохиргоо болон товчлолыг унших"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Апп нь Нүүрэндэх товчлол болон тохиргоог уншиж чадна."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Нүүрний тохиргоо болон товчлолыг бичих"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Апп нь Нүүрэндэх товчлол болон тохиргоог өөрчилж чадна."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> утасны дуудлага хийх боломжгүй"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Виджет ачаалахад асуудал гарав"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Тохируулга"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Нэргүй фолдер"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Шинэ үндсэн нүүр хуудас"</string>
+ <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="WIDTH">%1$d</xliff:g> <xliff:g id="HEIGHT">%2$d</xliff:g> фолдер нээгдэв"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Фолдерийг хаахын тулд дарна уу"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Шинэ нэрийг хадгалахын тулд дарна уу."</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Фолдер хаагдав"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Фолдерын нэр <xliff:g id="NAME">%1$s</xliff:g> болов"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Тохиргоо"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Тойм"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Утсыг эргүүлсэн үед"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Дэлгэцийн одоогийн тохиргоогоор эргүүлэх боломжгүй"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Нүүр хуудаст дүрс тэмдэг нэмэх"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Шинэ аппад зориулсан"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Устгах"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Хайх"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Энэ апп-г суулгаагүй байна"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>-г татаж байна, <xliff:g id="PROGRESS">%2$s</xliff:g> татсан"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> нь суулгахыг хүлээж байна"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> жижиг хэрэгсэл"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Нүүр дэлгэц нэмэх"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Энд байршуулах"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Нүүр дэлгэцэнд нэмсэн зүйл"</string>
+ <string name="item_removed" msgid="851119963877842327">"Арилгасан зүйл"</string>
+ <string name="action_move" msgid="4339390619886385032">"Зөөх"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> мөр <xliff:g id="NUMBER_1">%2$s</xliff:g> баганад зөөх"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Байршил <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Дуртай байршил болох <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Зөөвөрлөсөн зүйл"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Хавтас: <xliff:g id="NAME">%1$s</xliff:g> руу нэм"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g>-тай хавтас нэмэх"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Хавтсанд нэмэгдсэн зүйл"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Хавтсыг: <xliff:g id="NAME">%1$s</xliff:g> нэрээр үүсгэ"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Үүсгэсэн хавтас"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Нүүр дэлгэц рүү зөөх"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Дэлгэцийг зүүн тийш зөөх"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Дэлгэцийг баруун тийш зөөх"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Дэлгэцийг зөөсөн"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Хэмжээг өөрчлөх"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Өргөсгөх"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Өндөрсгөх"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Нарийсгах"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Намсгах"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Виджэтийн өргөн <xliff:g id="NUMBER_0">%1$s</xliff:g>, өндөр <xliff:g id="NUMBER_1">%2$s</xliff:g> болсон"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Товчлол"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>-н <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> товчлол"</string>
+</resources>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index aec8e6a..b23ea96 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग्ज"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्ज"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"विहंगावलोकन"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्यास अनुमती द्या"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन अॅप्ससाठी"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"चिन्हाचा आकार बदला"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"सिस्टमचे डीफॉल्ट वापरा"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"चौरस"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"गोलाकार चौरस"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"वर्तुळ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"अश्रू"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"चिन्हाचा आकार बदल लागू करत आहे"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
new file mode 100644
index 0000000..1a10e4b
--- /dev/null
+++ b/res/values-mr/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"कार्य"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"अॅप स्थापित केलेला नाही."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"अॅप उपलब्ध नाही"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड केलेला अॅप सुरक्षित मोड मध्ये अक्षम केला"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा सानुकूल क्रिया वापरा."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"स्वयंचलितपणे जोडा"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अॅप्स शोधा"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"अॅप्स लोड करीत आहे..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"अधिक अॅप्स शोधा"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"सूचना"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"या मुख्य स्क्रीनवर आणखी जागा नाही."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"अॅप्स सूची"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"मुख्यपृष्ठ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"काढा"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित करा"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"अॅप माहिती"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्थापित करा"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ला फोन कॉल करण्याची अनुमती नाही"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"विजेट लोड करण्यात समस्या"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फोल्डर"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"नवीन मुख्य स्क्रीन पृष्ठ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"फोल्डर उघडले, <xliff:g id="WIDTH">%1$d</xliff:g> बाय <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डर बंद करण्यासाठी टॅप करा"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे जतन करण्यासाठी टॅप करा"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बंद"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग्ज"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"विहंगावलोकन"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्यास अनुमती द्या"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"फोन फिरविला जातो तेव्हा"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"मुख्य स्क्रीनवर चिन्ह जोडा"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नवीन अॅप्ससाठी"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"चिन्हाचा आकार बदला"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"बदलू नका"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"चिन्हाचा आकार बदल लागू करत आहे"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"शोधा"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"हा अॅप स्थापित केलेला नाही"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> स्थापित करण्याची प्रतिक्षा करीत आहे"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"मुख्य स्क्रीनवर जोडा"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"आयटम येथे हलवा"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"आयटम मुख्य स्क्रीनवर जोडला"</string>
+ <string name="item_removed" msgid="851119963877842327">"आयटम काढला"</string>
+ <string name="action_move" msgid="4339390619886385032">"आयटम हलवा"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> मध्ये हलवा"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"आवडत्या <xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
+ <string name="item_moved" msgid="4606538322571412879">"आयटम हलविला"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"फोल्डरवर जोडा: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> सह फोल्डरमध्ये जोडा"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"फोल्डरमध्ये आयटम जोडले"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"यासह फोल्डर तयार करा: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"फोल्डर तयार केले"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"मुख्य स्क्रीनवर हलवा"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"स्क्रीन डावीकडे हलवा"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"स्क्रीन उजवीकडे हलवा"</string>
+ <string name="screen_moved" msgid="266230079505650577">"स्क्रीन हलविली"</string>
+ <string name="action_resize" msgid="1802976324781771067">"आकार बदला"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"रूंदी वाढवा"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"उंची वाढवा"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"रुंदी कमी करा"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"उंची कमी करा"</string>
+ <string name="widget_resized" msgid="9130327887929620">"विजेटचा आकार रुंदी <xliff:g id="NUMBER_0">%1$s</xliff:g> उंची <xliff:g id="NUMBER_1">%2$s</xliff:g> मध्ये बदलला"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"शॉर्टकट"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> साठी <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट"</string>
+</resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 22cb425..0928b7d 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Tetapan"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Tetapan laman utama"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Ikhtisar"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gunakan lalai sistem"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Segi empat sama"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Segi empat berbucu bulat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Bulatan"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Titisan air mata"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 7335605..756a2cf 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -19,35 +19,103 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="application_name" msgid="8424725141379931883">"Pelancar"</string>
- <string name="folder_name" msgid="8551881338202938211"></string>
- <string name="wallpaper_instructions" msgid="4215640646180727542">"Tetapkan kertas dinding"</string>
- <string name="pick_wallpaper" msgid="5630222540525626723">"Kertas dinding"</string>
- <string name="activity_not_found" msgid="217823393239365967">"Aplikasi tidak dipasang."</string>
- <string name="long_press_widget_to_add" msgid="7395697462851217506">"Sentuh & tahan untuk mengambil widget."</string>
- <string name="widget_dims_format" msgid="1386418557719032947">"%1$d × %2$d"</string>
- <string name="out_of_space" msgid="8365249326091984698">"Tiada lagi ruang pada skrin Utama ini"</string>
- <string name="hotseat_out_of_space" msgid="6304886797358479361">"Tiada lagi ruang pada kerusi panas."</string>
- <string name="all_apps_button_label" msgid="2578400570124163469">"Apl"</string>
- <string name="all_apps_home_button_label" msgid="1022222300329398558">"Laman Utama"</string>
- <string name="delete_target_label" msgid="665300185123139530">"Alih keluar"</string>
- <string name="delete_target_uninstall_label" msgid="748894921183769150">"Nyahpasang"</string>
- <string name="info_target_label" msgid="4019495079517426980">"Maklumat apl"</string>
- <string name="permlab_install_shortcut" msgid="1201690825493376489">"pasang pintasan"</string>
- <string name="permdesc_install_shortcut" msgid="8634424803272077038">"Membenarkan aplikasi menambah pintasan tanpa campur tangan pengguna."</string>
- <string name="permlab_read_settings" msgid="3452408290738106747">"membaca tetapan dan pintasan Laman Utama"</string>
- <string name="permdesc_read_settings" msgid="5788109303585403679">"Membenarkan apl membaca tetapan dan pintasan di Laman Utama."</string>
- <string name="permlab_write_settings" msgid="1360567537236705628">"menulis tetapan dan pintasan Laman Utama"</string>
- <string name="permdesc_write_settings" msgid="8530105489115785531">"Membenarkan apl menukar tetapan dan pintasan di Laman Utama."</string>
- <string name="gadget_error_text" msgid="8359351016167075858">"Masalah memuatkan widget"</string>
- <string name="uninstall_system_app_text" msgid="6429814133777046491">"Ini adalah aplikasi sistem dan tidak boleh dinyahpasang."</string>
- <string name="folder_hint_text" msgid="8633351560105748141">"Folder Tanpa Nama"</string>
- <string name="default_scroll_format" msgid="4057140866420001240">"Halaman %1$d dari %2$d"</string>
- <string name="workspace_scroll_format" msgid="1704767047951143301">"Skrin utama %1$d dari %2$d"</string>
- <string name="folder_opened" msgid="1262064100943801533">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> kali <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="1335478160661137579">"Sentuh untuk menutup folder"</string>
- <string name="folder_tap_to_rename" msgid="5201612989905472442">"Sentuh untuk menyimpan penamaan semula"</string>
- <string name="folder_closed" msgid="3130534551370511932">"Folder ditutup"</string>
- <string name="folder_renamed" msgid="7951233572858053642">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_name_format" msgid="3051680259794759037">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Kerja"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Apl tidak dipasang."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Apl tidak tersedia"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Apl yang dimuat turun dilumpuhkan dalam mod Selamat"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Widget dilumpuhkan dalam mod Selamat"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
+ <string name="home_screen" msgid="806512411299847073">"Skrin utama"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Tindakan tersuai"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh & tahan untuk mengambil widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketik dua kali & tahan untuk mengambil widget atau menggunakan tindakan tersuai"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Sentuh & tahan untuk meletakkan widget/ikon secara manual"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Tambahkan secara automatik"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cari Apl"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuatkan Apl…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tiada Apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cari lagi apl"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Pemberitahuan"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Tiada lagi ruang pada skrin Laman Utama ini."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Senarai apl"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Laman Utama"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Alih keluar"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"baca tetapan dan pintasan Laman Utama"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Membenarkan apl membaca tetapan dan pintasan di Laman Utama."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"tulis tetapan dan pintasan Laman Utama"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Membenarkan apl menukar tetapan dan pintasan di Laman Utama."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dibenarkan membuat panggilan telefon"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuatkan widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Persediaan"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> kali <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Ketik untuk menutup folder"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketik untuk menyimpan penamaan semula"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Folder ditutup"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Tetapan"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Ikhtisar"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Tetapan Paparan semasa tidak membenarkan putaran"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Tambahkan ikon pada Skrin Utama"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Untuk apl baharu"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Carian"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Apl ini tidak dipasang"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> memuat turun, <xliff:g id="PROGRESS">%2$s</xliff:g> selesai"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> menunggu untuk dipasang"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widget <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Tambahkan pada Skrin Utama"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Alihkan item ke sini"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan pada skrin utama"</string>
+ <string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string>
+ <string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Alihkan ke kedudukan <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Alihkan ke kedudukan kegemaran <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Item dialihkan"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Tambahkan pada folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Tambahkan pada folder dengan <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Item ditambahkan pada folder"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Buat folder dengan: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Folder dibuat"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Alihkan ke Skrin Utama"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Alihkan skrin ke kiri"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Alihkan skrin ke kanan"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Skrin dialihkan"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Ubah saiz"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Tambahkan kelebaran"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Tambahkan ketinggian"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Kurangkan kelebaran"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Kurangkan ketinggian"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Saiz widget diubah menjadi <xliff:g id="NUMBER_0">%1$s</xliff:g> lebar <xliff:g id="NUMBER_1">%2$s</xliff:g> tinggi"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Pintasan"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan untuk <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
</resources>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 2a5b23b..a4648b1 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ဆက်တင်များ"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ပင်မဆက်တင်များ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"ခြုံငုံသုံးသပ်ချက်"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"စနစ်၏ မူရင်းပုံကို အသုံးပြုရန်"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"လေးထောင့်"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"စတုရန်းမကျ စက်ဝိုင်းမကျပုံ"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"စက်ဝိုင်း"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"မျက်ရည်စက်ပုံ"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
new file mode 100644
index 0000000..449aae8
--- /dev/null
+++ b/res/values-my/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"ဖွင့်တင်စက်၃"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"အလုပ်"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"အက်ပ်မထည့်သွင်းထားပါ"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"အက်ပ်လက်လှမ်း မမှီပါ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ဒေါင်းလုဒ် အက်ပ်ကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ဝစ်ဂျက်တစ်ခုကိုရယူရန် သို့မဟုတ် စိတ်ကြိုက်လုပ်ဆောင်မှုများကို အသုံးပြုရန် နှစ်ချက်တို့ပြီး ကိုင်ထားပါ။"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ကိုယ်တိုင်ထည့်ရန် ထိထားပါ"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"အလိုအလျောက် ထည့်ရန်"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ရှာဖွေမှု အက်ပ်များ"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"အက်ပ်များ ရယူနေစဉ်..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"နောက်ထပ် အက်ပ်များကို ရှာပါ"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"အကြောင်းကြားချက်များ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"အက်ပ်စာရင်း"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ဖယ်ရှားမည်"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ထုတ်မည်"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ်အချက်အလက်များ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ပင်မမျက်နှာစာတွင်ရှိသော အပြင်အဆင်နှင့် အတိုကောက်မှတ်သားမှုများကို အပ်ပလီကေးရှင်းအား ဖတ်ခွင့်ပြုခြင်း"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g>သည် ဖုန်းခေါ်ဆိုခွင့် မရှိပါ"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"စဖွင့်သတ်မှတ်ရန်"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"အမည်မရှိအကန့်"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"စာမျက်နှာ %1$d မှ %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ပင်မစာမျက်နှာ %1$d မှ %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ပင်မမျက်နှာပြင် စာမျက်နှာသစ်"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ဖွင့်ထားသောအကန့်, <xliff:g id="WIDTH">%1$d</xliff:g> နှင့် <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ဖိုင်တွဲကို ပိတ်ရန် တို့ပါ"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"အမည်ပြောင်းခြင်းကို သိမ်းဆည်းရန် တို့ပါ"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ပိတ်ထားသောအကန့်"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ဆက်တင်များ"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"ခြုံငုံသုံးသပ်ချက်"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ဖုန်းကိုလှည့်ထားစဉ်"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"လက်ရှိ မြင်ကွင်းဆက်တင်တွင် မြင်ကွင်းကို လှည့်ခွင့်မပေးပါ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ပင်မစာမျက်နှာသို့ သင်္ကေတပုံ ထည့်ရန်"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"အက်ပ်အသစ်များအတွက်"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ရှာဖွေရန်"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"အက်ပ်မတပ်ဆင်ရသေးပါ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ဤအိုင်ကွန်အတွက် အက်ပ်အားမထည့်သွင်းထားပါ။ You can remove it, or search for the အက်ပ်and install it manually."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ဒေါင်းလုဒ်လုပ်နေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ကိုထည့်သွင်းရန်စောင့်နေသည်"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ဝိဂျက်များ"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"ပင်မမျက်နှာစာသို့ ထည့်ပါ"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"၎င်းအား ဤသို့ ရွှေ့ပါ"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ပင်မ ဖန်မျက်နှာပြင်သို့ ထည့်ပြီး၏"</string>
+ <string name="item_removed" msgid="851119963877842327">"၎င်းအား ဖယ်ရှားပြီး၏"</string>
+ <string name="action_move" msgid="4339390619886385032">"၎င်းအား ရွှေ့ပါ"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> အတိုင် <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့ပါ"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> သို့ နေရာရွှေ့ပါ"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"စိတ်ကြိုက်နေရာ <xliff:g id="NUMBER">%1$s</xliff:g> သို့ ရွှေ့ပါ"</string>
+ <string name="item_moved" msgid="4606538322571412879">"၎င်းအားရွှေ့ပြီး"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ဖိုလ်ဒါသို့ ထည့်ရန်- <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> အမည်ရှိ ဖိုလ်ဒါသို့ ထည့်ပြီး၏"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ဖိုလ်ဒါသို့ ထည့်ပြီး"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ဖိုလ်ဒါ ပြုလုပ်ရန်- <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ဖိုလ်ဒါ ပြုလုပ်ပြီး"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ပင်မမျက်နှာပြင်သို့ ရွှေ့ပါ"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"မျက်နှာပြင် ဘယ်ဘက်သို့ ရွှေ့ပါ"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"မျက်နှာပြင် ညာဘက်သို့ ရွှေ့ပါ"</string>
+ <string name="screen_moved" msgid="266230079505650577">"ဖန်မျက်နှာပြင် ပြောင်းရွှေ့ပြီး၏"</string>
+ <string name="action_resize" msgid="1802976324781771067">"အရွယ်အစားပြောင်းပါ"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"အကျယ်အား တိုးပါ"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"အမြင့်အား တိုးပါ"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"အကျယ်အား လျှော့ပါ"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"အမြင့်အား လျှော့ပါ"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Widget အား အကျယ် <xliff:g id="NUMBER_0">%1$s</xliff:g> အမြင့် <xliff:g id="NUMBER_1">%2$s</xliff:g> အရွယ်အစားပြန်လည်ချိန်ညှိပြီး၏"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"ဖြတ်လမ်းများ"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> အတွက် အမြန်နည်း <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ခု"</string>
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index f75d5e0..2b776d6 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Innstillinger"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Startsideinnstillinger"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Oversikt"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Tillat rotasjon av startskjermen"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Bruk systemstandard"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Superellipse"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Sirkel"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Dråpe"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 92d3b2d..558b6cf 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"सेटिंङहरू"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"गृहपृष्ठका सेटिङहरू"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"परिदृश्य"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ अनुप्रयोगका लागि"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"आइकनको आकार परिवर्तन गर्नुहोस्"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"प्रणालीको पूर्वनिर्धारित सेटिङ प्रयोग गर्नुहोस्"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"वर्ग"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"वर्गाकार वृत्त"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"वृत्त"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"आँसुको थोपा"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"आइकनको आकारमा गरिएका परिवर्तनहरू लागू गरिँदैछन्"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
new file mode 100644
index 0000000..46c8f9c
--- /dev/null
+++ b/res/values-ne/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"कार्य"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"अनुप्रयोग स्थापित छैन।"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"अनुप्रयोग उपलब्ध छैन"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"सुरक्षित मोडमा डाउनलोड गरेको अनुप्रयोग अक्षम गरिएको छ"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"विजेटलाई छान्न वा आफू अनुकूल कार्यहरू प्रयोग गर्न डबल ट्याप गरी होल्ड गर्नुहोस्।"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौडाइ गुणा %2$d उचाइ"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"म्यानुअल तरिकाले थप्न छुनुहोस् र थिची राख्नुहोस्"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"स्वतः थप्नुहोस्"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अनुप्रयोगहरू खोज्नुहोस्"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"अनुप्रयोगहरू लोड गरिँदै..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"थप अनुप्रयोगहरू खोज्नुहोस्"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"सूचनाहरू"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"अनुप्रयोगको सूची"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"हटाउनुहोस्"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित गर्नुहोस्"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"अनुप्रयोग जानकारी"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा अनुप्रयोगलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा अनुप्रयोगलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले फोन कलहरू गर्न अनुमति छैन"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"समस्या लोडिङ गर्ने विजेट"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"बेनाम फोल्डर"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %1$d को %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"नयाँ गृह स्क्रिन पृष्ठ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डरलाई बन्द गर्न ट्याप गर्नुहोस्"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनःनामाकरणलाई सुरक्षित गर्न ट्याप गर्नुहोस्"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बन्द भयो"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"सेटिंङहरू"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"परिदृश्य"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"फोनलाई घुमाइँदा"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"हालको प्रदर्शन सम्बन्धी सेटिङले घुमाउने सुविधालाई अनुमति दिँदैन"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"गृह स्क्रिनमा आइकन थप्नुहोस्"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"नयाँ अनुप्रयोगका लागि"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"आइकनको आकार परिवर्तन गर्नुहोस्"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"परिवर्तन नगर्नुहोस्"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"आइकनको आकारमा गरिएका परिवर्तनहरू लागू गरिँदैछन्"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"खोजी गर्नुहोस्"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"यो अनुप्रयोग स्थापित छैन"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईं यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड गर्दै, <xliff:g id="PROGRESS">%2$s</xliff:g> सम्पन्न"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> स्थापना गर्न प्रतीक्षा गर्दै"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेटहरू"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"गृह स्क्रिनमा थप्नुहोस्"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"वस्तु यहाँ सार्नुहोस्"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"वस्तु गृह स्क्रिनमा थपियो"</string>
+ <string name="item_removed" msgid="851119963877842327">"वस्तु हटाइयो"</string>
+ <string name="action_move" msgid="4339390619886385032">"वस्तु सार्नुहोस्"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"पङ्क्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तम्भ <xliff:g id="NUMBER_1">%2$s</xliff:g> मा सार्नुहोस्"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"मनपर्ने स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string>
+ <string name="item_moved" msgid="4606538322571412879">"वस्तु सारियो"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g> मा थप्नुहोस्"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"फोल्डरमा <xliff:g id="NAME">%1$s</xliff:g> सँग थप्नुहोस्"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"वस्तु फोल्डरमा थपियो"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"<xliff:g id="NAME">%1$s</xliff:g>: मार्फत फोल्डर सिर्जना गर्नुहोस्"</string>
+ <string name="folder_created" msgid="6409794597405184510">"फोल्डर सिर्जना गरियो"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"गृह स्क्रिनमा सार्नुहोस्"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"स्क्रिनलाई बायाँ सार्नुहोस्"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"स्क्रिनलाई दायाँ सार्नुहोस्"</string>
+ <string name="screen_moved" msgid="266230079505650577">"स्क्रिन सारियो"</string>
+ <string name="action_resize" msgid="1802976324781771067">"पुनःआकार मिलाउनुहोस्"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"चौडाइ बढाउनुहोस्"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"उँचाइ बढाउनुहोस्"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"चौडाइ घटाउनुहोस्"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"उँचाइ घटाउनुहोस्"</string>
+ <string name="widget_resized" msgid="9130327887929620">"विजेट चौडाइ <xliff:g id="NUMBER_0">%1$s</xliff:g> उचाइ <xliff:g id="NUMBER_1">%2$s</xliff:g> मा पुनः आकार मिलाइयो"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"सर्टकटहरू"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> का <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> सर्टकटहरू"</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 3349ed1..0180bb4 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Map: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Instellingen"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Instellingen voor homepage"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Overzicht"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Draaien van startscherm toestaan"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Systeemstandaard gebruiken"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Vierkant"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Traan"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 9fe8dd5..05fe509 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜਿਟ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ਸੈਟਿੰਗਾਂ"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"ਰੂਪ-ਰੇਖਾ"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ਮੁੱਖ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ਨਵੀਆਂ ਐਪਾਂ ਲਈ"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"ਸਿਸਟਮ ਦੀ ਪੂਰਵ-ਨਿਰਧਾਰਤ ਸੈਟਿੰਗ ਵਰਤੋ"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"ਵਰਗ"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"ਵਰਗਾਕਾਰ-ਚੱਕਰ"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"ਚੱਕਰ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"ਹੰਝੂ ਦੀ ਬੂੰਦ"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
new file mode 100644
index 0000000..c4c11ce
--- /dev/null
+++ b/res/values-pa/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"ਦਫ਼ਤਰ"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ਡਾਊਨਲੋਡ ਕੀਤਾ ਐਪ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT & ਹੋਲਡ ਕਰੋ।"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ਡਬਲ-ਟੈਪ & ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"ਹੱਥੀਂ ਰੱਖਣ ਲਈ ਸਪੱਰਸ਼ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"ਸਵੈਚਲਿਤ ਤਰੀਕੇ ਨਾਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮਿਲਦੀਆਂ ਕੋਈ ਵੀ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"ਹੋਰ ਐਪਾਂ ਖੋਜੋ"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"ਸੂਚਨਾਵਾਂ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ਐਪ ਸੂਚੀ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ਹੋਮ"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ਹਟਾਓ"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਇੰਸਟੌਲ ਕਰੋ"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਲਿਖੋ"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਫੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ਸਥਾਪਤ ਕਰੋ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ਬਿਨਾਂ ਨਾਮ ਦਿੱਤਾ ਫੋਲਡਰ"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ਫੋਲਡਰ ਖੋਲ੍ਹਿਆ, <xliff:g id="WIDTH">%1$d</xliff:g> ਬਾਇ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ਫੋਲਡਰ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"ਬਦਲੇ ਗਏ ਨਾਮ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ਫੋਲਡਰ ਬੰਦ ਕੀਤਾ"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ਫੋਲਡਰ ਨੂੰ <xliff:g id="NAME">%1$s</xliff:g> ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜਿਟ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ਸੈਟਿੰਗਾਂ"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"ਰੂਪ-ਰੇਖਾ"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ਮੁੱਖ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"ਮੁੱਖ ਸਕ੍ਰੀਨ \'ਤੇ ਪ੍ਰਤੀਕ ਸ਼ਾਮਲ ਕਰੋ"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"ਨਵੀਆਂ ਐਪਾਂ ਲਈ"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਬਦਲੋ"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"ਨਾ ਬਦਲੋ"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"ਆਈਕਨ ਦੀ ਆਕ੍ਰਿਤੀ ਵਿੱਚ ਤਬਦੀਲੀਆਂ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ਇਹ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ।"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ਇਸ ਆਈਕਨ ਲਈ ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਹੋਇਆ ਹੈ। ਤੁਸੀਂ ਇਸਨੂੰ ਹਟਾ ਸਕਦੇ ਹੋ ਜਾਂ ਐਪ ਖੋਜ ਸਕਦੇ ਹੋ ਅਤੇ ਇਸਨੂੰ ਮੈਨੂਅਲੀ ਇੰਸਟੌਲ ਕਰ ਸਕਦੇ ਹੋ।"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ਡਾਉਨਲੋਡ ਹੋਰ ਰਿਹਾ ਹੈ, <xliff:g id="PROGRESS">%2$s</xliff:g> ਸੰਪੂਰਣ"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ਸਥਾਪਿਤ ਕਰਨ ਦੀ ਉਡੀਕ ਕਰ ਰਿਹਾ ਹੈ"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ਵਿਜਿਟ"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
+ <string name="item_removed" msgid="851119963877842327">"ਅਈਟਮ ਹਟਾਈ ਗਈ"</string>
+ <string name="action_move" msgid="4339390619886385032">"ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ਮਨਪਸੰਦ ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
+ <string name="item_moved" msgid="4606538322571412879">"ਆਈਟਮ ਮੂਵ ਕੀਤੀ ਗਈ"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ਇਸ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜੋ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> ਦੇ ਨਾਲ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜੋ"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"ਆਈਟਮ ਨੂੰ ਫੋਲਡਰ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ਇਸਦੇ ਨਾਲ ਫੋਲਡਰ ਬਣਾਓ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ਫੋਲਡਰ ਬਣਾਇਆ ਗਿਆ"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"ਸਕ੍ਰੀਨ ਨੂੰ ਖੱਬੇ ਮੂਵ ਕਰੋ"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"ਸਕ੍ਰੀਨ ਨੂੰ ਸੱਜੇ ਮੂਵ ਕਰੋ"</string>
+ <string name="screen_moved" msgid="266230079505650577">"ਸਕ੍ਰੀਨ ਨੂੰ ਮੂਵ ਕੀਤਾ ਗਿਆ"</string>
+ <string name="action_resize" msgid="1802976324781771067">"ਮੁੜ ਆਕਾਰ ਦਿਓ"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"ਚੌੜਾਈ ਵਧਾਓ"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"ਉਂਚਾਈ ਵਧਾਓ"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"ਚੌੜਾਈ ਘਟਾਓ"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"ਉਂਚਾਈ ਘਟਾਓ"</string>
+ <string name="widget_resized" msgid="9130327887929620">"ਵਿਜੈਟ ਨੂੰ ਚੌੜਾਈ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਉਂਚਾਈ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਨੂੰ ਮੁੜ ਆਕਾਰ ਦਿੱਤਾ"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"ਸ਼ਾਰਟਕੱਟ"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਲਈ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ"</string>
+</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 1e7f6f7..f8a7f66 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Ustawienia"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia strony głównej"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Przegląd"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Zezwalaj na obrót ekranu głównego"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Użyj ustawienia domyślnego"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kwadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaokrąglony kwadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Okrąg"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Łza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 5ebe060..5c37eb8 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Definições"</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="accessibility_action_overview" msgid="6257665857640347026">"Vista geral"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação do ecrã principal"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Utilizar a predefinição do sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado e círculo"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 3d8d865..fd24066 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Configurações"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Configurações da página inicial"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Visão geral"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação da tela inicial"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Usar padrão do sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Quadrado"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Quadrado arredondado"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Círculo"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lágrima"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 3a273cd..e7b8144 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Setări"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Setări pentru ecranul de pornire"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Prezentare generală"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Permiteți rotirea ecranului de pornire"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Folosiți setarea prestabilită a sistemului"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Pătrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Pătrat cu colțuri rotunjite"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cerc"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Lacrimă"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 660804b..07ff761 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Настройки главного экрана"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Обзор"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Разрешить поворачивать главный экран"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Использовать системные настройки по умолчанию"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Квадрат с закругленными краями"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Капля"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index a986273..3b0425e 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"සැකසීම්"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home සැකසීම්"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"දළ විශ්ලේෂණය"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"පද්ධති පෙරනිමි භාවිත කරන්න"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"සමචතුරස්රය"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"හතරැස් කවය"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"කවය"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"කඳුළු බිංදුව"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
new file mode 100644
index 0000000..569a77f
--- /dev/null
+++ b/res/values-si/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"කාර්යාලය"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"යෙදුම ස්ථාපනය කර නැත."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"යෙදුම නොතිබේ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ආරක්ෂිත ආකාරය තුළ බාගන්න ලද යෙදුම් අබල කරන්න"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"විජට් එකක් අහුලා ගැනීමට හෝ අභිරුචි ක්රියා කිරීමට ඩබල් ටැප් කර අල්ලා ගෙන සිටින්න."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"පළල %1$d උස %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"අතින් ස්ථානගත කිරීමට ස්පර්ශ කර අල්ලාගෙන සිටින්න"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"ස්වයංක්රියව එක් කරන්න"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"යෙදුම් සෙවීම"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"යෙදුම් පූරණය වෙමින්…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"තව යෙදුම් සඳහා සොයන්න"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"දැනුම්දීම්"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"යෙදුම් ලැයිස්තුව"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"මුල් පිටුව"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ඉවත් කරන්න"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"අස්ථාපනය කරන්න"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"යෙදුම් තොරතුරු"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"මුල් පිටුවේ ඇති සැකසීම් සහ කෙටිමං කියවීමට යෙදුමකට අවසර දෙයි."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"මුල් පිටු සැකසීම් සහ කෙටිමං ලියන්න"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"මුල් පිටුවේ සැකසීම් සහ කෙටිමං ඉවත් කිරීමට යෙදුමට අවසර දෙයි."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> හට දුරකථන ඇමතුම් සිදු කිරීමට ඉඩ නොදේ"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ගැටලු පූරණ විජට් එක"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ස්ථාපනය කරන්න"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"නම් නොකළ ෆෝල්ඩරය"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> අබල කෙරිණි"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"නව මුල් පිටුව"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ෆෝල්ඩරය විවෘත විය, <xliff:g id="WIDTH">%1$d</xliff:g> හි <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ෆෝල්ඩරය වැසීමට තට්ටු කරන්න"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"යළි නම් කිරීම සුරැකීමට තට්ටු කරන්න"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ෆෝල්ඩරය වසා ඇත"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"සැකසීම්"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"දළ විශ්ලේෂණය"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"දුරකථනය කරකවන විට"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"වත්මන් සංදර්ශක සැකසීම් කරකැවීමට සහාය නොදක්වයි"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"මුල් පිටු තිරය වෙත අයිකනය එක් කරන්න"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"නව යෙදුම් සඳහා"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"සොයන්න"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"මෙම යෙදුම ස්ථාපනය කර නොමැත"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> බාගත කරමින්, <xliff:g id="PROGRESS">%2$s</xliff:g> සම්පූර්ණයි"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ස්ථාපනය කිරීමට බලා සිටිමින්"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> විජට්"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"මුල් තිරය වෙත එක් කරන්න"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"මෙතනට අයිතමය ගෙන එන්න"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"අයිතමය මුල් තිරය වෙත එකතු කරන ලදි"</string>
+ <string name="item_removed" msgid="851119963877842327">"අයිතමය ඉවත් කරන ලදි"</string>
+ <string name="action_move" msgid="4339390619886385032">"අයිතමය ගෙනයන්න"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"පේළිය <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුව <xliff:g id="NUMBER_1">%2$s</xliff:g> වෙත ගෙන යන්න"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ස්ථානය වෙත ගෙන යන්න"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ප්රියතම ස්ථානය <xliff:g id="NUMBER">%1$s</xliff:g> වෙත ගෙන යන්න"</string>
+ <string name="item_moved" msgid="4606538322571412879">"අයිතමය ගෙන යන ලදි"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ෆෝල්ඩරය එක් කරන්න: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> සමඟ ෆෝල්ඩරය වෙත එක් කරන්න"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"අයිතමය ෆෝඩරය වෙතට එක් කරන ලදි"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"මේ සමග ෆෝල්ඩරය සාදන්න: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ෆෝල්ඩරය සාදන ලදි"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"මුල් තිරය වෙත ගෙන යන්න"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"තිරය වම් පැත්තට ගෙනයන්න"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"තිරය දකුණු පැත්තට ගෙනයන්න"</string>
+ <string name="screen_moved" msgid="266230079505650577">"තිරය ගෙන යන ලදි"</string>
+ <string name="action_resize" msgid="1802976324781771067">"නැවත ප්රමාණගත කිරීම"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"පළල වැඩි කරන්න"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"උස වැඩි කරන්න"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"පළල අඩු කරන්න"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"උස අඩු කරන්න"</string>
+ <string name="widget_resized" msgid="9130327887929620">"විජට් පළල <xliff:g id="NUMBER_0">%1$s</xliff:g> උස <xliff:g id="NUMBER_1">%2$s</xliff:g> වෙත ප්රමාණකරණය කරන ලදි"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"කෙටිමං"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> සඳහා කෙටි මං <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
+</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index bc0d461..b7999f4 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Nastavenia"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia služby Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Prehľad"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Povoliť otáčanie plochy"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Použiť predvolené nastavenie systému"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Štvorec"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Okrúhly štvorec"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Kruh"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Slza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index f481a53..927b389 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Nastavitve"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Nastavitve začetnega zaslona"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Omogočanje sukanja začetnega zaslona"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Uporabi privzeto nastavitev sistema"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Zaobljen kvadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Krog"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Solza"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 3bcc540..1400557 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Cilësimet"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Përmbledhje"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit kryesor"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Përdor parazgjedhjen e sisteit"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Katror"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Katror me kënde të rrumbullakëta"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Rreth"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Pikë loti"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
new file mode 100644
index 0000000..2b65684
--- /dev/null
+++ b/res/values-sq/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Nisësi3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Puna"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Aplikacioni nuk është i instaluar."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Aplikacioni nuk mundësohet"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikacioni i shkarkuar është i çaktivizuar në modalitetin e sigurt"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikacionet janë të çaktivizuara në modalitetin e sigurt"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Shkurtorja nuk është e disponueshme"</string>
+ <string name="home_screen" msgid="806512411299847073">"Ekrani bazë"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Veprimet e personalizuara"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Prek dhe mbaj shtypur për të zgjedhur një miniaplikacion."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Prek dy herë dhe mbaj shtypur për të zgjedhur një miniaplikacion ose për të përdorur veprimet e personalizuara."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Prek dhe mbaj të shtypur për të vendosur në mënyrë manuale"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Shto automatikisht"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Kërko për aplikacione"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Kërko për më shumë aplikacione"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Njoftimet"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Lista e aplikacioneve"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Faqja kryesore"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Hiqe"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacion mbi aplikacionin"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalo shkurtore"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Lejon aplikacionin të lexojë cilësimet dhe shkurtoret në ekranin bazë."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"shkruaj cilësimet dhe shkurtoret e ekranit bazë"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Lejon aplikacionin të ndryshojë cilësimet dhe shkurtoret në ekranin bazë."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk lejohet të kryejë telefonata"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Problem në ngarkimin e miniaplikacionit"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguro"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
+ <string name="folder_hint_text" msgid="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>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Dosja u hap, <xliff:g id="WIDTH">%1$d</xliff:g> me <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Trokit për të mbyllur dosjen"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Trokit për të ruajtur riemërtimin"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Dosja u mbyll"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Dosja u riemërtua në <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Cilësimet"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Përmbledhje"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit kryesor"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cilësimi aktuali i afishimit nuk lejon rrotullimin"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Shto ikonë në ekranin bazë"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Për aplikacionet e reja"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Kërko"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikacioni nuk është i instaluar"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacioni për këtë ikonë nuk është i instaluar. Mund ta heqësh ose të kërkosh aplikacionin dhe ta instalosh atë në mënyrë manuale."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> po shkarkohet, <xliff:g id="PROGRESS">%2$s</xliff:g> të përfunduara"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> po pret të instalohet"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Miniaplikacionet e <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Shto në Ekranin bazë"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Zhvendose artikullin këtu"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Artikulli u shtua tek ekrani bazë"</string>
+ <string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string>
+ <string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"Zhvendos te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolona <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g> i preferencave"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Artikulli u zhvendos"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"Shto te dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"Shto te dosja me <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Artikulli u shtua te dosja"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"Krijo një dosje me: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Dosja u krijua"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Zhvendose në Ekranin bazë"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Zhvendose ekranin në të majtë"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Zhvendose ekranin në të djathtë"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Ekrani u zhvendos"</string>
+ <string name="action_resize" msgid="1802976324781771067">"Ndrysho madhësinë"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Rrit gjerësinë"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Rrit lartësinë"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Zvogëlo gjerësinë"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Zvogëlo lartësinë"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Madhësia e miniaplikacionit u ndryshua me gjerësinë <xliff:g id="NUMBER_0">%1$s</xliff:g> dhe lartësinë <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Shkurtoret"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shkurtesa për <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
+</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 78bb67c..2cfaee6 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Подешавања"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Подешавања почетног екрана"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Преглед"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Дозволи ротацију почетног екрана"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Користи подразумевано системско подешавање"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Заобљени квадрат"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Суза"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index bef1dbf..aa6e99a 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Inställningar"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Inställningar för startsidan"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Översikt"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Tillåt rotering av startskärmen"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Använd systemstandard"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kvirkel"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Cirkel"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Droppe"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6d0c0a3..3b6d373 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -72,7 +72,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Mipangilio"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Mipangilio ya ukurasa wa mwanzo"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Muhtasari"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Ruhusu kuzungusha skrini ya Kwanza"</string>
@@ -86,6 +86,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Tumia umbo chaguo-msingi la mfumo"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Mraba"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Mstatili wenye pembe duara"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Mduara"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Umbo la chozi"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 358d9b6..e836d7d 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -15,10 +15,16 @@
-->
<resources>
-<!-- All Apps -->
+ <!-- All Apps -->
<dimen name="all_apps_button_scale_down">8dp</dimen>
<dimen name="all_apps_search_bar_height">54dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
<dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
+ <!-- Fast scroll -->
+ <dimen name="fastscroll_popup_width">75dp</dimen>
+ <dimen name="fastscroll_popup_height">62dp</dimen>
+ <dimen name="fastscroll_popup_padding">13dp</dimen>
+ <dimen name="fastscroll_popup_text_size">32dp</dimen>
+
</resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index de809b1..bb0dbc2 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -26,13 +26,13 @@
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+ <item name="android:keyboardLayout">@layout/all_apps_search_container</item>
</style>
<!-- Workspace -->
<style name="DropTargetButton" parent="DropTargetButtonBase">
<item name="android:paddingLeft">60dp</item>
<item name="android:paddingRight">60dp</item>
- <item name="android:shadowColor">#393939</item>
<item name="android:shadowDx">0.0</item>
<item name="android:shadowDy">0.0</item>
<item name="android:shadowRadius">2.0</item>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 770af4a..be8e82d 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ஷார்ட்கட்ஸ்"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"அமைப்பு"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"மேலோட்டப் பார்வை"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"முகப்புத் திரை சுழற்சியை அனுமதி"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"அமைப்பின் இயல்புநிலையைப் பயன்படுத்து"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"சதுரம்"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"சதுரவட்டம்"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"வட்டம்"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"கண்ணீர்துளி"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
new file mode 100644
index 0000000..a1e00a1
--- /dev/null
+++ b/res/values-ta/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"பணியிடம்"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"பயன்பாடு நிறுவப்படவில்லை."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"பயன்பாடு இல்லை"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"விட்ஜெட்டைத் தேர்ந்தெடுக்க இருமுறை தட்டிப் பிடிக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d அகலத்திற்கு %2$d உயரம்"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"நீங்களே சேர்க்க, தொட்டுப் பிடித்திருக்கவும்"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"தானாகவே சேர்"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"பயன்பாடுகளில் தேடுக"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"பயன்பாடுகளை ஏற்றுகிறது..."</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"அறிவிப்புகள்"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"பயன்பாடுகளின் பட்டியல்"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"அகற்று"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"பயன்பாட்டுத் தகவல்"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"முகப்பில் உள்ள அமைப்பு மற்றும் குறுக்குவழிகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளை எழுதுதல்"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"முகப்பில் உள்ள அமைப்பு மற்றும் குறுக்குவழிகளை மாற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"ஃபோன் அழைப்புகள் செய்ய, <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதிக்கப்படவில்லை"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"விட்ஜெட்டை ஏற்றுவதில் சிக்கல்"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"அமைவு"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"பெயரிடப்படாத கோப்புறை"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> முடக்கப்பட்டது"</string>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"திறக்கப்பட்டக் கோப்புறை, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"கோப்புறையை மூட, தட்டவும்"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"மாற்றிய பெயரைச் சேமிக்க, தட்டவும்"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"கோப்புறை மூடப்பட்டது"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"கோப்புறை <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ஷார்ட்கட்ஸ்"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"அமைப்பு"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"மேலோட்டப் பார்வை"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"முகப்புத் திரை சுழற்சியை அனுமதி"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"மொபைலைச் சுழற்றும் போது"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"முகப்புத் திரையில் ஐகானைச் சேர்"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"புதிய பயன்பாடுகளுக்கு"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"அகற்று"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"தேடு"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"பயன்பாடு நிறுவப்படவில்லை"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ஐகானுக்கான பயன்பாடு நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g>ஐப் பதிவிறக்குகிறது, <xliff:g id="PROGRESS">%2$s</xliff:g> முடிந்தது"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>ஐ நிறுவுவதற்காகக் காத்திருக்கிறது"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> விட்ஜெட்டுகள்"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"முகப்புத் திரையில் சேர்"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"இங்கு நகர்த்து"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
+ <string name="item_removed" msgid="851119963877842327">"அகற்றப்பட்டது"</string>
+ <string name="action_move" msgid="4339390619886385032">"நகர்த்து"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> வரிசை, <xliff:g id="NUMBER_1">%2$s</xliff:g> நெடுவரிசைக்கு நகர்த்து"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"விரும்பும் நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string>
+ <string name="item_moved" msgid="4606538322571412879">"உருப்படி நகர்த்தப்பட்டது"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"இந்தக் கோப்புறையில் சேர்க்கும்: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> உள்ள கோப்புறையில் சேர்க்கும்"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"கோப்புறையில் உருப்படி சேர்க்கப்பட்டது"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"இதனுடன் கோப்புறையை உருவாக்கும்: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"கோப்புறை உருவாக்கப்பட்டது"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"முகப்புத் திரைக்கு நகர்த்து"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"திரையை இடப்புறம் நகர்த்து"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"திரையை வலப்புறம் நகர்த்து"</string>
+ <string name="screen_moved" msgid="266230079505650577">"திரை நகர்த்தப்பட்டது"</string>
+ <string name="action_resize" msgid="1802976324781771067">"அளவு மாற்று"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"அகலத்தை அதிகரி"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"உயரத்தை அதிகரி"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"அகலத்தைக் குறை"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"உயரத்தைக் குறை"</string>
+ <string name="widget_resized" msgid="9130327887929620">"அகலம் <xliff:g id="NUMBER_0">%1$s</xliff:g> மற்றும் உயரம் <xliff:g id="NUMBER_1">%2$s</xliff:g>க்கு விட்ஜெட் அளவு மாற்றப்பட்டது"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"குறுக்குவழிகள்"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>க்கான <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> குறுக்குவழிகள்"</string>
+</resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 6fe9a60..15f3de7 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్లు"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్పేపర్లు"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"సెట్టింగ్లు"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"హోమ్ సెట్టింగ్లు"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"స్థూలదృష్టి"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"కొత్త అనువర్తనాల కోసం"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"చిహ్న ఆకారాన్ని మార్చు"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"సిస్టమ్ డిఫాల్ట్ను ఉపయోగించండి"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"చతురస్రం"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"చతురస్రాకార వృత్తం"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"వృత్తం"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"కన్నీటి చుక్క"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"చిహ్న ఆకార మార్పులను వర్తింపజేస్తోంది"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
new file mode 100644
index 0000000..65ca927
--- /dev/null
+++ b/res/values-te/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"కార్యాలయం"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"అనువర్తనం ఇన్స్టాల్ చేయబడలేదు."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"అనువర్తనం అందుబాటులో లేదు"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"డౌన్లోడ్ చేసిన అనువర్తనం సురక్షిత మోడ్లో నిలిపివేయబడింది"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"విడ్జెట్ను ఎంచుకోవడానికి తాకి & నొక్కి పెట్టండి."</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 వెడల్పు X %2$d ఎత్తు"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"మాన్యువల్గా ఉంచడానికి నొక్కి &amp పట్టుకోండి"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"స్వయంచాలకంగా జోడించు"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"అనువర్తనాలను శోధించండి"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"అనువర్తనాలను లోడ్ చేస్తోంది…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"మరిన్ని అనువర్తనాల కోసం శోధించు"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"నోటిఫికేషన్లు"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్లో ఖాళీ లేదు."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"అనువర్తనాల జాబితా"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"హోమ్"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"తీసివేయి"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్స్టాల్ చేయి"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"అనువర్తన సమాచారం"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్స్టాల్ చేయడం"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడం"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్లో సెట్టింగ్లు మరియు సత్వరమార్గాలను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"హోమ్ సెట్టింగ్లు మరియు సత్వరమార్గాలను వ్రాయడం"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"హోమ్లో సెట్టింగ్లు మరియు సత్వరమార్గాలను మార్చడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"ఫోన్ కాల్లను చేసేందుకు <xliff:g id="APP_NAME">%1$s</xliff:g>కి అనుమతి లేదు"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"విడ్జెట్ను లోడ్ చేయడంలో సమస్య"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"సెటప్ చేయి"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్ఇన్స్టాల్ చేయడం సాధ్యపడదు."</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"పేరు లేని ఫోల్డర్"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> నిలిపివేయబడింది"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"కొత్త హోమ్ స్క్రీన్ పేజీ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ఫోల్డర్ తెరవబడింది, <xliff:g id="WIDTH">%1$d</xliff:g> X <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"ఫోల్డర్ను మూసివేయడానికి నొక్కండి"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"పేరు మార్పును సేవ్ చేయడానికి నొక్కండి"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ఫోల్డర్ మూసివేయబడింది"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్లు"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్పేపర్లు"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"సెట్టింగ్లు"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"స్థూలదృష్టి"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"ఫోన్ను తిప్పినప్పుడు"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ప్రస్తుత డిస్ప్లే సెట్టింగ్ భ్రమణాన్ని అనుమతించలేదు"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"హోమ్ స్క్రీన్కి చిహ్నాన్ని జోడించు"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"కొత్త అనువర్తనాల కోసం"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"చిహ్న ఆకారాన్ని మార్చు"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"మార్చవద్దు"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"చిహ్న ఆకార మార్పులను వర్తింపజేస్తోంది"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"శోధించు"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ఈ అనువర్తనం ఇన్స్టాల్ చేయబడలేదు"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ఈ చిహ్నం యొక్క అనువర్తనం ఇన్స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్గా ఇన్స్టాల్ చేయవచ్చు."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> డౌన్లోడ్ అవుతోంది, <xliff:g id="PROGRESS">%2$s</xliff:g> పూర్తయింది"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ఇన్స్టాల్ కావడానికి వేచి ఉంది"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> విడ్జెట్లు"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"హోమ్ స్క్రీన్కు జోడించు"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"అంశాన్ని ఇక్కడికి తరలించు"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"అంశం హోమ్స్క్రీన్కి జోడించబడింది"</string>
+ <string name="item_removed" msgid="851119963877842327">"అంశం తీసివేయబడింది"</string>
+ <string name="action_move" msgid="4339390619886385032">"అంశాన్ని తరలించు"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"అడ్డు వరుస <xliff:g id="NUMBER_0">%1$s</xliff:g> నిలువు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>కి తరలించు"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"ఇష్టమైనవిలో <xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
+ <string name="item_moved" msgid="4606538322571412879">"అంశం తరలించబడింది"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"ఈ ఫోల్డర్కి జోడించండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> గల ఫోల్డర్కు జోడించు"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"అంశం ఫోల్డర్కు జోడించబడింది"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"ఈ పేరుతో ఫోల్డర్ను సృష్టించండి: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"ఫోల్డర్ సృష్టించబడింది"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"హోమ్స్క్రీన్కు తరలించు"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"స్క్రీన్ను ఎడమవైపుకి జరుపు"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"స్క్రీన్ను కుడివైపుకి జరుపు"</string>
+ <string name="screen_moved" msgid="266230079505650577">"స్క్రీన్ జరపబడింది"</string>
+ <string name="action_resize" msgid="1802976324781771067">"పరిమాణం మార్చు"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"వెడల్పును పెంచు"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"ఎత్తును పెంచు"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"వెడల్పును తగ్గించు"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"ఎత్తును తగ్గించు"</string>
+ <string name="widget_resized" msgid="9130327887929620">"విడ్జెట్ పరిమాణం వెడల్పు <xliff:g id="NUMBER_0">%1$s</xliff:g>కి, ఎత్తు <xliff:g id="NUMBER_1">%2$s</xliff:g>కి మార్చబడింది"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"సత్వరమార్గాలు"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> కోసం <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> సత్వరమార్గాలు"</string>
+</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 73b6dd9..e03931e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"การตั้งค่า"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าแรก"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"ภาพรวม"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"อนุญาตให้หมุนหน้าจอหลัก"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"ใช้ค่าเริ่มต้นของระบบ"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"สี่เหลี่ยมจัตุรัส"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"สี่เหลี่ยมขอบมน"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"วงกลม"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"หยดน้ำตา"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index a50358e..12bc013 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Mga Setting"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Mga setting ng Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Pangkalahatang-ideya"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Payagan ang pag-rotate ng Home screen"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Gamitin ang default ng system"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Parisukat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Bilog"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Teardrop"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 6f007fc..b245bff 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Ana ekran ayarları"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Genel bakış"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Ana ekranı döndürmeye izin ver"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sistem varsayılanını kullan"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kare"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Kare-daire"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Daire"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Gözyaşı damlası"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 318db66..8d33910 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Папка <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Налаштування"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Налаштування Home"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Огляд"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Дозволити обертання головного екрана"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Використовувати налаштування системи за умовчанням"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Квадрат"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Квадрат із заокругленими кутами"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Круг"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Сльоза"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index a466db2..84966b2 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"ترتیبات"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"ہوم ترتیبات"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"مجموعی جائزہ"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
@@ -83,6 +83,10 @@
<string name="auto_add_shortcuts_description" msgid="7117251166066978730">"نئی ایپس کیلئے"</string>
<string name="icon_shape_override_label" msgid="2977264953998281004">"آئیکن کی شکل تبدیل کریں"</string>
<string name="icon_shape_system_default" msgid="1709762974822753030">"سسٹم ڈیفالٹ کا استعمال کریں"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"مربع"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"اسکورکل"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"حلقہ"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"آنسو کا قطرہ"</string>
<string name="icon_shape_override_progress" msgid="3461735694970239908">"آئيکن کی شکل کی تبدیلیاں لاگو ہو رہی ہیں"</string>
<string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
<string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
new file mode 100644
index 0000000..29d1cd5
--- /dev/null
+++ b/res/values-ur/strings.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"دفتری"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ایپ انسٹال نہیں ہے۔"</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"ایپ دستیاب نہیں ہے"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ڈاؤن لوڈ کردہ ایپ کو محفوظ وضع میں غیر فعال کر دیا گیا"</string>
+ <string name="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="long_press_widget_to_add" msgid="7699152356777458215">"کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"کوئی ویجٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور پکڑے رکھیں۔"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d چوڑا اور %2$d اونچا"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"دستی طور پر رکھنے کیلئے & ٹچ کرکے ہولڈ کریں"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"خود کار طور پر شامل کریں"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ایپس تلاش کریں"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"ایپس لوڈ ہو رہی ہیں…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"مزید ایپس تلاش کریں"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"اطلاعات"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"ایپس کی فہرست"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ہوم"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"ہٹائیں"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"اَن انسٹال کریں"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"ایپ کی معلومات"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ایپ کو ہوم میں ترتیبات اور شارٹ کٹس کو پڑھنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ہوم ترتیبات اور شارٹ کٹس کو لکھیں"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ایپ کو ہوم میں ترتیبات اور شارٹ کٹس کو تبدیل کرنے کی اجازت دیتا ہے۔"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو فون کالیں کرنے کی اجازت نہیں ہے"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ویجیٹ کو لوڈ کرنے میں مسئلہ"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ترتیب دیں"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"بلا نام فولڈر"</string>
+ <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیر فعال ہے"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"صفحہ %1$d از %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ہوم اسکرین %1$d از %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"نیا ہوم اسکرین صفحہ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"فولڈر کھولا گیا، <xliff:g id="WIDTH">%1$d</xliff:g> × <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"فولڈر کو بند کرنے کیلئے تھپتھپائیں"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"نام کی تبدیلی محفوظ کرنے کیلئے تھپتھپائیں"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"فولڈر بند ہو گیا"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ترتیبات"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"مجموعی جائزہ"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"جب فون گھمایا جاتا ہے"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"موجودہ ڈسپلے ترتیب گھمانے کی اجازت نہیں دیتی"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"آئیکن کو ہوم اسکرین میں شامل کریں"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"نئی ایپس کیلئے"</string>
+ <string name="icon_shape_override_label" msgid="2977264953998281004">"آئیکن کی شکل تبدیل کریں"</string>
+ <string name="icon_shape_no_override" msgid="3678524428085518367">"تبدیل نہ کریں"</string>
+ <string name="icon_shape_override_progress" msgid="3461735694970239908">"آئيکن کی شکل کی تبدیلیاں لاگو ہو رہی ہیں"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"تلاش کریں"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"یہ ایپ انسٹال کردہ نہیں ہے"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔"</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ڈاؤن لوڈ ہو رہا ہے، <xliff:g id="PROGRESS">%2$s</xliff:g> مکمل ہو گیا"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> انسٹال ہونے کا انتظار کر رہی ہے"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ویجیٹس"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"ہوم اسکرین میں شامل کریں"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"آئٹم یہاں منتقل کریں"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"آئٹم کو ہوم اسکرین میں شامل کر دیا گیا"</string>
+ <string name="item_removed" msgid="851119963877842327">"آئٹم ہٹا دیا گیا"</string>
+ <string name="action_move" msgid="4339390619886385032">"آئٹم منتقل کریں"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"پسندیدہ پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string>
+ <string name="item_moved" msgid="4606538322571412879">"آئٹم منتقل کر دیا گیا"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"فولڈر میں شامل کریں: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> کے فولڈر میں شامل کریں"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"آئٹم فولڈر میں شامل کر دیا گیا"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"اس کے ساتھ فولڈر بنائیں: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_created" msgid="6409794597405184510">"فولڈر بنا دیا گیا"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"ہوم اسکرین میں منتقل کریں"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"اسکرین کو بائیں منتقل کریں"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"اسکرین کو دائیں منتقل کریں"</string>
+ <string name="screen_moved" msgid="266230079505650577">"اسکرین منتقل کر دی گئی"</string>
+ <string name="action_resize" msgid="1802976324781771067">"سائز تبدیل کریں"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"چوڑائی بڑھائیں"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"اونچائی بڑھائیں"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"چوڑائی کم کریں"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"اونچائی کم کریں"</string>
+ <string name="widget_resized" msgid="9130327887929620">"ویجیٹ کے سائز کو چوڑائی <xliff:g id="NUMBER_0">%1$s</xliff:g> اونچائی <xliff:g id="NUMBER_1">%2$s</xliff:g> میں تبدیل کر دیا گیا"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"شارٹ کٹس"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> کیلئے <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> شارٹ کٹس"</string>
+</resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index a43949b..a4725db 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Sozlamalar"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home sozlamalari"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Umumiy ko‘rinish"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Standart tizim parametrlaridan foydalanish"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Kvadrat"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Qirralari aylana kvadrat"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Aylana"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Tomchi"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
new file mode 100644
index 0000000..81be979
--- /dev/null
+++ b/res/values-uz/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="work_folder_name" msgid="3753320833950115786">"Ishga oid"</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Ilova o‘rnatilmadi."</string>
+ <string name="activity_not_available" msgid="7456344436509528827">"Ilova mavjud emas"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi"</string>
+ <string name="safemode_widget_error" msgid="4863470563535682004">"Xavfsiz rejimda vidjetlar o‘chirib qo‘yilgan"</string>
+ <string name="shortcut_not_available" msgid="2536503539825726397">"Tezkor tugmadan foydalanib bo‘lmaydi"</string>
+ <string name="home_screen" msgid="806512411299847073">"Bosh ekran"</string>
+ <string name="custom_actions" msgid="3747508247759093328">"Maxsus amallar"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidjetni tanlash uchun bosib turing."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
+ <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Qo‘lda joylashtirish uchun bosib turing"</string>
+ <string name="place_automatically" msgid="8064208734425456485">"Avtomatik chiqarish"</string>
+ <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ilovalar ichidan qidirish"</string>
+ <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilovalar yuklanmoqda…"</string>
+ <string name="all_apps_no_search_results" msgid="6332185285860416787">"“<xliff:g id="QUERY">%1$s</xliff:g>” so‘rovi bo‘yicha hech narsa topilmadi"</string>
+ <string name="all_apps_search_market_message" msgid="1366263386197059176">"Boshqa ilovalarni qidirish"</string>
+ <string name="notifications_header" msgid="1404149926117359025">"Bildirishnomalar"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
+ <string name="all_apps_button_label" msgid="8130441508702294465">"Ilovalar ro‘yxati"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Bosh sahifa"</string>
+ <string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
+ <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
+ <string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalarni o‘qish uchun ruxsat beradi."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Uy sozlamalari va yorliqlarini yozish"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalrni o‘zgartirish uchun ruxsat beradi."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga qo‘ng‘iroqlarni amalga oshirishga ruxsat berilmagan"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Vidjetni yuklashda muammo"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Sozlash"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
+ <string name="folder_hint_text" msgid="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>
+ <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>
+ <string name="folder_opened" msgid="94695026776264709">"Jild ochildi, <xliff:g id="WIDTH">%1$d</xliff:g> ga <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Jildni yopish uchun ustiga bosing"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"O‘zgarishni saqlash uchun ustiga bosing"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Jild yopildi"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Sozlamalar"</string>
+ <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
+ <string name="accessibility_action_overview" msgid="6257665857640347026">"Umumiy ko‘rinish"</string>
+ <string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
+ <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon burilganda"</string>
+ <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi"</string>
+ <string name="auto_add_shortcuts_label" msgid="8222286205987725611">"Bosh ekranga ikonka qo‘shish"</string>
+ <string name="auto_add_shortcuts_description" msgid="7117251166066978730">"Yangi o‘rnatilgan ilovalar ikonkasini bosh ekranga chiqarish"</string>
+ <!-- no translation found for icon_shape_override_label (2977264953998281004) -->
+ <skip />
+ <!-- no translation found for icon_shape_no_override (3678524428085518367) -->
+ <skip />
+ <!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
+ <skip />
+ <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Qidirish"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ushbu ilova o‘rnatilmagan"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> yuklab olinmoqda, <xliff:g id="PROGRESS">%2$s</xliff:g> bajarildi"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi o‘rnatilishi kutilmoqda"</string>
+ <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> vidjetlari"</string>
+ <string name="action_add_to_workspace" msgid="8902165848117513641">"Bosh ekranga qo‘shish"</string>
+ <string name="action_move_here" msgid="2170188780612570250">"Obyektni bu yerga ko‘chirish"</string>
+ <string name="item_added_to_workspace" msgid="4211073925752213539">"Obyekt bosh ekranga qo‘shildi"</string>
+ <string name="item_removed" msgid="851119963877842327">"Obyekt o‘chirib tashlandi"</string>
+ <string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</string>
+ <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g> katakka ko‘chirib o‘tkazish"</string>
+ <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga ko‘chirib o‘tkazish"</string>
+ <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga (<xliff:g id="NUMBER">%1$s</xliff:g>) ko‘chirib o‘tkazish"</string>
+ <string name="item_moved" msgid="4606538322571412879">"Element ko‘chirib o‘tkazildi"</string>
+ <string name="add_to_folder" msgid="9040534766770853243">"<xliff:g id="NAME">%1$s</xliff:g> jildiga qo‘shish"</string>
+ <string name="add_to_folder_with_app" msgid="4534929978967147231">"<xliff:g id="NAME">%1$s</xliff:g> ilovasi bor jildga qo‘shish"</string>
+ <string name="added_to_folder" msgid="4793259502305558003">"Element jildga qo‘shildi"</string>
+ <string name="create_folder_with" msgid="4050141361160214248">"<xliff:g id="NAME">%1$s</xliff:g> bilan jild yaratish"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Jild yaratildi"</string>
+ <string name="action_move_to_workspace" msgid="1603837886334246317">"Bosh ekranga ko‘chirish"</string>
+ <string name="action_move_screen_left" msgid="8854216831569401665">"Ekranni chapga siljitish"</string>
+ <string name="action_move_screen_right" msgid="329334910274311123">"Ekranni o‘ngga siljitish"</string>
+ <string name="screen_moved" msgid="266230079505650577">"Ekran siljitildi"</string>
+ <string name="action_resize" msgid="1802976324781771067">"O‘lchamini o‘zgartirish"</string>
+ <string name="action_increase_width" msgid="8773715375078513326">"Enini uzaytirish"</string>
+ <string name="action_increase_height" msgid="459390020612501122">"Bo‘yini uzaytirish"</string>
+ <string name="action_decrease_width" msgid="1374549771083094654">"Enini kichraytirish"</string>
+ <string name="action_decrease_height" msgid="282377193880900022">"Bo‘yini kichraytirish"</string>
+ <string name="widget_resized" msgid="9130327887929620">"Vidjetning eni <xliff:g id="NUMBER_0">%1$s</xliff:g>, bo‘yi <xliff:g id="NUMBER_1">%2$s</xliff:g> qilib o‘zgartirildi"</string>
+ <string name="action_deep_shortcut" msgid="2864038805849372848">"Tezkor tugmalar"</string>
+ <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ilovasi uchun <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ta tezkor tugma"</string>
+</resources>
diff --git a/res/values-v19/styles.xml b/res/values-v19/styles.xml
index cfc7c0f..36c0971 100644
--- a/res/values-v19/styles.xml
+++ b/res/values-v19/styles.xml
@@ -18,7 +18,7 @@
-->
<resources>
- <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+ <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
index 8d3de01..927719c 100644
--- a/res/values-v21/styles.xml
+++ b/res/values-v21/styles.xml
@@ -18,7 +18,7 @@
-->
<resources>
- <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+ <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
diff --git a/res/values-v26/styles.xml b/res/values-v26/styles.xml
index cb18409..fd6fc4d 100644
--- a/res/values-v26/styles.xml
+++ b/res/values-v26/styles.xml
@@ -21,6 +21,10 @@
<style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
</style>
+ <style name="WidgetContainerTheme.Dark" parent="LauncherThemeDark">
+ <item name="android:colorEdgeEffect">?android:attr/textColorSecondary</item>
+ <item name="android:colorPrimaryDark">#616161</item> <!-- Gray 700 -->
+ </style>
<!-- From O and above, we show a dark nav bar in all-apps -->
<style name="AllAppsNavBarProtection">
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index b8ecc2f..f6cb8f2 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Cài đặt"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt trang chủ"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Tổng quan"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Cho phép xoay Màn hình chính"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sử dụng mặc định của hệ thống"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Hình vuông"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"Hình vuông cạnh bo tròn"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Hình tròn"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"Hình giọt nước"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7aaff43..5dcef74 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"设置"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"概览"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"允许旋转主屏幕"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"使用系统默认设置"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"方形"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"方圆形"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"圆形"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"泪珠形"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index b699a51..2bbc160 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"概覽"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"允許主畫面旋轉"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設設定"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"正方形"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"方圓形"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"圓形"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"淚珠形"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 9e689c4..f1605f6 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"總覽"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"使用系統預設值"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"正方形"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"方圓形"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"圓形"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"淚珠形"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 6cab4b9..12d14bd 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -70,7 +70,7 @@
<string name="folder_name_format" msgid="6629239338071103179">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
- <string name="settings_button_text" msgid="8119458837558863227">"Izilungiselelo"</string>
+ <string name="settings_button_text" msgid="8873672322605444408">"Izilungiselelo zasekhaya"</string>
<string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
<string name="accessibility_action_overview" msgid="6257665857640347026">"Ukubuka konke"</string>
<string name="allow_rotation_title" msgid="7728578836261442095">"Vumela ukuphendukiswa kwesikrini sasekhaya"</string>
@@ -84,6 +84,10 @@
<!-- no translation found for icon_shape_override_label (2977264953998281004) -->
<skip />
<string name="icon_shape_system_default" msgid="1709762974822753030">"Sebenzisa okuzenzakalelayo kwesistimu"</string>
+ <string name="icon_shape_square" msgid="633575066111622774">"Isikwele"</string>
+ <string name="icon_shape_squircle" msgid="5658049910802669495">"I-Squircle"</string>
+ <string name="icon_shape_circle" msgid="6550072265930144217">"Indingiliza"</string>
+ <string name="icon_shape_teardrop" msgid="4525869388200835463">"I-Teardrop"</string>
<!-- no translation found for icon_shape_override_progress (3461735694970239908) -->
<skip />
<string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 18f409f..7b52dae 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -16,7 +16,21 @@
*/
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- Attributes used for launcher theme -->
+ <attr name="allAppsScrimColor" format="color" />
+ <attr name="popupColorPrimary" format="color" />
+ <attr name="popupColorSecondary" format="color" />
+ <attr name="popupColorTertiary" format="color" />
+ <attr name="isMainColorDark" format="boolean" />
+ <attr name="isWorkspaceDarkText" format="boolean" />
+ <attr name="workspaceTextColor" format="color" />
+ <attr name="workspaceShadowColor" format="color" />
+ <attr name="workspaceAmbientShadowColor" format="color"/>
+ <attr name="workspaceKeyShadowColor" format="color" />
+ <attr name="workspaceStatusBarScrim" format="reference" />
+ <attr name="widgetsTheme" format="reference" />
<!-- BubbleTextView specific attributes. -->
<declare-styleable name="BubbleTextView">
@@ -30,10 +44,17 @@
<enum name="shortcut_popup" value="4" />
</attr>
<attr name="deferShadowGeneration" format="boolean" />
- <attr name="customShadows" format="boolean" />
<attr name="centerVertically" format="boolean" />
</declare-styleable>
+ <declare-styleable name="ShadowInfo">
+ <attr name="ambientShadowColor" format="color" />
+ <attr name="ambientShadowBlur" format="dimension" />
+ <attr name="keyShadowColor" format="color" />
+ <attr name="keyShadowBlur" format="dimension" />
+ <attr name="keyShadowOffset" format="dimension" />
+ </declare-styleable>
+
<!-- PagedView specific attributes. These attributes are used to customize
a PagedView view in XML files. -->
<declare-styleable name="PagedView">
@@ -96,9 +117,9 @@
<attr name="numHotseatIcons" format="integer" />
<attr name="iconSize" format="float" />
+ <!-- landscapeIconSize defaults to iconSize, if not specified -->
+ <attr name="landscapeIconSize" format="float" />
<attr name="iconTextSize" format="float" />
- <!-- hotseatIconSize defaults to iconSize, if not specified -->
- <attr name="hotseatIconSize" format="float" />
<attr name="defaultLayoutId" format="reference" />
</declare-styleable>
@@ -111,4 +132,14 @@
</attr>
</declare-styleable>
+ <declare-styleable name="ShadowDrawable">
+ <attr name="android:src" />
+ <attr name="android:shadowColor" />
+ <attr name="android:elevation" />
+ <attr name="darkTintColor" format="color"/>
+ </declare-styleable>
+
+ <declare-styleable name="RecyclerViewFastScroller">
+ <attr name="canThumbDetach" format="boolean" />
+ </declare-styleable>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 67a7544..b44a31e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -25,28 +25,19 @@
<color name="focused_background">#80c6c5c5</color>
- <color name="workspace_icon_text_color">#FFF</color>
- <color name="workspace_edge_effect_color">#FFFFFFFF</color>
-
<color name="default_shadow_color_no_alpha">#FF000000</color>
- <color name="outline_color">#FFFFFFFF</color>
-
<color name="spring_loaded_panel_color">#40FFFFFF</color>
<color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
<!-- Popup container -->
- <color name="popup_header_background_color">#EEEEEE</color> <!-- Gray 200 -->
- <color name="popup_background_color">#FFF</color>
<color name="notification_icon_default_color">#757575</color> <!-- Gray 600 -->
- <color name="notification_color_beneath">#E0E0E0</color> <!-- Gray 300 -->
-
<color name="badge_color">#1DE9B6</color> <!-- Teal A400 -->
<color name="folder_badge_color">#1DE9B6</color> <!-- Teal A400 -->
- <!-- System shortcuts -->
- <color name="system_shortcuts_icon_color">@android:color/tertiary_text_light</color>
-
- <color name="legacy_icon_background">#FFFFFF</color>
<color name="icon_background">#E0E0E0</color> <!-- Gray 300 -->
+ <color name="legacy_icon_background">#FFFFFF</color>
+
+ <color name="all_apps_bg_hand_fill">#E5E5E5</color>
+ <color name="all_apps_bg_hand_fill_dark">#9AA0A6</color>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 5a8da76..00db4ef 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -13,6 +13,10 @@
easily override the app name without providing all translations -->
<string name="derived_app_name" translatable="false">@string/app_name</string>
+ <!-- String representing the intent for search on the apps market. To specify a query, add
+ q=<query> to the data to the intent -->
+ <string name="market_search_intent" translatable="false">market://search?c=apps</string>
+
<!-- Values for icon shape overrides. These should correspond to entries defined
in icon_shape_override_paths_names -->
<string-array translatable="false" name="icon_shape_override_paths_values">
@@ -26,11 +30,12 @@
<string-array translatable="false" name="icon_shape_override_paths_names">
<!-- Option to not change the icon shape on home screen. [CHAR LIMIT=50] -->
<item>@string/icon_shape_system_default</item>
- <item>Square</item>
- <item>Squircle</item>
- <item>Circle</item>
- <item>Teardrop</item>
+ <item>@string/icon_shape_square</item>
+ <item>@string/icon_shape_squircle</item>
+ <item>@string/icon_shape_circle</item>
+ <item>@string/icon_shape_teardrop</item>
</string-array>
+
<!-- DragController -->
<item type="id" name="drag_event_parity" />
@@ -59,6 +64,9 @@
<!-- The duration of the animation from search hint to text entry -->
<integer name="config_searchHintAnimationDuration">50</integer>
+ <!-- View tag key used to store SpringAnimation data. -->
+ <item type="id" name="spring_animation_tag" />
+
<!-- Workspace -->
<!-- The duration (in ms) of the fade animation on the object outlines, used when
we are dragging objects around on the home screen. -->
@@ -76,6 +84,7 @@
<integer name="config_folderExpandDuration">120</integer>
<integer name="config_materialFolderExpandDuration">200</integer>
<integer name="config_materialFolderExpandStagger">60</integer>
+ <integer name="config_folderDelay">30</integer>
<!-- The distance at which the animation should take the max duration -->
<integer name="config_dropAnimMaxDist">800</integer>
@@ -99,15 +108,15 @@
<!-- Name of a user event dispatcher class. -->
<string name="user_event_dispatcher_class" translatable="false"></string>
+ <!-- Name of a color extraction implementation class. -->
+ <string name="color_extraction_impl_class" translatable="false"></string>
+
<!-- Package name of the default wallpaper picker. -->
<string name="wallpaper_picker_package" translatable="false"></string>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
- <!-- View ID to use for blocked area on the first screen -->
- <item type="id" name="workspace_blocked_row" />
-
<!-- View ID used by cell layout to jail its content -->
<item type="id" name="cell_layout_jail_id" />
@@ -115,11 +124,8 @@
<item type="id" name="preview_image_id" />
<!-- Popup items -->
- <integer name="config_deepShortcutOpenDuration">220</integer>
- <integer name="config_deepShortcutArrowOpenDuration">80</integer>
- <integer name="config_deepShortcutOpenStagger">40</integer>
- <integer name="config_deepShortcutCloseDuration">150</integer>
- <integer name="config_deepShortcutCloseStagger">20</integer>
+ <integer name="config_popupOpenCloseDuration">150</integer>
+ <integer name="config_popupArrowOpenDuration">80</integer>
<integer name="config_removeNotificationViewDuration">300</integer>
<!-- Accessibility actions -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index bd6466b..980b714 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -16,24 +16,32 @@
<resources>
<!-- Dynamic Grid -->
- <dimen name="dynamic_grid_edge_margin">8dp</dimen>
- <dimen name="dynamic_grid_page_indicator_height">28dp</dimen>
+ <dimen name="dynamic_grid_edge_margin">16dp</dimen>
+ <dimen name="dynamic_grid_page_indicator_size">32dp</dimen>
<dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
- <dimen name="dynamic_grid_page_indicator_gutter_width_left_nav_bar">38dp</dimen>
- <dimen name="dynamic_grid_page_indicator_gutter_width_right_nav_bar">48dp</dimen>
+ <dimen name="dynamic_grid_page_indicator_gutter_width">50dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
<dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
<dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
<dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
<dimen name="dynamic_grid_overview_bar_spacer_width">25dp</dimen>
- <dimen name="dynamic_grid_hotseat_height">88dp</dimen>
- <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
- <dimen name="dynamic_grid_hotseat_gutter_width">24dp</dimen>
- <dimen name="dynamic_grid_workspace_top_padding">12dp</dimen>
+ <dimen name="dynamic_grid_workspace_top_padding">8dp</dimen>
<dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
<!-- Minimum space between workspace and hotseat in spring loaded mode -->
<dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
+ <!-- dynamic_grid_edge_margin / 2 -->
+ <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
+
+ <!-- Hotseat -->
+ <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
+ <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
+ <dimen name="dynamic_grid_hotseat_height">80dp</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>
+
<!-- Drop target bar -->
<dimen name="dynamic_grid_drop_target_size">48dp</dimen>
<dimen name="vert_drop_target_vertical_gap">20dp</dimen>
@@ -44,27 +52,31 @@
<dimen name="widget_handle_margin">13dp</dimen>
<dimen name="resize_frame_background_padding">24dp</dimen>
-<!-- Container -->
- <!-- Note: This needs to match the fixed insets for the search box. -->
- <dimen name="container_bounds_inset">8dp</dimen>
- <!-- Notes: container_bounds_inset - quantum_panel_outer_padding -->
- <dimen name="container_bounds_minus_quantum_panel_padding_inset">4dp</dimen>
+<!-- Fast scroll -->
+ <dimen name="fastscroll_track_min_width">6dp</dimen>
+ <dimen name="fastscroll_track_max_width">8dp</dimen>
+ <dimen name="fastscroll_thumb_padding">1dp</dimen>
+ <dimen name="fastscroll_thumb_height">52dp</dimen>
+ <dimen name="fastscroll_thumb_touch_inset">-24dp</dimen>
- <dimen name="container_fastscroll_thumb_min_width">5dp</dimen>
- <dimen name="container_fastscroll_thumb_max_width">9dp</dimen>
- <dimen name="container_fastscroll_popup_margin">18dp</dimen>
- <dimen name="container_fastscroll_thumb_height">72dp</dimen>
- <dimen name="container_fastscroll_thumb_touch_inset">-24dp</dimen>
- <dimen name="container_fastscroll_popup_size">72dp</dimen>
- <dimen name="container_fastscroll_popup_text_size">48dp</dimen>
+ <dimen name="fastscroll_popup_width">75dp</dimen>
+ <dimen name="fastscroll_popup_height">62dp</dimen>
+ <dimen name="fastscroll_popup_padding">13dp</dimen>
+ <dimen name="fastscroll_popup_text_size">32dp</dimen>
+ <dimen name="fastscroll_popup_margin">19dp</dimen>
-<!-- All Apps -->
+ <!--
+ Fast scroller draws the content horizontally centered. The end of the track should be
+ aligned at the end of the container.
+ fastscroll_end_margin = - (fastscroll_width - fastscroll_track_min_width) / 2
+ -->
+ <dimen name="fastscroll_width">58dp</dimen>
+ <dimen name="fastscroll_end_margin">-26dp</dimen>
+
+ <!-- All Apps -->
<dimen name="all_apps_button_scale_down">0dp</dimen>
<dimen name="all_apps_search_bar_field_height">48dp</dimen>
<dimen name="all_apps_search_bar_height">60dp</dimen>
- <dimen name="all_apps_search_bar_icon_margin_right">4dp</dimen>
- <dimen name="all_apps_search_bar_icon_margin_top">1dp</dimen>
- <dimen name="all_apps_list_bottom_padding">8dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
<dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
<dimen name="all_apps_background_canvas_width">700dp</dimen>
@@ -72,7 +84,7 @@
<dimen name="all_apps_caret_stroke_width">2dp</dimen>
<dimen name="all_apps_caret_shadow_spread">1dp</dimen>
<dimen name="all_apps_caret_size">13dp</dimen>
- <dimen name="all_apps_caret_workspace_offset">4dp</dimen>
+ <dimen name="all_apps_caret_workspace_offset">18dp</dimen>
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
@@ -109,6 +121,7 @@
<!-- Drag padding to add to the bottom of drop targets -->
<dimen name="drop_target_drag_padding">14dp</dimen>
<dimen name="drop_target_text_size">14sp</dimen>
+ <dimen name="drop_target_shadow_elevation">2dp</dimen>
<!-- the distance an icon must be dragged before button drop targets accept it -->
<dimen name="drag_distanceThreshold">30dp</dimen>
@@ -123,7 +136,7 @@
<!-- Folders -->
<!-- The size of the padding on the preview background drawable -->
- <dimen name="folder_preview_padding">6dp</dimen>
+ <dimen name="folder_preview_padding">10dp</dimen>
<dimen name="page_indicator_dot_size">8dp</dimen>
<dimen name="folder_cell_x_padding">9dp</dimen>
@@ -152,7 +165,7 @@
<dimen name="deep_shortcuts_elevation">9dp</dimen>
<dimen name="bg_popup_item_width">220dp</dimen>
<dimen name="bg_popup_item_height">56dp</dimen>
- <dimen name="popup_items_spacing">4dp</dimen>
+ <dimen name="bg_popup_item_condensed_height">48dp</dimen>
<dimen name="pre_drag_view_scale">6dp</dimen>
<!-- an icon with shortcuts must be dragged this far before the container is removed. -->
<dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
@@ -196,10 +209,11 @@
<!-- notification_padding_end + (icon_size - footer_icon_size) / 2 -->
<dimen name="notification_footer_icon_row_padding">15dp</dimen>
<dimen name="notification_header_height">32dp</dimen>
- <dimen name="notification_main_height">80dp</dimen>
+ <dimen name="notification_main_height">96dp</dimen>
<dimen name="notification_footer_height">32dp</dimen>
<dimen name="notification_header_text_size">13sp</dimen>
<dimen name="notification_header_count_text_size">12sp</dimen>
+ <dimen name="notification_main_title_size">16sp</dimen>
<dimen name="notification_main_text_size">14sp</dimen>
<dimen name="notification_icon_size">24dp</dimen>
<dimen name="notification_footer_icon_size">18dp</dimen>
diff --git a/res/drawable/bg_white_round_rect.xml b/res/values/drawables.xml
similarity index 65%
copy from res/drawable/bg_white_round_rect.xml
copy to res/values/drawables.xml
index c7f786f..fea17b1 100644
--- a/res/drawable/bg_white_round_rect.xml
+++ b/res/values/drawables.xml
@@ -5,7 +5,7 @@
you may not use this 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,9 +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="#FFFFFF" />
- <corners android:radius="@dimen/bg_round_rect_radius" />
-</shape>
\ No newline at end of file
+<resources>
+ <drawable name="ic_info_shadow">@drawable/ic_info_no_shadow</drawable>
+ <drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
+ <drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ecc537d..da6da04 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -159,7 +159,7 @@
<!-- Text for wallpaper change button -->
<string name="wallpaper_button_text">Wallpapers</string>
<!-- Text for settings button -->
- <string name="settings_button_text">Settings</string>
+ <string name="settings_button_text">Home settings</string>
<!-- Message shown when a feature is disabled by the administrator -->
<string name="msg_disabled_by_admin">Disabled by your admin</string>
<!-- Text for custom accessibility action to go to the overview mode, where users can look and change the overall UI of the launcher. -->
@@ -188,6 +188,15 @@
<string name="icon_shape_override_label">Change icon shape</string>
<!-- Option to not change the icon shape on home screen and use the system default setting instead. [CHAR LIMIT=50] -->
<string name="icon_shape_system_default">Use system default</string>
+ <!-- Option to change the shape of the home screen icons to a square. [CHAR LIMIT=50] -->
+ <string name="icon_shape_square">Square</string>
+ <!-- Option to change the shape of the home screen icons to a squircle. This represents the name of the shape somewhere between a circle and a square. [CHAR LIMIT=50] -->
+ <string name="icon_shape_squircle">Squircle</string>
+ <!-- Option to change the shape of the home screen icons to a circle. [CHAR LIMIT=50] -->
+ <string name="icon_shape_circle">Circle</string>
+ <!-- Option to change the shape of the home screen icons to a teardrop. This represents the name of the shape similar to a circle with with the bottom right corner pushed out like a square [CHAR LIMIT=50] -->
+ <string name="icon_shape_teardrop">Teardrop</string>
+
<!-- Message shown in the progress dialog when the icon shape override is being applied [CHAR LIMIT=100]-->
<string name="icon_shape_override_progress">Applying icon shape changes</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8a46e83..813fe8f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -25,9 +25,60 @@
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:colorEdgeEffect">#FF757575</item>
+ <item name="android:keyboardLayout">@layout/all_apps_search_container</item>
</style>
- <style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style>
+ <style name="BaseLauncherThemeWithCustomAttrs" parent="@style/BaseLauncherTheme">
+ <item name="allAppsScrimColor">#DDFFFFFF</item>
+ <item name="popupColorPrimary">#FFF</item>
+ <item name="popupColorSecondary">#F5F5F5</item> <!-- Gray 100 -->
+ <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+ <item name="isMainColorDark">false</item>
+ <item name="isWorkspaceDarkText">false</item>
+ <item name="workspaceTextColor">@android:color/white</item>
+ <item name="workspaceShadowColor">#B0000000</item>
+ <item name="workspaceAmbientShadowColor">#33000000</item>
+ <item name="workspaceKeyShadowColor">#44000000</item>
+ <item name="workspaceStatusBarScrim">@drawable/workspace_bg</item>
+ <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+ </style>
+
+ <style name="LauncherTheme" parent="@style/BaseLauncherThemeWithCustomAttrs"></style>
+
+ <style name="LauncherThemeDarkText" parent="@style/LauncherTheme">
+ <item name="workspaceTextColor">#FF212121</item>
+ <item name="workspaceShadowColor">@android:color/transparent</item>
+ <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
+ <item name="workspaceKeyShadowColor">@android:color/transparent</item>
+ <item name="isWorkspaceDarkText">true</item>
+ <item name="workspaceStatusBarScrim">@null</item>
+ </style>
+
+ <style name="LauncherThemeDark" parent="@style/LauncherTheme">
+ <item name="android:textColorPrimary">#FFFFFFFF</item>
+ <item name="android:textColorSecondary">#FFFFFFFF</item>
+ <item name="android:textColorTertiary">#CCFFFFFF</item>
+ <item name="android:textColorHint">#A0FFFFFF</item>
+ <item name="android:colorControlHighlight">#A0FFFFFF</item>
+ <item name="android:colorPrimary">#FF333333</item>
+ <item name="allAppsScrimColor">#33000000</item>
+ <item name="popupColorPrimary">?android:attr/colorPrimary</item>
+ <item name="popupColorSecondary">#424242</item> <!-- Gray 800 -->
+ <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+ <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
+ <item name="isMainColorDark">true</item>
+ </style>
+
+ <!--
+ Theme overrides to element on homescreen, i.e., which are drawn on top on wallpaper.
+ Various foreground colors are overridden to be workspaceTextColor so that they are properly
+ visible on various wallpapers.
+ -->
+ <style name="HomeScreenElementTheme">
+ <item name="android:colorEdgeEffect">?attr/workspaceTextColor</item>
+ <item name="android:textColorPrimary">?attr/workspaceTextColor</item>
+ <item name="android:textColorSecondary">?attr/workspaceTextColor</item>
+ </style>
<!-- Theme for the widget container. Overridden on API 26. -->
<style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
@@ -36,12 +87,14 @@
<item name="android:textColorSecondary">?android:attr/textColorSecondaryInverse</item>
</style>
+ <style name="WidgetContainerTheme.Dark" />
+
<style name="FastScrollerPopup" >
- <item name="android:background">@drawable/container_fastscroll_popup_bg</item>
<item name="android:layout_width">wrap_content</item>
- <item name="android:minWidth">@dimen/container_fastscroll_popup_size</item>
- <item name="android:layout_height">@dimen/container_fastscroll_popup_size</item>
- <item name="android:textSize">@dimen/container_fastscroll_popup_text_size</item>
+ <item name="android:minWidth">@dimen/fastscroll_popup_width</item>
+ <item name="android:layout_height">@dimen/fastscroll_popup_height</item>
+ <item name="android:textSize">@dimen/fastscroll_popup_text_size</item>
+ <item name="android:paddingEnd">@dimen/fastscroll_popup_padding</item>
<item name="android:gravity">center</item>
<item name="android:alpha">0</item>
<item name="android:elevation">3dp</item>
@@ -70,15 +123,29 @@
<!-- No shadows in the base theme -->
<item name="android:shadowRadius">0</item>
- <item name="customShadows">false</item>
</style>
<!-- Icon displayed on the worksapce -->
<style name="BaseIcon.Workspace">
- <item name="customShadows">true</item>
- <item name="android:textColor">@color/workspace_icon_text_color</item>
<item name="android:shadowRadius">2.0</item>
- <item name="android:shadowColor">#B0000000</item>
+ <item name="android:shadowColor">?attr/workspaceShadowColor</item>
+ <item name="ambientShadowColor">?attr/workspaceAmbientShadowColor</item>
+ <item name="ambientShadowBlur">2.5dp</item>
+ <item name="keyShadowColor">?attr/workspaceKeyShadowColor</item>
+ <item name="keyShadowBlur">1dp</item>
+ <item name="keyShadowOffset">.5dp</item>
+ </style>
+
+ <!-- Theme for the popup container -->
+ <style name="PopupItem">
+ <item name="android:colorControlHighlight">?attr/popupColorTertiary</item>
+ </style>
+ <style name="PopupGutter">
+ <item name="android:backgroundTintMode">multiply</item>
+ <item name="android:backgroundTint">?attr/popupColorSecondary</item>
+ <item name="android:background">@drawable/gutter_horizontal</item>
+ <item name="android:elevation">@dimen/notification_elevation</item>
+ <item name="android:outlineProvider">none</item>
</style>
<!-- Drop targets -->
@@ -86,15 +153,30 @@
<item name="android:drawablePadding">7.5dp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">16dp</item>
- <item name="android:textColor">@color/workspace_icon_text_color</item>
+ <item name="android:textColor">?attr/workspaceTextColor</item>
<item name="android:textSize">@dimen/drop_target_text_size</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">end</item>
- <item name="android:shadowColor">@color/default_shadow_color_no_alpha</item>
+ <item name="android:shadowColor">?attr/workspaceShadowColor</item>
<item name="android:shadowDx">0.0</item>
<item name="android:shadowDy">1.0</item>
<item name="android:shadowRadius">4.0</item>
</style>
<style name="DropTargetButton" parent="DropTargetButtonBase" />
+
+ <style name="TextTitle">
+ <item name="android:fontFamily">sans-serif</item>
+ </style>
+
+ <style name="AllAppsEmptySearchBackground">
+ <item name="android:colorPrimary">#E0E0E0</item>
+ <item name="android:colorControlHighlight">#BDBDBD</item>
+ <item name="android:colorForeground">@color/all_apps_bg_hand_fill</item>
+ </style>
+ <style name="AllAppsEmptySearchBackground.Dark">
+ <item name="android:colorPrimary">#9AA0A6</item>
+ <item name="android:colorControlHighlight">#DFE1E5</item>
+ <item name="android:colorForeground">@color/all_apps_bg_hand_fill_dark</item>
+ </style>
</resources>
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 7e833a0..299e92e 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -3,5 +3,6 @@
<include domain="database" path="launcher.db" />
<include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
+ <include domain="file" path="downgrade_schema.json" />
</full-backup-content>
\ No newline at end of file
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index aeda1a2..c582fc5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -29,7 +29,6 @@
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="3"
- launcher:hotseatIconSize="48"
launcher:defaultLayoutId="@xml/default_workspace_3x3"
/>
@@ -45,7 +44,6 @@
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="3"
- launcher:hotseatIconSize="48"
launcher:defaultLayoutId="@xml/default_workspace_3x3"
/>
@@ -61,7 +59,6 @@
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
- launcher:hotseatIconSize="48"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
/>
@@ -77,7 +74,6 @@
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
- launcher:hotseatIconSize="48"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
/>
@@ -93,7 +89,6 @@
launcher:iconSize="48"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
- launcher:hotseatIconSize="48"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
/>
@@ -106,10 +101,9 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:minAllAppsPredictionColumns="4"
- launcher:iconSize="60"
+ launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
- launcher:hotseatIconSize="56"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
/>
@@ -122,10 +116,9 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:minAllAppsPredictionColumns="4"
- launcher:iconSize="60"
+ launcher:iconSize="54"
launcher:iconTextSize="13.0"
launcher:numHotseatIcons="5"
- launcher:hotseatIconSize="56"
launcher:defaultLayoutId="@xml/default_workspace_4x4"
/>
@@ -138,10 +131,9 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="4"
launcher:minAllAppsPredictionColumns="4"
- launcher:iconSize="64"
+ launcher:iconSize="56"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="5"
- launcher:hotseatIconSize="56"
launcher:defaultLayoutId="@xml/default_workspace_5x5"
/>
@@ -154,10 +146,9 @@
launcher:numFolderRows="4"
launcher:numFolderColumns="5"
launcher:minAllAppsPredictionColumns="4"
- launcher:iconSize="72"
+ launcher:iconSize="64"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="7"
- launcher:hotseatIconSize="60"
launcher:defaultLayoutId="@xml/default_workspace_5x6"
/>
@@ -173,7 +164,6 @@
launcher:iconSize="76"
launcher:iconTextSize="14.4"
launcher:numHotseatIcons="7"
- launcher:hotseatIconSize="76"
launcher:defaultLayoutId="@xml/default_workspace_5x6"
/>
@@ -189,7 +179,6 @@
launcher:iconSize="100"
launcher:iconTextSize="20.0"
launcher:numHotseatIcons="7"
- launcher:hotseatIconSize="72"
launcher:defaultLayoutId="@xml/default_workspace_5x6"
/>
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 8763883..c76f118 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -16,6 +16,18 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <Preference
+ android:key="pref_icon_badging"
+ android:title="@string/icon_badging_title"
+ android:persistent="false">
+ <intent android:action="android.settings.NOTIFICATION_SETTINGS">
+ <!-- This extra highlights the "Allow icon badges" field in Notification settings -->
+ <extra
+ android:name=":settings:fragment_args_key"
+ android:value="notification_badging" />
+ </intent>
+ </Preference>
+
<SwitchPreference
android:key="pref_add_icon_to_home"
android:title="@string/auto_add_shortcuts_label"
@@ -24,6 +36,13 @@
android:persistent="true"
/>
+ <SwitchPreference
+ android:key="pref_allowRotation"
+ android:title="@string/allow_rotation_title"
+ android:defaultValue="@bool/allow_rotation"
+ android:persistent="true"
+ />
+
<ListPreference
android:key="pref_override_icon_shape"
android:title="@string/icon_shape_override_label"
@@ -33,23 +52,4 @@
android:defaultValue=""
android:persistent="false" />
- <Preference
- android:key="pref_icon_badging"
- android:title="@string/icon_badging_title"
- android:persistent="false">
- <intent android:action="android.settings.NOTIFICATION_SETTINGS">
- <!-- This extra highlights the "Allow icon badges" field in Notification settings -->
- <extra
- android:name=":settings:fragment_args_key"
- android:value="notification_badging" />
- </intent>
- </Preference>/>
-
- <SwitchPreference
- android:key="pref_allowRotation"
- android:title="@string/allow_rotation_title"
- android:defaultValue="@bool/allow_rotation"
- android:persistent="true"
- />
-
</PreferenceScreen>
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 5b42cad..8ac8570 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -18,10 +18,16 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
+import android.os.Process;
import android.os.UserHandle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -34,18 +40,18 @@
* Stores the list of all applications for the all apps view.
*/
public class AllAppsList {
+ private static final String TAG = "AllAppsList";
+
public static final int DEFAULT_APPLICATIONS_NUMBER = 42;
/** The list off all apps. */
- public ArrayList<AppInfo> data =
- new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
+ public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
/** The list of apps that have been added since the last notify() call. */
- public ArrayList<AppInfo> added =
- new ArrayList<AppInfo>(DEFAULT_APPLICATIONS_NUMBER);
+ public ArrayList<AppInfo> added = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
/** The list of apps that have been removed since the last notify() call. */
- public ArrayList<AppInfo> removed = new ArrayList<AppInfo>();
+ public ArrayList<AppInfo> removed = new ArrayList<>();
/** The list of apps that have been modified since the last notify() call. */
- public ArrayList<AppInfo> modified = new ArrayList<AppInfo>();
+ public ArrayList<AppInfo> modified = new ArrayList<>();
private IconCache mIconCache;
@@ -69,7 +75,7 @@
if (!mAppFilter.shouldShowApp(info.componentName)) {
return;
}
- if (findActivity(data, info.componentName, info.user)) {
+ if (findAppInfo(info.componentName, info.user) != null) {
return;
}
mIconCache.getTitleAndIcon(info, activityInfo, true /* useLowResIcon */);
@@ -78,6 +84,25 @@
added.add(info);
}
+ public void addPromiseApp(Context context,
+ PackageInstallerCompat.PackageInstallInfo installInfo) {
+ ApplicationInfo applicationInfo = LauncherAppsCompat.getInstance(context)
+ .getApplicationInfo(installInfo.packageName, 0, Process.myUserHandle());
+ // only if not yet installed
+ if (applicationInfo == null) {
+ PromiseAppInfo info = new PromiseAppInfo(installInfo);
+ mIconCache.getTitleAndIcon(info, info.usingLowResIcon);
+ data.add(info);
+ added.add(info);
+ }
+ }
+
+ public void removePromiseApp(AppInfo appInfo) {
+ // the <em>removed</em> list is handled by the caller
+ // so not adding it here
+ data.remove(appInfo);
+ }
+
public void clear() {
data.clear();
// TODO: do we clear these too?
@@ -160,6 +185,7 @@
if (user.equals(applicationInfo.user)
&& packageName.equals(applicationInfo.componentName.getPackageName())) {
if (!findActivity(matches, applicationInfo.componentName)) {
+ Log.w(TAG, "Shortcut will be removed due to app component name change.");
removed.add(applicationInfo);
data.remove(i);
}
@@ -169,9 +195,7 @@
// Find enabled activities and add them to the adapter
// Also updates existing activities with new labels/icons
for (final LauncherActivityInfo info : matches) {
- AppInfo applicationInfo = findApplicationInfoLocked(
- info.getComponentName().getPackageName(), user,
- info.getComponentName().getClassName());
+ AppInfo applicationInfo = findAppInfo(info.getComponentName(), user);
if (applicationInfo == null) {
add(new AppInfo(context, info, user), info);
} else {
@@ -208,28 +232,14 @@
}
/**
- * Returns whether <em>apps</em> contains <em>component</em>.
+ * Find an AppInfo object for the given componentName
+ *
+ * @return the corresponding AppInfo or null
*/
- private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
- UserHandle user) {
- final int N = apps.size();
- for (int i = 0; i < N; i++) {
- final AppInfo info = apps.get(i);
- if (info.user.equals(user) && info.componentName.equals(component)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Find an ApplicationInfo object for the given packageName and className.
- */
- private AppInfo findApplicationInfoLocked(String packageName, UserHandle user,
- String className) {
+ private @Nullable AppInfo findAppInfo(@NonNull ComponentName componentName,
+ @NonNull UserHandle user) {
for (AppInfo info: data) {
- if (user.equals(info.user) && packageName.equals(info.componentName.getPackageName())
- && className.equals(info.componentName.getClassName())) {
+ if (componentName.equals(info.componentName) && user.equals(info.user)) {
return info;
}
}
diff --git a/src/com/android/launcher3/AppFilter.java b/src/com/android/launcher3/AppFilter.java
index db8f5dd..923835a 100644
--- a/src/com/android/launcher3/AppFilter.java
+++ b/src/com/android/launcher3/AppFilter.java
@@ -1,9 +1,14 @@
package com.android.launcher3;
import android.content.ComponentName;
+import android.content.Context;
public class AppFilter {
+ public static AppFilter newInstance(Context context) {
+ return Utilities.getOverrideObject(AppFilter.class, context, R.string.app_filter_class);
+ }
+
public boolean shouldShowApp(ComponentName app) {
return true;
}
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 84a8bce..70be7da 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -12,6 +12,7 @@
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.util.ContentWriter;
public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
@@ -52,7 +53,7 @@
final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
final int state;
- if (LauncherModel.isValidProvider(provider)) {
+ if (LoaderTask.isValidProvider(provider)) {
// This will ensure that we show 'Click to setup' UI if required.
state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
} else {
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index c4086a8..d82579b 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -30,23 +30,20 @@
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.Patterns;
-
import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Locale;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
/**
* Layout parsing code for auto installs layout
@@ -83,7 +80,7 @@
// Try with grid size and hotseat count
String layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES_WITH_HOSTEAT,
- (int) grid.numColumns, (int) grid.numRows, (int) grid.numHotseatIcons);
+ grid.numColumns, grid.numRows, grid.numHotseatIcons);
int layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
// Try with only grid size
@@ -91,7 +88,7 @@
Log.d(TAG, "Formatted layout: " + layoutName
+ " not found. Trying layout without hosteat");
layoutName = String.format(Locale.ENGLISH, FORMATTED_LAYOUT_RES,
- (int) grid.numColumns, (int) grid.numRows);
+ grid.numColumns, grid.numRows);
layoutId = targetRes.getIdentifier(layoutName, "xml", pkg);
}
@@ -209,7 +206,7 @@
beginDocument(parser, mRootTag);
final int depth = parser.getDepth();
int type;
- HashMap<String, TagParser> tagParserMap = getLayoutElementsMap();
+ ArrayMap<String, TagParser> tagParserMap = getLayoutElementsMap();
int count = 0;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
@@ -243,10 +240,10 @@
* Parses the current node and returns the number of elements added.
*/
protected int parseAndAddNode(
- XmlResourceParser parser,
- HashMap<String, TagParser> tagParserMap,
- ArrayList<Long> screenIds)
- throws XmlPullParserException, IOException {
+ XmlResourceParser parser,
+ ArrayMap<String, TagParser> tagParserMap,
+ ArrayList<Long> screenIds)
+ throws XmlPullParserException, IOException {
if (TAG_INCLUDE.equals(parser.getName())) {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
@@ -303,16 +300,16 @@
}
}
- protected HashMap<String, TagParser> getFolderElementsMap() {
- HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ protected ArrayMap<String, TagParser> getFolderElementsMap() {
+ ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_APP_ICON, new AppShortcutParser());
parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
return parsers;
}
- protected HashMap<String, TagParser> getLayoutElementsMap() {
- HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ protected ArrayMap<String, TagParser> getLayoutElementsMap() {
+ ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_APP_ICON, new AppShortcutParser());
parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
parsers.put(TAG_FOLDER, new FolderParser());
@@ -393,7 +390,7 @@
return -1;
}
- mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINTALL_ICON);
+ mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINSTALL_ICON);
final Intent intent = new Intent(Intent.ACTION_MAIN, null)
.addCategory(Intent.CATEGORY_LAUNCHER)
.setComponent(new ComponentName(packageName, className))
@@ -528,13 +525,13 @@
}
protected class FolderParser implements TagParser {
- private final HashMap<String, TagParser> mFolderElements;
+ private final ArrayMap<String, TagParser> mFolderElements;
public FolderParser() {
this(getFolderElementsMap());
}
- public FolderParser(HashMap<String, TagParser> elements) {
+ public FolderParser(ArrayMap<String, TagParser> elements) {
mFolderElements = elements;
}
@@ -561,7 +558,7 @@
}
final ContentValues myValues = new ContentValues(mValues);
- ArrayList<Long> folderItems = new ArrayList<Long>();
+ ArrayList<Long> folderItems = new ArrayList<>();
int type;
int folderDepth = parser.getDepth();
@@ -617,7 +614,7 @@
}
}
- protected static final void beginDocument(XmlPullParser parser, String firstElementName)
+ protected static void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
@@ -671,7 +668,7 @@
return value;
}
- public static interface LayoutParserCallback {
+ public interface LayoutParserCallback {
long generateNewItemId();
long insertAndCheck(SQLiteDatabase db, ContentValues values);
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 08cd955..2b59ede 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -22,11 +22,13 @@
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.util.SystemUiController;
public abstract class BaseActivity extends Activity {
protected DeviceProfile mDeviceProfile;
protected UserEventDispatcher mUserEventDispatcher;
+ protected SystemUiController mSystemUiController;
public DeviceProfile getDeviceProfile() {
return mDeviceProfile;
@@ -54,4 +56,11 @@
}
return ((BaseActivity) ((ContextWrapper) context).getBaseContext());
}
+
+ public SystemUiController getSystemUiController() {
+ if (mSystemUiController == null) {
+ mSystemUiController = new SystemUiController(getWindow());
+ }
+ return mSystemUiController;
+ }
}
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index ac7cbaf..c55a586 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -113,20 +113,18 @@
* Calculate the background padding as it can change due to insets/content padding change.
*/
private void updatePaddings() {
- Context context = getContext();
- int paddingLeft;
- int paddingRight;
- int paddingTop;
- int paddingBottom;
-
- DeviceProfile grid = Launcher.getLauncher(context).getDeviceProfile();
+ DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
int[] padding = grid.getContainerPadding();
- paddingLeft = padding[0] + grid.edgeMarginPx;
- paddingRight = padding[1] + grid.edgeMarginPx;
+
+ int paddingLeft = padding[0];
+ int paddingRight = padding[1];
+ int paddingTop = 0;
+ int paddingBottom = 0;
+
if (!grid.isVerticalBarLayout()) {
+ paddingLeft += grid.edgeMarginPx;
+ paddingRight += grid.edgeMarginPx;
paddingTop = paddingBottom = grid.edgeMarginPx;
- } else {
- paddingTop = paddingBottom = 0;
}
updateBackground(paddingLeft, paddingTop, paddingRight, paddingBottom);
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 6fdf454..3ee6e51 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -22,8 +22,9 @@
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;
+import android.widget.TextView;
-import com.android.launcher3.util.Thunk;
+import com.android.launcher3.views.RecyclerViewFastScroller;
/**
@@ -36,17 +37,7 @@
public abstract class BaseRecyclerView extends RecyclerView
implements RecyclerView.OnItemTouchListener {
- private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
-
- /** Keeps the last known scrolling delta/velocity along y-axis. */
- @Thunk int mDy = 0;
- private float mDeltaThreshold;
-
- protected final BaseRecyclerViewFastScrollBar mScrollbar;
-
- private int mDownX;
- private int mDownY;
- private int mLastY;
+ protected RecyclerViewFastScroller mScrollbar;
public BaseRecyclerView(Context context) {
this(context, null);
@@ -58,32 +49,6 @@
public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
- mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
-
- ScrollListener listener = new ScrollListener();
- setOnScrollListener(listener);
- }
-
- private class ScrollListener extends OnScrollListener {
- public ScrollListener() {
- // Do nothing
- }
-
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mDy = dy;
-
- // TODO(winsonc): If we want to animate the section heads while scrolling, we can
- // initiate that here if the recycler view scroll state is not
- // RecyclerView.SCROLL_STATE_IDLE.
-
- onUpdateScrollbar(dy);
- }
- }
-
- public void reset() {
- mScrollbar.reattachThumbToScroll();
}
@Override
@@ -95,7 +60,9 @@
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- mScrollbar.setPopupView(((ViewGroup) getParent()).findViewById(R.id.fast_scroller_popup));
+ ViewGroup parent = (ViewGroup) getParent();
+ mScrollbar = parent.findViewById(R.id.fast_scroller);
+ mScrollbar.setRecyclerView(this, (TextView) parent.findViewById(R.id.fast_scroller_popup));
}
/**
@@ -117,30 +84,15 @@
* it is already showing).
*/
private boolean handleTouchEvent(MotionEvent ev) {
- int action = ev.getAction();
- int x = (int) ev.getX();
- int y = (int) ev.getY();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- // Keep track of the down positions
- mDownX = x;
- mDownY = mLastY = y;
- if (shouldStopScroll(ev)) {
- stopScroll();
- }
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
- break;
- case MotionEvent.ACTION_MOVE:
- mLastY = y;
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- onFastScrollCompleted();
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
- break;
+ // Move to mScrollbar's coordinate system.
+ int left = getLeft() - mScrollbar.getLeft();
+ int top = getTop() - mScrollbar.getTop();
+ ev.offsetLocation(left, top);
+ try {
+ return mScrollbar.handleTouchEvent(ev);
+ } finally {
+ ev.offsetLocation(-left, -top);
}
- return mScrollbar.isDraggingThumb();
}
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -148,25 +100,10 @@
}
/**
- * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
- */
- protected boolean shouldStopScroll(MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- if ((Math.abs(mDy) < mDeltaThreshold &&
- getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
- // now the touch events are being passed to the {@link WidgetCell} until the
- // touch sequence goes over the touch slop.
- return true;
- }
- }
- return false;
- }
-
- /**
* Returns the height of the fast scroll bar
*/
- protected int getScrollbarTrackHeight() {
- return getHeight();
+ public int getScrollbarTrackHeight() {
+ return getHeight() - getPaddingTop() - getPaddingBottom();
}
/**
@@ -185,24 +122,16 @@
}
/**
- * Returns the track color (ignoring alpha), can be overridden by each subclass.
- */
- public int getFastScrollerTrackColor(int defaultTrackColor) {
- return defaultTrackColor;
- }
-
- /**
* Returns the scrollbar for this recycler view.
*/
- public BaseRecyclerViewFastScrollBar getScrollBar() {
+ public RecyclerViewFastScroller getScrollBar() {
return mScrollbar;
}
@Override
protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
onUpdateScrollbar(0);
- mScrollbar.draw(canvas);
+ super.dispatchDraw(canvas);
}
/**
@@ -233,7 +162,7 @@
/**
* @return whether fast scrolling is supported in the current state.
*/
- protected boolean supportsFastScrolling() {
+ public boolean supportsFastScrolling() {
return true;
}
@@ -249,16 +178,16 @@
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
- protected abstract String scrollToPositionAtProgress(float touchFraction);
+ public abstract String scrollToPositionAtProgress(float touchFraction);
/**
* Updates the bounds for the scrollbar.
* <p>Override in each subclass of this base class.
*/
- protected abstract void onUpdateScrollbar(int dy);
+ public abstract void onUpdateScrollbar(int dy);
/**
* <p>Override in each subclass of this base class.
*/
- protected void onFastScrollCompleted() {}
+ public void onFastScrollCompleted() {}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
deleted file mode 100644
index 5feb42e..0000000
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import android.animation.ObjectAnimator;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.Rect;
-import android.util.Property;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.widget.TextView;
-
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Themes;
-
-/**
- * The track and scrollbar that shows when you scroll the list.
- */
-public class BaseRecyclerViewFastScrollBar {
-
- private static final Property<BaseRecyclerViewFastScrollBar, Integer> TRACK_WIDTH =
- new Property<BaseRecyclerViewFastScrollBar, Integer>(Integer.class, "width") {
-
- @Override
- public Integer get(BaseRecyclerViewFastScrollBar scrollBar) {
- return scrollBar.mWidth;
- }
-
- @Override
- public void set(BaseRecyclerViewFastScrollBar scrollBar, Integer value) {
- scrollBar.setTrackWidth(value);
- }
- };
-
- private final static int MAX_TRACK_ALPHA = 30;
- private final static int SCROLL_BAR_VIS_DURATION = 150;
- private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f;
-
- private final Rect mTmpRect = new Rect();
- private final BaseRecyclerView mRv;
-
- private final boolean mIsRtl;
-
- // The inset is the buffer around which a point will still register as a click on the scrollbar
- private final int mTouchInset;
-
- private final int mMinWidth;
- private final int mMaxWidth;
-
- // Current width of the track
- private int mWidth;
- private ObjectAnimator mWidthAnimator;
-
- private final Path mThumbPath = new Path();
- private final Paint mThumbPaint;
- private final int mThumbHeight;
-
- private final Paint mTrackPaint;
-
- private float mLastTouchY;
- private boolean mIsDragging;
- private boolean mIsThumbDetached;
- private boolean mCanThumbDetach;
- private boolean mIgnoreDragGesture;
-
- // This is the offset from the top of the scrollbar when the user first starts touching. To
- // prevent jumping, this offset is applied as the user scrolls.
- private int mTouchOffsetY;
- private int mThumbOffsetY;
-
- // Fast scroller popup
- private TextView mPopupView;
- private boolean mPopupVisible;
- private String mPopupSectionName;
-
- public BaseRecyclerViewFastScrollBar(BaseRecyclerView rv, Resources res) {
- mRv = rv;
- mTrackPaint = new Paint();
- mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
- mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
-
- mThumbPaint = new Paint();
- mThumbPaint.setAntiAlias(true);
- mThumbPaint.setColor(Themes.getColorAccent(rv.getContext()));
- mThumbPaint.setStyle(Paint.Style.FILL);
-
- mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width);
- mMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width);
- mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height);
- mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset);
- mIsRtl = Utilities.isRtl(res);
- updateThumbPath();
- }
-
- public void setPopupView(View popup) {
- mPopupView = (TextView) popup;
- }
-
- public void setDetachThumbOnFastScroll() {
- mCanThumbDetach = true;
- }
-
- public void reattachThumbToScroll() {
- mIsThumbDetached = false;
- }
-
- private int getDrawLeft() {
- return mIsRtl ? 0 : (mRv.getWidth() - mMaxWidth);
- }
-
- public void setThumbOffsetY(int y) {
- if (mThumbOffsetY == y) {
- return;
- }
-
- // Invalidate the previous and new thumb area
- int drawLeft = getDrawLeft();
- mTmpRect.set(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
- mThumbOffsetY = y;
- mTmpRect.union(drawLeft, mThumbOffsetY, drawLeft + mMaxWidth, mThumbOffsetY + mThumbHeight);
- mRv.invalidate(mTmpRect);
- }
-
- public int getThumbOffsetY() {
- return mThumbOffsetY;
- }
-
- private void setTrackWidth(int width) {
- if (mWidth == width) {
- return;
- }
- int left = getDrawLeft();
- // Invalidate the whole scroll bar area.
- mRv.invalidate(left, 0, left + mMaxWidth, mRv.getScrollbarTrackHeight());
-
- mWidth = width;
- updateThumbPath();
- }
-
- /**
- * Updates the path for the thumb drawable.
- */
- private void updateThumbPath() {
- int smallWidth = mIsRtl ? mWidth : -mWidth;
- int largeWidth = mIsRtl ? mMaxWidth : -mMaxWidth;
-
- mThumbPath.reset();
- mThumbPath.moveTo(0, 0);
- mThumbPath.lineTo(0, mThumbHeight); // Left edge
- mThumbPath.lineTo(smallWidth, mThumbHeight); // bottom edge
- mThumbPath.cubicTo(smallWidth, mThumbHeight, // right edge
- largeWidth, mThumbHeight / 2,
- smallWidth, 0);
- mThumbPath.close();
- }
-
- public int getThumbHeight() {
- return mThumbHeight;
- }
-
- public boolean isDraggingThumb() {
- return mIsDragging;
- }
-
- public boolean isThumbDetached() {
- return mIsThumbDetached;
- }
-
- /**
- * Handles the touch event and determines whether to show the fast scroller (or updates it if
- * it is already showing).
- */
- public void handleTouchEvent(MotionEvent ev, int downX, int downY, int lastY) {
- ViewConfiguration config = ViewConfiguration.get(mRv.getContext());
-
- int action = ev.getAction();
- int y = (int) ev.getY();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- if (isNearThumb(downX, downY)) {
- mTouchOffsetY = downY - mThumbOffsetY;
- } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
- && mRv.supportsFastScrolling()
- && isNearScrollBar(downX)) {
- calcTouchOffsetAndPrepToFastScroll(downY, lastY);
- updateFastScrollSectionNameAndThumbOffset(lastY, y);
- }
- break;
- case MotionEvent.ACTION_MOVE:
- // Check if we should start scrolling, but ignore this fastscroll gesture if we have
- // exceeded some fixed movement
- mIgnoreDragGesture |= Math.abs(y - downY) > config.getScaledPagingTouchSlop();
- if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
- isNearThumb(downX, lastY) &&
- Math.abs(y - downY) > config.getScaledTouchSlop()) {
- calcTouchOffsetAndPrepToFastScroll(downY, lastY);
- }
- if (mIsDragging) {
- updateFastScrollSectionNameAndThumbOffset(lastY, y);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- mTouchOffsetY = 0;
- mLastTouchY = 0;
- mIgnoreDragGesture = false;
- if (mIsDragging) {
- mIsDragging = false;
- animatePopupVisibility(false);
- showActiveScrollbar(false);
- }
- break;
- }
- }
-
- private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
- mRv.getParent().requestDisallowInterceptTouchEvent(true);
- mIsDragging = true;
- if (mCanThumbDetach) {
- mIsThumbDetached = true;
- }
- mTouchOffsetY += (lastY - downY);
- animatePopupVisibility(true);
- showActiveScrollbar(true);
- }
-
- private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
- // Update the fastscroller section name at this touch position
- int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
- float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
- String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
- if (!sectionName.equals(mPopupSectionName)) {
- mPopupSectionName = sectionName;
- mPopupView.setText(sectionName);
- }
- animatePopupVisibility(!sectionName.isEmpty());
- updatePopupY(lastY);
- mLastTouchY = boundedY;
- setThumbOffsetY((int) mLastTouchY);
- }
-
- public void draw(Canvas canvas) {
- if (mThumbOffsetY < 0) {
- return;
- }
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
- if (!mIsRtl) {
- canvas.translate(mRv.getWidth(), 0);
- }
- // Draw the track
- int thumbWidth = mIsRtl ? mWidth : -mWidth;
- canvas.drawRect(0, 0, thumbWidth, mRv.getScrollbarTrackHeight(), mTrackPaint);
-
- canvas.translate(0, mThumbOffsetY);
- canvas.drawPath(mThumbPath, mThumbPaint);
- canvas.restoreToCount(saveCount);
- }
-
- /**
- * Animates the width of the scrollbar.
- */
- private void showActiveScrollbar(boolean isScrolling) {
- if (mWidthAnimator != null) {
- mWidthAnimator.cancel();
- }
-
- mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
- isScrolling ? mMaxWidth : mMinWidth);
- mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
- mWidthAnimator.start();
- }
-
- /**
- * Returns whether the specified point is inside the thumb bounds.
- */
- public boolean isNearThumb(int x, int y) {
- int left = getDrawLeft();
- mTmpRect.set(left, mThumbOffsetY, left + mMaxWidth, mThumbOffsetY + mThumbHeight);
- mTmpRect.inset(mTouchInset, mTouchInset);
- return mTmpRect.contains(x, y);
- }
-
- /**
- * Returns whether the specified x position is near the scroll bar.
- */
- public boolean isNearScrollBar(int x) {
- int left = getDrawLeft();
- return x >= left && x <= left + mMaxWidth;
- }
-
- private void animatePopupVisibility(boolean visible) {
- if (mPopupVisible != visible) {
- mPopupVisible = visible;
- mPopupView.animate().cancel();
- mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
- }
- }
-
- private void updatePopupY(int lastTouchY) {
- int height = mPopupView.getHeight();
- float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height);
- top = Math.max(mMaxWidth, Math.min(top, mRv.getScrollbarTrackHeight() - mMaxWidth - height));
- mPopupView.setTranslationY(top);
- }
-}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3c3e224..aeb82b3 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -19,15 +19,16 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Resources;
import android.content.res.TypedArray;
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.Region;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.util.Property;
import android.util.TypedValue;
@@ -44,6 +45,7 @@
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.badge.BadgeRenderer;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.graphics.IconPalette;
@@ -59,13 +61,6 @@
*/
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver {
- // Dimensions in DP
- private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
- private static final float KEY_SHADOW_RADIUS = 1f;
- private static final float KEY_SHADOW_OFFSET = 0.5f;
- private static final int AMBIENT_SHADOW_COLOR = 0x33000000;
- private static final int KEY_SHADOW_COLOR = 0x66000000;
-
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
private static final int DISPLAY_FOLDER = 2;
@@ -75,24 +70,20 @@
private final Launcher mLauncher;
private Drawable mIcon;
private final boolean mCenterVertically;
- private final Drawable mBackground;
- private OnLongClickListener mOnLongClickListener;
+
private final CheckLongPressHelper mLongPressHelper;
private final HolographicOutlineHelper mOutlineHelper;
private final StylusEventHelper mStylusEventHelper;
-
- private boolean mBackgroundSizeChanged;
+ private final float mSlop;
private Bitmap mPressedBackground;
- private float mSlop;
-
private final boolean mDeferShadowGenerationOnTouch;
- private final boolean mCustomShadowsEnabled;
private final boolean mLayoutHorizontal;
private final int mIconSize;
@ViewDebug.ExportedProperty(category = "launcher")
private int mTextColor;
+ private boolean mIsIconVisible = true;
private BadgeInfo mBadgeInfo;
private BadgeRenderer mBadgeRenderer;
@@ -116,6 +107,19 @@
}
};
+ public static final Property<BubbleTextView, Integer> TEXT_ALPHA_PROPERTY
+ = new Property<BubbleTextView, Integer>(Integer.class, "textAlpha") {
+ @Override
+ public Integer get(BubbleTextView bubbleTextView) {
+ return bubbleTextView.getTextAlpha();
+ }
+
+ @Override
+ public void set(BubbleTextView bubbleTextView, Integer alpha) {
+ bubbleTextView.setTextAlpha(alpha);
+ }
+ };
+
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mStayPressed;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -137,10 +141,10 @@
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(context);
DeviceProfile grid = mLauncher.getDeviceProfile();
+ mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
- mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, false);
mLayoutHorizontal = a.getBoolean(R.styleable.BubbleTextView_layoutHorizontal, false);
mDeferShadowGenerationOnTouch =
a.getBoolean(R.styleable.BubbleTextView_deferShadowGeneration, false);
@@ -149,6 +153,7 @@
int defaultIconSize = grid.iconSizePx;
if (display == DISPLAY_WORKSPACE) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
+ setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
} else if (display == DISPLAY_ALL_APPS) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
@@ -164,23 +169,12 @@
defaultIconSize);
a.recycle();
- if (mCustomShadowsEnabled) {
- // Draw the background itself as the parent is drawn twice.
- mBackground = getBackground();
- setBackground(null);
-
- // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
- float density = getResources().getDisplayMetrics().density;
- setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
- } else {
- mBackground = null;
- }
-
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mOutlineHelper = HolographicOutlineHelper.getInstance(getContext());
setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
+
}
public void applyFromShortcutInfo(ShortcutInfo info) {
@@ -206,6 +200,10 @@
// Verify high res immediately
verifyHighRes();
+ if (info instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+ applyProgressLevel(promiseAppInfo.level);
+ }
applyBadgeState(info, false /* animate */);
}
@@ -238,19 +236,6 @@
}
@Override
- protected boolean setFrame(int left, int top, int right, int bottom) {
- if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
- mBackgroundSizeChanged = true;
- }
- return super.setFrame(left, top, right, bottom);
- }
-
- @Override
- protected boolean verifyDrawable(Drawable who) {
- return who == mBackground || super.verifyDrawable(who);
- }
-
- @Override
public void setTag(Object tag) {
if (tag != null) {
LauncherModel.checkItemInfo((ItemInfo) tag);
@@ -279,21 +264,6 @@
return mIcon;
}
- /** Returns whether the layout is horizontal. */
- public boolean isLayoutHorizontal() {
- return mLayoutHorizontal;
- }
-
- @Override
- public void setOnLongClickListener(OnLongClickListener l) {
- super.setOnLongClickListener(l);
- mOnLongClickListener = l;
- }
-
- public OnLongClickListener getOnLongClickListener() {
- return mOnLongClickListener;
- }
-
@Override
public boolean onTouchEvent(MotionEvent event) {
// Call the superclass onTouchEvent first, because sometimes it changes the state to
@@ -391,54 +361,14 @@
return result;
}
+ @SuppressWarnings("wrongcall")
+ protected void drawWithoutBadge(Canvas canvas) {
+ super.onDraw(canvas);
+ }
+
@Override
- public void draw(Canvas canvas) {
- if (!mCustomShadowsEnabled) {
- super.draw(canvas);
- drawBadgeIfNecessary(canvas);
- return;
- }
-
- final Drawable background = mBackground;
- if (background != null) {
- final int scrollX = getScrollX();
- final int scrollY = getScrollY();
-
- if (mBackgroundSizeChanged) {
- background.setBounds(0, 0, getRight() - getLeft(), getBottom() - getTop());
- mBackgroundSizeChanged = false;
- }
-
- if ((scrollX | scrollY) == 0) {
- background.draw(canvas);
- } else {
- canvas.translate(scrollX, scrollY);
- background.draw(canvas);
- canvas.translate(-scrollX, -scrollY);
- }
- }
-
- // If text is transparent, don't draw any shadow
- if ((getCurrentTextColor() >> 24) == 0) {
- getPaint().clearShadowLayer();
- super.draw(canvas);
- drawBadgeIfNecessary(canvas);
- return;
- }
-
- // We enhance the shadow by drawing the shadow twice
- float density = getResources().getDisplayMetrics().density;
- getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
- super.draw(canvas);
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
- getScrollX() + getWidth(),
- getScrollY() + getHeight(), Region.Op.INTERSECT);
- getPaint().setShadowLayer(
- density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
- super.draw(canvas);
- canvas.restore();
-
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
drawBadgeIfNecessary(canvas);
}
@@ -446,7 +376,7 @@
* Draws the icon badge in the top right corner of the icon bounds.
* @param canvas The canvas to draw to.
*/
- private void drawBadgeIfNecessary(Canvas canvas) {
+ protected void drawBadgeIfNecessary(Canvas canvas) {
if (!mForceHideBadge && (hasBadge() || mBadgeScale > 0)) {
getIconBounds(mTempIconBounds);
mTempSpaceForBadgeOffset.set((getWidth() - mIconSize) / 2, getPaddingTop());
@@ -485,14 +415,6 @@
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- if (mBackground != null) mBackground.setCallback(this);
- mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
-
- @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mCenterVertically) {
Paint.FontMetrics fm = getPaint().getFontMetrics();
@@ -506,12 +428,6 @@
}
@Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- if (mBackground != null) mBackground.setCallback(null);
- }
-
- @Override
public void setTextColor(int color) {
mTextColor = color;
super.setTextColor(color);
@@ -523,15 +439,38 @@
super.setTextColor(colors);
}
+ public boolean shouldTextBeVisible() {
+ // Text should be visible everywhere but the hotseat.
+ Object tag = getParent() instanceof FolderIcon ? ((View) getParent()).getTag() : getTag();
+ ItemInfo info = tag instanceof ItemInfo ? (ItemInfo) tag : null;
+ return info == null || info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ }
+
public void setTextVisibility(boolean visible) {
- Resources res = getResources();
if (visible) {
super.setTextColor(mTextColor);
} else {
- super.setTextColor(res.getColor(android.R.color.transparent));
+ setTextAlpha(0);
}
}
+ private void setTextAlpha(int alpha) {
+ super.setTextColor(ColorUtils.setAlphaComponent(mTextColor, alpha));
+ }
+
+ private int getTextAlpha() {
+ return Color.alpha(getCurrentTextColor());
+ }
+
+ /**
+ * Creates an animator to fade the text in or out.
+ * @param fadeIn Whether the text should fade in or fade out.
+ */
+ public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
+ int toAlpha = shouldTextBeVisible() && fadeIn ? Color.alpha(mTextColor) : 0;
+ return ObjectAnimator.ofInt(this, TEXT_ALPHA_PROPERTY, toAlpha);
+ }
+
@Override
public void cancelLongPress() {
super.cancelLongPress();
@@ -547,27 +486,36 @@
((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
info.getInstallProgress() : 0)) : 100;
- setContentDescription(progressLevel > 0 ?
- getContext().getString(R.string.app_downloading_title, info.title,
- NumberFormat.getPercentInstance().format(progressLevel * 0.01)) :
- getContext().getString(R.string.app_waiting_download_title, info.title));
+ PreloadIconDrawable preloadDrawable = applyProgressLevel(progressLevel);
+ if (preloadDrawable != null && promiseStateChanged) {
+ preloadDrawable.maybePerformFinishedAnimation();
+ }
+ }
+ }
+
+ public PreloadIconDrawable applyProgressLevel(int progressLevel) {
+ if (getTag() instanceof ItemInfoWithIcon) {
+ ItemInfoWithIcon info = (ItemInfoWithIcon) getTag();
+ setContentDescription(progressLevel > 0
+ ? getContext().getString(R.string.app_downloading_title, info.title,
+ NumberFormat.getPercentInstance().format(progressLevel * 0.01))
+ : getContext().getString(R.string.app_waiting_download_title, info.title));
if (mIcon != null) {
final PreloadIconDrawable preloadDrawable;
if (mIcon instanceof PreloadIconDrawable) {
preloadDrawable = (PreloadIconDrawable) mIcon;
+ preloadDrawable.setLevel(progressLevel);
} else {
preloadDrawable = DrawableFactory.get(getContext())
.newPendingIcon(info.iconBitmap, getContext());
+ preloadDrawable.setLevel(progressLevel);
setIcon(preloadDrawable);
}
-
- preloadDrawable.setLevel(progressLevel);
- if (promiseStateChanged) {
- preloadDrawable.maybePerformFinishedAnimation();
- }
+ return preloadDrawable;
}
}
+ return null;
}
public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
@@ -603,7 +551,21 @@
private void setIcon(Drawable icon) {
mIcon = icon;
mIcon.setBounds(0, 0, mIconSize, mIconSize);
- applyCompoundDrawables(mIcon);
+ if (mIsIconVisible) {
+ applyCompoundDrawables(mIcon);
+ }
+ }
+
+ public void setIconVisible(boolean visible) {
+ mIsIconVisible = visible;
+ mDisableRelayout = true;
+ Drawable icon = mIcon;
+ if (!visible) {
+ icon = new ColorDrawable(Color.TRANSPARENT);
+ icon.setBounds(0, 0, mIconSize, mIconSize);
+ }
+ applyCompoundDrawables(icon);
+ mDisableRelayout = false;
}
protected void applyCompoundDrawables(Drawable icon) {
@@ -634,7 +596,9 @@
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof ShortcutInfo) {
applyFromShortcutInfo((ShortcutInfo) info);
- if ((info.rank < FolderIcon.NUM_ITEMS_IN_PREVIEW) && (info.container >= 0)) {
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
+ if (verifier.isItemInPreview(info.rank) && (info.container >= 0)) {
View folderIcon =
mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
if (folderIcon != null) {
@@ -666,6 +630,10 @@
}
}
+ public int getIconSize() {
+ return mIconSize;
+ }
+
/**
* Interface to be implemented by the grand parent to allow click shadow effect.
*/
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 8a477d8..e4a3226 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -102,8 +102,8 @@
protected void setDrawable(int resId) {
// We do not set the drawable in the xml as that inflates two drawables corresponding to
// drawableLeft and drawableStart.
- mDrawable = getResources().getDrawable(resId);
- setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+ setCompoundDrawablesRelativeWithIntrinsicBounds(resId, 0, 0, 0);
+ mDrawable = getCompoundDrawablesRelative()[0];
}
public void setDropTargetBar(DropTargetBar dropTargetBar) {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 8179dad..42b64ea 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -35,6 +35,7 @@
import android.os.Parcelable;
import android.support.annotation.IntDef;
import android.support.v4.view.ViewCompat;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -44,28 +45,27 @@
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
-
import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.anim.PropertyListBuilder;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.Stack;
public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
@@ -75,7 +75,7 @@
private static final String TAG = "CellLayout";
private static final boolean LOGD = false;
- private Launcher mLauncher;
+ private final Launcher mLauncher;
@ViewDebug.ExportedProperty(category = "launcher")
@Thunk int mCellWidth;
@ViewDebug.ExportedProperty(category = "launcher")
@@ -101,10 +101,10 @@
private GridOccupancy mTmpOccupied;
private OnTouchListener mInterceptTouchListener;
- private StylusEventHelper mStylusEventHelper;
+ private final StylusEventHelper mStylusEventHelper;
- private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
- FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
+ private final ArrayList<PreviewBackground> mFolderBackgrounds = new ArrayList<>();
+ final PreviewBackground mFolderLeaveBehind = new PreviewBackground();
private float mBackgroundAlpha;
@@ -121,9 +121,9 @@
// These arrays are used to implement the drag visualization on x-large screens.
// They are used as circular arrays, indexed by mDragOutlineCurrent.
- @Thunk Rect[] mDragOutlines = new Rect[4];
- @Thunk float[] mDragOutlineAlphas = new float[mDragOutlines.length];
- private InterruptibleInOutAnimator[] mDragOutlineAnims =
+ @Thunk final Rect[] mDragOutlines = new Rect[4];
+ @Thunk final float[] mDragOutlineAlphas = new float[mDragOutlines.length];
+ private final InterruptibleInOutAnimator[] mDragOutlineAnims =
new InterruptibleInOutAnimator[mDragOutlines.length];
// Used as an index into the above 3 arrays; indicates which is the most current value.
@@ -132,8 +132,8 @@
private final ClickShadowView mTouchFeedbackView;
- @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new HashMap<>();
- @Thunk HashMap<View, ReorderPreviewAnimation> mShakeAnimators = new HashMap<>();
+ @Thunk final ArrayMap<LayoutParams, Animator> mReorderAnimators = new ArrayMap<>();
+ @Thunk final ArrayMap<View, ReorderPreviewAnimation> mShakeAnimators = new ArrayMap<>();
private boolean mItemPlacementDirty = false;
@@ -142,8 +142,8 @@
private boolean mDragging = false;
- private TimeInterpolator mEaseOutInterpolator;
- private ShortcutAndWidgetContainer mShortcutsAndWidgets;
+ private final TimeInterpolator mEaseOutInterpolator;
+ private final ShortcutAndWidgetContainer mShortcutsAndWidgets;
@Retention(RetentionPolicy.SOURCE)
@IntDef({WORKSPACE, HOTSEAT, FOLDER})
@@ -154,7 +154,7 @@
@ContainerType private final int mContainerType;
- private final float mChildScale;
+ private final float mChildScale = 1f;
public static final int MODE_SHOW_REORDER_HINT = 0;
public static final int MODE_DRAG_OVER = 1;
@@ -168,10 +168,10 @@
private static final int REORDER_ANIMATION_DURATION = 150;
@Thunk final float mReorderPreviewAnimationMagnitude;
- private ArrayList<View> mIntersectingViews = new ArrayList<View>();
- private Rect mOccupiedRect = new Rect();
- private int[] mDirectionVector = new int[2];
- int[] mPreviousReorderDirection = new int[2];
+ private final ArrayList<View> mIntersectingViews = new ArrayList<>();
+ private final Rect mOccupiedRect = new Rect();
+ private final int[] mDirectionVector = new int[2];
+ final int[] mPreviousReorderDirection = new int[2];
private static final int INVALID_DIRECTION = -100;
private final Rect mTempRect = new Rect();
@@ -218,8 +218,6 @@
mFolderLeaveBehind.delegateCellX = -1;
mFolderLeaveBehind.delegateCellY = -1;
- mChildScale = mContainerType == HOTSEAT ? grid.inv.hotseatScale : 1f;
-
setAlwaysDrawnWithCacheEnabled(false);
final Resources res = getResources();
@@ -235,7 +233,7 @@
for (int i = 0; i < mDragOutlines.length; i++) {
mDragOutlines[i] = new Rect(-1, -1, -1, -1);
}
- mDragOutlinePaint.setColor(getResources().getColor(R.color.outline_color));
+ mDragOutlinePaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
// When dragging things around the home screens, we show a green outline of
// where the item will land. The outlines gradually fade out, leaving a trail
@@ -496,7 +494,7 @@
}
for (int i = 0; i < mFolderBackgrounds.size(); i++) {
- FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+ PreviewBackground bg = mFolderBackgrounds.get(i);
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
canvas.translate(mTempLocation[0], mTempLocation[1]);
@@ -522,7 +520,7 @@
super.dispatchDraw(canvas);
for (int i = 0; i < mFolderBackgrounds.size(); i++) {
- FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+ PreviewBackground bg = mFolderBackgrounds.get(i);
if (bg.isClipping) {
cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
canvas.save();
@@ -533,19 +531,16 @@
}
}
- public void addFolderBackground(FolderIcon.PreviewBackground bg) {
+ public void addFolderBackground(PreviewBackground bg) {
mFolderBackgrounds.add(bg);
}
- public void removeFolderBackground(FolderIcon.PreviewBackground bg) {
+ public void removeFolderBackground(PreviewBackground bg) {
mFolderBackgrounds.remove(bg);
}
public void setFolderLeaveBehindCell(int x, int y) {
-
- DeviceProfile grid = mLauncher.getDeviceProfile();
View child = getChildAt(x, y);
-
- mFolderLeaveBehind.setup(getResources().getDisplayMetrics(), grid, null,
+ mFolderLeaveBehind.setup(mLauncher, null,
child.getMeasuredWidth(), child.getPaddingTop());
mFolderLeaveBehind.delegateCellX = x;
@@ -568,7 +563,7 @@
try {
dispatchRestoreInstanceState(states);
} catch (IllegalArgumentException ex) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw ex;
}
// Mismatched viewId / viewType preventing restore. Skip restore on production builds.
@@ -611,7 +606,7 @@
// Hotseat icons - remove text
if (child instanceof BubbleTextView) {
BubbleTextView bubbleChild = (BubbleTextView) child;
- bubbleChild.setTextVisibility(mContainerType != HOTSEAT);
+ bubbleChild.setTextVisibility(bubbleChild.shouldTextBeVisible());
}
child.setScaleX(mChildScale);
@@ -1102,7 +1097,7 @@
result, resultSpan);
}
- private final Stack<Rect> mTempRectStack = new Stack<Rect>();
+ private final Stack<Rect> mTempRectStack = new Stack<>();
private void lazyInitTempRectStack() {
if (mTempRectStack.isEmpty()) {
for (int i = 0; i < mCountX * mCountY; i++) {
@@ -1147,7 +1142,7 @@
final int[] bestXY = result != null ? result : new int[2];
double bestDistance = Double.MAX_VALUE;
final Rect bestRect = new Rect(-1, -1, -1, -1);
- final Stack<Rect> validRegions = new Stack<Rect>();
+ final Stack<Rect> validRegions = new Stack<>();
final int countX = mCountX;
final int countY = mCountY;
@@ -1349,14 +1344,14 @@
final static int RIGHT = 1 << 2;
final static int BOTTOM = 1 << 3;
- ArrayList<View> views;
- ItemConfiguration config;
- Rect boundingRect = new Rect();
+ final ArrayList<View> views;
+ final ItemConfiguration config;
+ final Rect boundingRect = new Rect();
- int[] leftEdge = new int[mCountY];
- int[] rightEdge = new int[mCountY];
- int[] topEdge = new int[mCountX];
- int[] bottomEdge = new int[mCountX];
+ final int[] leftEdge = new int[mCountY];
+ final int[] rightEdge = new int[mCountY];
+ final int[] topEdge = new int[mCountX];
+ final int[] bottomEdge = new int[mCountX];
int dirtyEdges;
boolean boundingRectDirty;
@@ -1496,7 +1491,7 @@
return boundingRect;
}
- PositionComparator comparator = new PositionComparator();
+ final PositionComparator comparator = new PositionComparator();
class PositionComparator implements Comparator<View> {
int whichEdge = 0;
public int compare(View left, View right) {
@@ -1796,7 +1791,7 @@
}
}
- solution.intersectingViews = new ArrayList<View>(mIntersectingViews);
+ solution.intersectingViews = new ArrayList<>(mIntersectingViews);
// First we try to find a solution which respects the push mechanic. That is,
// we try to find a solution such that no displaced item travels through another item
@@ -1852,7 +1847,7 @@
int result[] = new int[2];
result = findNearestArea(pixelX, pixelY, spanX, spanY, result);
- boolean success = false;
+ boolean success;
// First we try the exact nearest position of the item being dragged,
// we will then want to try to move this around to other neighbouring positions
success = rearrangementExists(result[0], result[1], spanX, spanY, direction, dragView,
@@ -1960,14 +1955,14 @@
// Class which represents the reorder preview animations. These animations show that an item is
// in a temporary state, and hint at where the item will return to.
class ReorderPreviewAnimation {
- View child;
+ final View child;
float finalDeltaX;
float finalDeltaY;
float initDeltaX;
float initDeltaY;
- float finalScale;
+ final float finalScale;
float initScale;
- int mode;
+ final int mode;
boolean repeating = false;
private static final int PREVIEW_DURATION = 300;
private static final int HINT_DURATION = Workspace.REORDER_TIMEOUT;
@@ -2417,9 +2412,9 @@
}
private static class ItemConfiguration extends CellAndSpan {
- HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
- private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
- ArrayList<View> sortedViews = new ArrayList<View>();
+ final ArrayMap<View, CellAndSpan> map = new ArrayMap<>();
+ private final ArrayMap<View, CellAndSpan> savedMap = new ArrayMap<>();
+ final ArrayList<View> sortedViews = new ArrayList<>();
ArrayList<View> intersectingViews;
boolean isSolution = false;
@@ -2469,7 +2464,6 @@
* @param pixelY The Y location at which you want to search for a vacant area.
* @param spanX Horizontal span of the object.
* @param spanY Vertical span of the object.
- * @param ignoreView Considers space occupied by this view as unoccupied
* @param result Previously returned value to possibly recycle.
* @return The X, Y cell of a vacant area that can contain this object,
* nearest the requested location.
@@ -2781,9 +2775,9 @@
// cellX and cellY coordinates and which page was clicked. We then set this as a tag on
// the CellLayout that was long clicked
public static final class CellInfo extends CellAndSpan {
- public View cell;
- long screenId;
- long container;
+ public final View cell;
+ final long screenId;
+ final long container;
public CellInfo(View v, ItemInfo info) {
cellX = info.cellX;
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 05911ab..1ec30ba 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -13,18 +13,15 @@
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Log;
-
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.IOException;
import java.net.URISyntaxException;
-import java.util.HashMap;
import java.util.List;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
/**
* Implements the layout parser with rules for internal layouts and partner layouts.
@@ -55,20 +52,20 @@
}
@Override
- protected HashMap<String, TagParser> getFolderElementsMap() {
+ protected ArrayMap<String, TagParser> getFolderElementsMap() {
return getFolderElementsMap(mSourceRes);
}
- @Thunk HashMap<String, TagParser> getFolderElementsMap(Resources res) {
- HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ @Thunk ArrayMap<String, TagParser> getFolderElementsMap(Resources res) {
+ ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(res));
return parsers;
}
@Override
- protected HashMap<String, TagParser> getLayoutElementsMap() {
- HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ protected ArrayMap<String, TagParser> getLayoutElementsMap() {
+ ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_APPWIDGET, new AppWidgetParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
diff --git a/src/com/android/launcher3/DeferredHandler.java b/src/com/android/launcher3/DeferredHandler.java
deleted file mode 100644
index a43ab67..0000000
--- a/src/com/android/launcher3/DeferredHandler.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.MessageQueue;
-
-import com.android.launcher3.util.Thunk;
-
-import java.util.LinkedList;
-
-/**
- * Queue of things to run on a looper thread. Items posted with {@link #post} will not
- * be actually enqued on the handler until after the last one has run, to keep from
- * starving the thread.
- *
- * This class is fifo.
- */
-public class DeferredHandler {
- @Thunk LinkedList<Runnable> mQueue = new LinkedList<>();
- private MessageQueue mMessageQueue = Looper.myQueue();
- private Impl mHandler = new Impl();
-
- @Thunk class Impl extends Handler implements MessageQueue.IdleHandler {
- public void handleMessage(Message msg) {
- Runnable r;
- synchronized (mQueue) {
- if (mQueue.size() == 0) {
- return;
- }
- r = mQueue.removeFirst();
- }
- r.run();
- synchronized (mQueue) {
- scheduleNextLocked();
- }
- }
-
- public boolean queueIdle() {
- handleMessage(null);
- return false;
- }
- }
-
- private class IdleRunnable implements Runnable {
- Runnable mRunnable;
-
- IdleRunnable(Runnable r) {
- mRunnable = r;
- }
-
- public void run() {
- mRunnable.run();
- }
- }
-
- public DeferredHandler() {
- }
-
- /** Schedule runnable to run after everything that's on the queue right now. */
- public void post(Runnable runnable) {
- synchronized (mQueue) {
- mQueue.add(runnable);
- if (mQueue.size() == 1) {
- scheduleNextLocked();
- }
- }
- }
-
- /** Schedule runnable to run when the queue goes idle. */
- public void postIdle(final Runnable runnable) {
- post(new IdleRunnable(runnable));
- }
-
- public void cancelAll() {
- synchronized (mQueue) {
- mQueue.clear();
- }
- }
-
- /** Runs all queued Runnables from the calling thread. */
- public void flush() {
- LinkedList<Runnable> queue = new LinkedList<>();
- synchronized (mQueue) {
- queue.addAll(mQueue);
- mQueue.clear();
- }
- for (Runnable r : queue) {
- r.run();
- }
- }
-
- void scheduleNextLocked() {
- if (mQueue.size() > 0) {
- Runnable peek = mQueue.getFirst();
- if (peek instanceof IdleRunnable) {
- mMessageQueue.addIdleHandler(mHandler);
- } else {
- mHandler.sendEmptyMessage(1);
- }
- }
- }
-}
-
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9097ed2..975675a 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -40,7 +40,7 @@
// Get the hover color
mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
- setDrawable(R.drawable.ic_remove_launcher);
+ setDrawable(R.drawable.ic_remove_shadow);
}
@Override
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e47031a..eef6510 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -19,6 +19,7 @@
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
@@ -32,7 +33,6 @@
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.badge.BadgeRenderer;
-import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList;
@@ -80,9 +80,8 @@
public final int workspaceSpringLoadedBottomSpace;
// Page indicator
- private final int pageIndicatorHeightPx;
- private final int pageIndicatorLandGutterLeftNavBarPx;
- private final int pageIndicatorLandGutterRightNavBarPx;
+ private final int pageIndicatorSizePx;
+ private final int pageIndicatorLandGutterPx;
private final int pageIndicatorLandWorkspaceOffsetPx;
// Workspace icons
@@ -93,6 +92,7 @@
public int cellWidthPx;
public int cellHeightPx;
+ public int workspaceCellPaddingXPx;
// Folder
public int folderBackgroundOffset;
@@ -109,15 +109,17 @@
public int folderChildDrawablePaddingPx;
// Hotseat
- public int hotseatCellWidthPx;
public int hotseatCellHeightPx;
- public int hotseatIconSizePx;
public int hotseatBarHeightPx;
- private int hotseatBarTopPaddingPx;
- private int hotseatBarBottomPaddingPx;
- private int hotseatLandGutterPx;
+ public int hotseatBarTopPaddingPx;
+ public int hotseatBarLeftNavBarRightPaddingPx;
+ public int hotseatBarRightNavBarRightPaddingPx;
+ public int hotseatBarBottomPaddingPx;
+ public int hotseatLandLeftNavBarGutterPx;
+ public int hotseatLandRightNavBarGutterPx;
// All apps
+ public int allAppsCellHeightPx;
public int allAppsNumCols;
public int allAppsNumPredictiveCols;
public int allAppsButtonVisualSize;
@@ -159,19 +161,22 @@
transposeLayoutWithOrientation =
res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+ context = getContext(context, isVerticalBarLayout()
+ ? Configuration.ORIENTATION_LANDSCAPE
+ : Configuration.ORIENTATION_PORTRAIT);
+ res = context.getResources();
+
+
ComponentName cn = new ComponentName(context.getPackageName(),
this.getClass().getName());
defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
desiredWorkspaceLeftRightMarginPx = edgeMarginPx;
- pageIndicatorHeightPx =
- res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
- pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar);
+ pageIndicatorSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_size);
+ pageIndicatorLandGutterPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_page_indicator_gutter_width);
pageIndicatorLandWorkspaceOffsetPx =
res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
- pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize(
- R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar);
defaultPageSpacingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
topWorkspacePadding =
@@ -191,11 +196,26 @@
dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
workspaceSpringLoadedBottomSpace =
res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
- hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
+
+ workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
+
hotseatBarTopPaddingPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
- hotseatBarBottomPaddingPx = 0;
- hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
+ 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);
+ hotseatBarHeightPx = isVerticalBarLayout()
+ ? res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height)
+ : res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height)
+ + hotseatBarTopPaddingPx + hotseatBarBottomPaddingPx;
+
+ hotseatLandLeftNavBarGutterPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_land_left_nav_bar_gutter_width);
+ hotseatLandRightNavBarGutterPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_hotseat_land_right_nav_bar_gutter_width);
// Determine sizes.
widthPx = width;
@@ -228,9 +248,6 @@
profile.cellHeightPx = profile.iconSizePx + profile.iconDrawablePaddingPx
+ Utilities.calculateTextHeight(profile.iconTextSizePx);
- // The nav bar is black so we add bottom padding to visually center hotseat icons.
- profile.hotseatBarBottomPaddingPx = profile.hotseatBarTopPaddingPx;
-
// We use these scales to measure and layout the widgets using their full invariant profile
// sizes and then draw them scaled and centered to fit in their multi-window mode cellspans.
float appWidgetScaleX = (float) profile.getCellSize().x / getCellSize().x;
@@ -259,45 +276,51 @@
private void computeAllAppsButtonSize(Context context) {
Resources res = context.getResources();
float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
- allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
+ allAppsButtonVisualSize = (int) (iconSizePx * (1 - padding)) - context.getResources()
.getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
}
private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
- updateIconSize(1f, iconDrawablePaddingOriginalPx, res, dm);
+ updateIconSize(1f, res, dm);
// Check to see if the icons fit within the available height. If not, then scale down.
float usedHeight = (cellHeightPx * inv.numRows);
int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
if (usedHeight > maxHeight) {
float scale = maxHeight / usedHeight;
- updateIconSize(scale, 0, res, dm);
+ updateIconSize(scale, res, dm);
}
-
updateAvailableFolderCellDimensions(dm, res);
}
- private void updateIconSize(float scale, int drawablePadding, Resources res,
- DisplayMetrics dm) {
- iconSizePx = (int) (Utilities.pxFromDp(inv.iconSize, dm) * scale);
+ private void updateIconSize(float scale, Resources res, DisplayMetrics dm) {
+ float invIconSizePx = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+ iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
- iconDrawablePaddingPx = drawablePadding;
- hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
+ iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
+
+ // All apps
+ allAppsIconTextSizePx = iconTextSizePx;
allAppsIconSizePx = iconSizePx;
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
- allAppsIconTextSizePx = iconTextSizePx;
+ allAppsCellHeightPx = getCellSize().y;
- cellWidthPx = iconSizePx;
+ if (isVerticalBarLayout()) {
+ // Always hide the Workspace text with vertical bar layout.
+ iconTextSizePx = 0;
+ allAppsCellHeightPx += Utilities.calculateTextHeight(allAppsIconTextSizePx);
+ }
+
+ cellWidthPx = iconSizePx + iconDrawablePaddingPx;
cellHeightPx = iconSizePx + iconDrawablePaddingPx
+ Utilities.calculateTextHeight(iconTextSizePx);
// Hotseat
- hotseatCellWidthPx = iconSizePx;
- hotseatCellHeightPx = iconSizePx;
+ hotseatCellHeightPx = iconSizePx + iconDrawablePaddingPx;
if (!isVerticalBarLayout()) {
int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
- - pageIndicatorHeightPx - topWorkspacePadding;
+ - pageIndicatorSizePx - topWorkspacePadding;
float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
workspaceSpringLoadShrinkFactor = Math.min(
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -308,7 +331,7 @@
}
// Folder icon
- folderBackgroundOffset = -edgeMarginPx;
+ folderBackgroundOffset = -iconDrawablePaddingPx;
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
}
@@ -321,7 +344,7 @@
updateFolderCellSize(1f, dm, res);
// Don't let the folder get too close to the edges of the screen.
- int folderMargin = 4 * edgeMarginPx;
+ int folderMargin = edgeMarginPx;
// Check if the icons fit within the available height.
float usedHeight = folderCellHeightPx * inv.numFolderRows + folderBottomPanelSize;
@@ -401,22 +424,25 @@
/**
* Returns the workspace padding in the specified orientation.
- * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such
- * this value is not reliable.
- * Use {@link #getTotalWorkspacePadding()} instead.
*/
public Rect getWorkspacePadding(Rect recycle) {
Rect padding = recycle == null ? new Rect() : recycle;
if (isVerticalBarLayout()) {
if (mInsets.left > 0) {
- padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0,
- hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx);
+ padding.set(mInsets.left + pageIndicatorLandGutterPx,
+ 0,
+ hotseatBarHeightPx + hotseatLandLeftNavBarGutterPx
+ + hotseatBarLeftNavBarRightPaddingPx - mInsets.left,
+ edgeMarginPx);
} else {
- padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
- hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
+ padding.set(pageIndicatorLandGutterPx,
+ 0,
+ hotseatBarHeightPx + hotseatLandRightNavBarGutterPx
+ + hotseatBarRightNavBarRightPaddingPx,
+ edgeMarginPx);
}
} else {
- int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
+ int paddingBottom = hotseatBarHeightPx + pageIndicatorSizePx;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
@@ -458,7 +484,7 @@
return new Rect(mInsets.left,
mInsets.top + dropTargetBarSizePx + edgeMarginPx,
mInsets.left + availableWidthPx,
- mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
+ mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorSizePx -
edgeMarginPx);
}
}
@@ -471,7 +497,7 @@
// 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);
+ return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left / 2 + 1);
}
}
@@ -522,7 +548,7 @@
lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
lp.width = searchBarBounds.x;
lp.height = searchBarBounds.y;
- lp.topMargin = mInsets.top + edgeMarginPx;
+ lp.topMargin = mInsets.top + edgeMarginPx / 2;
searchBar.setLayoutParams(lp);
// Layout the workspace
@@ -532,14 +558,6 @@
workspacePadding.bottom);
workspace.setPageSpacing(getWorkspacePageSpacing());
- // Only display when enabled
- if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
- View qsbContainer = launcher.getQsbContainer();
- lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
- lp.topMargin = mInsets.top + workspacePadding.top;
- qsbContainer.setLayoutParams(lp);
- }
-
// Layout the hotseat
Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
@@ -556,7 +574,12 @@
lp.gravity = Gravity.RIGHT;
lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
lp.height = LayoutParams.MATCH_PARENT;
- hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
+
+ int paddingRight = mInsets.left > 0
+ ? hotseatBarLeftNavBarRightPaddingPx
+ : hotseatBarRightNavBarRightPaddingPx;
+
+ hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right + paddingRight,
workspacePadding.bottom);
} else if (isTablet) {
// Pad the hotseat with the workspace padding calculated above
@@ -584,17 +607,15 @@
lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
if (isVerticalBarLayout()) {
if (mInsets.left > 0) {
- lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx -
- lp.width - pageIndicatorLandWorkspaceOffsetPx;
- } else if (mInsets.right > 0) {
- lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width -
- pageIndicatorLandWorkspaceOffsetPx;
+ 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 = pageIndicatorHeightPx;
+ lp.height = pageIndicatorSizePx;
lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
}
pageIndicator.setLayoutParams(lp);
@@ -613,6 +634,12 @@
overviewMode.setLayoutParams(lp);
}
+ // Layout the AllAppsRecyclerView
+ View view = launcher.findViewById(R.id.apps_list_view);
+ int paddingLeftRight = hasVerticalBarLayout ? 0 : edgeMarginPx;
+ view.setPadding(paddingLeftRight, view.getPaddingTop(), paddingLeftRight,
+ view.getPaddingBottom());
+
if (notifyListeners) {
for (int i = mListeners.size() - 1; i >= 0; i--) {
mListeners.get(i).onLauncherLayoutChanged();
@@ -656,9 +683,8 @@
}
// In landscape, we match the width of the workspace
- int padding = (pageIndicatorLandGutterRightNavBarPx +
- hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
- return new int[]{ padding, padding };
+ Rect padding = getWorkspacePadding(null);
+ return new int[] { padding.left - mInsets.left, padding.right + mInsets.left};
}
public boolean shouldIgnoreLongPressToOverview(float touchX) {
@@ -667,4 +693,11 @@
boolean touchedRhsEdge = mInsets.right == 0 && touchX > (widthPx - edgeMarginPx);
return !inMultiWindowMode && (touchedLhsEdge || touchedRhsEdge);
}
+
+ private static Context getContext(Context c, int orientation) {
+ Configuration context = new Configuration(c.getResources().getConfiguration());
+ context.orientation = orientation;
+ return c.createConfigurationContext(context);
+
+ }
}
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 199baaf..1272e0a 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -36,8 +36,6 @@
public class FastBitmapDrawable extends Drawable {
- private static final int[] STATE_PRESSED = new int[] {android.R.attr.state_pressed};
-
private static final float PRESSED_BRIGHTNESS = 100f / 255f;
private static final float DISABLED_DESATURATION = 1f;
private static final float DISABLED_BRIGHTNESS = 0.5f;
@@ -107,17 +105,6 @@
@Override
public void draw(Canvas canvas) {
- drawInternal(canvas);
- }
-
- public void drawWithBrightness(Canvas canvas, float brightness) {
- float oldBrightness = getBrightness();
- setBrightness(brightness);
- drawInternal(canvas);
- setBrightness(oldBrightness);
- }
-
- protected void drawInternal(Canvas canvas) {
canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
}
@@ -185,6 +172,11 @@
}
@Override
+ public ColorFilter getColorFilter() {
+ return mPaint.getColorFilter();
+ }
+
+ @Override
protected boolean onStateChange(int[] state) {
boolean isPressed = false;
for (int s : state) {
diff --git a/src/com/android/launcher3/FirstFrameAnimatorHelper.java b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
index a51ddd4..3cbc989 100644
--- a/src/com/android/launcher3/FirstFrameAnimatorHelper.java
+++ b/src/com/android/launcher3/FirstFrameAnimatorHelper.java
@@ -23,7 +23,6 @@
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver;
-
import com.android.launcher3.util.Thunk;
/*
@@ -33,10 +32,11 @@
*/
public class FirstFrameAnimatorHelper extends AnimatorListenerAdapter
implements ValueAnimator.AnimatorUpdateListener {
+ private static final String TAG = "FirstFrameAnimatorHlpr";
private static final boolean DEBUG = false;
private static final int MAX_DELAY = 1000;
private static final int IDEAL_FRAME_DURATION = 16;
- private View mTarget;
+ private final View mTarget;
private long mStartFrame;
private long mStartTime = -1;
private boolean mHandlingOnAnimationUpdate;
@@ -77,7 +77,7 @@
sGlobalFrameCounter++;
if (DEBUG) {
long newTime = System.currentTimeMillis();
- Log.d("FirstFrameAnimatorHelper", "TICK " + (newTime - mTime));
+ Log.d(TAG, "TICK " + (newTime - mTime));
mTime = newTime;
}
}
@@ -139,7 +139,7 @@
public void print(ValueAnimator animation) {
float flatFraction = animation.getCurrentPlayTime() / (float) animation.getDuration();
- Log.d("FirstFrameAnimatorHelper", sGlobalFrameCounter +
+ Log.d(TAG, sGlobalFrameCounter +
"(" + (sGlobalFrameCounter - mStartFrame) + ") " + mTarget + " dirty? " +
mTarget.isDirty() + " " + flatFraction + " " + this + " " + animation);
}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index b36734b..fe7acda 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,7 +22,7 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.util.FocusLogic;
@@ -93,7 +93,7 @@
}
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new IllegalStateException("Parent of the focused item is not supported.");
} else {
return false;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 0041bb4..7959d40 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -45,6 +45,12 @@
*/
public static final int FLAG_MULTI_PAGE_ANIMATION = 0x00000004;
+ /**
+ * The folder items ranks have been updated such that they appear unchanged with the new
+ * permutation display logic.
+ */
+ public static final int FLAG_ITEM_RANKS_UPDATED = 0x00000008;
+
public int options;
/**
@@ -65,9 +71,17 @@
* @param item
*/
public void add(ShortcutInfo item, boolean animate) {
- contents.add(item);
+ add(item, contents.size(), animate);
+ }
+
+ /**
+ * Add an app or shortcut for a specified rank.
+ */
+ public void add(ShortcutInfo item, int rank, boolean animate) {
+ rank = Utilities.boundToRange(rank, 0, contents.size());
+ contents.add(rank, item);
for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onAdd(item);
+ listeners.get(i).onAdd(item, rank);
}
itemsChanged(animate);
}
@@ -121,7 +135,7 @@
}
public interface FolderListener {
- public void onAdd(ShortcutInfo item);
+ public void onAdd(ShortcutInfo item, int rank);
public void onRemove(ShortcutInfo item);
public void onTitleChanged(CharSequence title);
public void onItemsChanged(boolean animate);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 47052a7..af3abeb 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -72,7 +72,9 @@
mBackgroundColor = ColorUtils.setAlphaComponent(
Themes.getAttrColor(context, android.R.attr.colorPrimary), 0);
mBackground = new ColorDrawable(mBackgroundColor);
- setBackground(mBackground);
+ if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ setBackground(mBackground);
+ }
}
public CellLayout getLayout() {
@@ -149,7 +151,6 @@
mLauncher.setAllAppsButton(allAppsButton);
allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
allAppsButton.setOnClickListener(mLauncher);
- allAppsButton.setOnLongClickListener(mLauncher);
allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
}
@@ -179,8 +180,12 @@
}
public void updateColor(ExtractedColors extractedColors, boolean animate) {
+ if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ // not hotseat visible
+ return;
+ }
if (!mHasVerticalHotseat) {
- int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+ int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
if (mBackgroundColorAnimator != null) {
mBackgroundColorAnimator.cancel();
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 1217030..a23a674 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -32,10 +32,6 @@
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
@@ -45,7 +41,6 @@
import android.support.annotation.NonNull;
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;
@@ -55,9 +50,7 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.SQLiteCacheHelper;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
-
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -96,28 +89,18 @@
private final Context mContext;
private final PackageManager mPackageManager;
- private IconProvider mIconProvider;
+ private final IconProvider mIconProvider;
@Thunk final UserManagerCompat mUserManager;
private final LauncherAppsCompat mLauncherApps;
private final HashMap<ComponentKey, CacheEntry> mCache =
- new HashMap<ComponentKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
+ new HashMap<>(INITIAL_ICON_CACHE_CAPACITY);
private final int mIconDpi;
@Thunk final IconDB mIconDb;
@Thunk final Handler mWorkerHandler;
- // The background color used for activity icons. Since these icons are displayed in all-apps
- // and folders, this would be same as the light quantum panel background. This color
- // is used to convert icons to RGB_565.
- private final int mActivityBgColor;
- // The background color used for package icons. These are displayed in widget tray, which
- // has a dark quantum panel background.
- private final int mPackageBgColor;
private final BitmapFactory.Options mLowResOptions;
- private Canvas mLowResCanvas;
- private Paint mLowResPaint;
-
public IconCache(Context context, InvariantDeviceProfile inv) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -125,16 +108,11 @@
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mIconDpi = inv.fillResIconDpi;
mIconDb = new IconDB(context, inv.iconBitmapSize);
- mLowResCanvas = new Canvas();
- mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
mIconProvider = Utilities.getOverrideObject(
IconProvider.class, context, R.string.icon_provider_class);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
- mActivityBgColor = Themes.getColorPrimary(context, R.style.LauncherTheme);
- mPackageBgColor = Themes.getColorPrimary(context, R.style.WidgetContainerTheme);
-
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.
@@ -142,7 +120,7 @@
}
private Drawable getFullResDefaultActivityIcon() {
- return getFullResIcon(Resources.getSystem(), android.R.mipmap.sym_def_app_icon);
+ return getFullResIcon(Resources.getSystem(), android.R.drawable.sym_def_app_icon);
}
private Drawable getFullResIcon(Resources resources, int iconId) {
@@ -190,7 +168,11 @@
}
public Drawable getFullResIcon(LauncherActivityInfo info) {
- return mIconProvider.getIcon(info, mIconDpi);
+ return getFullResIcon(info, true);
+ }
+
+ public Drawable getFullResIcon(LauncherActivityInfo info, boolean flattenDrawable) {
+ return mIconProvider.getIcon(info, mIconDpi, flattenDrawable);
}
protected Bitmap makeDefaultIcon(UserHandle user) {
@@ -209,7 +191,7 @@
* Remove any records for the supplied package name from memory.
*/
private void removeFromMemCacheLocked(String packageName, UserHandle user) {
- HashSet<ComponentKey> forDeletion = new HashSet<ComponentKey>();
+ HashSet<ComponentKey> forDeletion = new HashSet<>();
for (ComponentKey key: mCache.keySet()) {
if (key.componentName.getPackageName().equals(packageName)
&& key.user.equals(user)) {
@@ -235,7 +217,6 @@
}
} catch (NameNotFoundException e) {
Log.d(TAG, "Package not found", e);
- return;
}
}
@@ -280,7 +261,7 @@
Set<String> ignorePackages) {
long userSerial = mUserManager.getSerialNumberForUser(user);
PackageManager pm = mContext.getPackageManager();
- HashMap<String, PackageInfo> pkgInfoMap = new HashMap<String, PackageInfo>();
+ HashMap<String, PackageInfo> pkgInfoMap = new HashMap<>();
for (PackageInfo info : pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
pkgInfoMap.put(info.packageName, info);
}
@@ -290,7 +271,7 @@
componentMap.put(app.getComponentName(), app);
}
- HashSet<Integer> itemsToRemove = new HashSet<Integer>();
+ HashSet<Integer> itemsToRemove = new HashSet<>();
Stack<LauncherActivityInfo> appsToUpdate = new Stack<>();
Cursor c = null;
@@ -387,7 +368,7 @@
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(key, entry);
- Bitmap lowResIcon = generateLowResIcon(entry.icon, mActivityBgColor);
+ Bitmap lowResIcon = generateLowResIcon(entry.icon);
ContentValues values = newContentValues(entry.icon, lowResIcon, entry.title.toString(),
app.getApplicationInfo().packageName);
addIconToDB(values, app.getComponentName(), info, userSerial);
@@ -637,7 +618,7 @@
// only keep the low resolution icon instead of the larger full-sized icon
Bitmap icon = LauncherIcons.createBadgedIconBitmap(
appInfo.loadIcon(mPackageManager), user, mContext, appInfo.targetSdkVersion);
- Bitmap lowResIcon = generateLowResIcon(icon, mPackageBgColor);
+ Bitmap lowResIcon = generateLowResIcon(icon);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
entry.icon = useLowResIcon ? lowResIcon : icon;
@@ -720,7 +701,7 @@
private final HashMap<String, PackageInfo> mPkgInfoMap;
private final Stack<LauncherActivityInfo> mAppsToAdd;
private final Stack<LauncherActivityInfo> mAppsToUpdate;
- private final HashSet<String> mUpdatedPackages = new HashSet<String>();
+ private final HashSet<String> mUpdatedPackages = new HashSet<>();
@Thunk SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
Stack<LauncherActivityInfo> appsToAdd,
@@ -769,7 +750,7 @@
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int DB_VERSION = 13;
+ private final static int DB_VERSION = 17;
private final static int RELEASE_VERSION = DB_VERSION +
(FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
@@ -822,24 +803,10 @@
/**
* Generates a new low-res icon given a high-res icon.
*/
- private Bitmap generateLowResIcon(Bitmap icon, int lowResBackgroundColor) {
- if (lowResBackgroundColor == Color.TRANSPARENT) {
- return Bitmap.createScaledBitmap(icon,
- icon.getWidth() / LOW_RES_SCALE_FACTOR,
- icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
- } else {
- Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
- icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
- synchronized (this) {
- mLowResCanvas.setBitmap(lowResIcon);
- mLowResCanvas.drawColor(lowResBackgroundColor);
- mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()),
- new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()),
- mLowResPaint);
- mLowResCanvas.setBitmap(null);
- }
- return lowResIcon;
- }
+ private Bitmap generateLowResIcon(Bitmap icon) {
+ return Bitmap.createScaledBitmap(icon,
+ icon.getWidth() / LOW_RES_SCALE_FACTOR,
+ icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
}
private static Bitmap loadIconNoResize(Cursor c, int iconIndex, BitmapFactory.Options options) {
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
index a5d3990..6872515 100644
--- a/src/com/android/launcher3/IconProvider.java
+++ b/src/com/android/launcher3/IconProvider.java
@@ -24,8 +24,11 @@
return mSystemState;
}
-
- public Drawable getIcon(LauncherActivityInfo info, int iconDpi) {
+ /**
+ * @param flattenDrawable true if the caller does not care about the specification of the
+ * original icon as long as the flattened version looks the same.
+ */
+ public Drawable getIcon(LauncherActivityInfo info, int iconDpi, boolean flattenDrawable) {
return info.getIcon(iconDpi);
}
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 0608fdd..f088d11 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -42,12 +42,10 @@
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
+ protected void setupUi() {
// Get the hover color
mHoverColor = Themes.getColorAccent(getContext());
-
- setDrawable(R.drawable.ic_info_launcher);
+ setDrawable(R.drawable.ic_info_shadow);
}
@Override
@@ -67,6 +65,11 @@
public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
+ if (info instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) info;
+ launcher.startActivity(promiseAppInfo.getMarketIntent());
+ return true;
+ }
boolean result = false;
ComponentName componentName = null;
if (info instanceof AppInfo) {
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 154641c..be76490 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -9,9 +9,6 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.config.FeatureFlags;
-
public class InsettableFrameLayout extends FrameLayout implements
ViewGroup.OnHierarchyChangeListener, Insettable {
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index ce85570..0370777 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -34,6 +34,7 @@
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
+import android.util.Pair;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -59,6 +60,16 @@
import java.util.Set;
public class InstallShortcutReceiver extends BroadcastReceiver {
+
+ public static final int FLAG_ACTIVITY_PAUSED = 1;
+ public static final int FLAG_LOADER_RUNNING = 2;
+ public static final int FLAG_DRAG_AND_DROP = 4;
+ public static final int FLAG_BULK_ADD = 4;
+
+ // Determines whether to defer installing shortcuts immediately until
+ // processAllPendingInstalls() is called.
+ private static int sInstallQueueDisabledFlags = 0;
+
private static final String TAG = "InstallShortcutReceiver";
private static final boolean DBG = false;
@@ -151,10 +162,6 @@
}
}
- // Determines whether to defer installing shortcuts immediately until
- // processAllPendingInstalls() is called.
- private static boolean mUseInstallQueue = false;
-
public void onReceive(Context context, Intent data) {
if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
return;
@@ -207,7 +214,11 @@
public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
PendingInstallShortcutInfo info = createPendingInfo(context, data);
- return info == null ? null : (ShortcutInfo) info.getItemInfo();
+ return info == null ? null : (ShortcutInfo) info.getItemInfo().first;
+ }
+
+ public static ShortcutInfo fromActivityInfo(LauncherActivityInfo info, Context context) {
+ return (ShortcutInfo) (new PendingInstallShortcutInfo(info, context).getItemInfo().first);
}
public static void queueShortcut(ShortcutInfoCompat info, Context context) {
@@ -245,27 +256,28 @@
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- LauncherAppState app = LauncherAppState.getInstance(context);
- boolean launcherNotLoaded = app.getModel().getCallback() == null;
-
addToInstallQueue(Utilities.getPrefs(context), info);
- if (!mUseInstallQueue && !launcherNotLoaded) {
- flushInstallQueue(context);
- }
+ flushInstallQueue(context);
}
- static void enableInstallQueue() {
- mUseInstallQueue = true;
+ public static void enableInstallQueue(int flag) {
+ sInstallQueueDisabledFlags |= flag;
}
- static void disableAndFlushInstallQueue(Context context) {
- mUseInstallQueue = false;
+ public static void disableAndFlushInstallQueue(int flag, Context context) {
+ sInstallQueueDisabledFlags &= ~flag;
flushInstallQueue(context);
}
static void flushInstallQueue(Context context) {
+ LauncherModel model = LauncherAppState.getInstance(context).getModel();
+ boolean launcherNotLoaded = model.getCallback() == null;
+ if (sInstallQueueDisabledFlags != 0 || launcherNotLoaded) {
+ return;
+ }
+
ArrayList<PendingInstallShortcutInfo> items = getAndClearInstallQueue(context);
if (!items.isEmpty()) {
- LauncherAppState.getInstance(context).getModel().addAndBindAddedWorkspaceItems(
+ model.addAndBindAddedWorkspaceItems(
new LazyShortcutsProvider(context.getApplicationContext(), items));
}
}
@@ -439,7 +451,7 @@
}
}
- public ItemInfo getItemInfo() {
+ public Pair<ItemInfo, Object> getItemInfo() {
if (activityInfo != null) {
AppInfo appInfo = new AppInfo(mContext, activityInfo, user);
final LauncherAppState app = LauncherAppState.getInstance(mContext);
@@ -459,11 +471,11 @@
}
});
}
- return si;
+ return Pair.create((ItemInfo) si, (Object) activityInfo);
} else if (shortcutInfo != null) {
ShortcutInfo si = new ShortcutInfo(shortcutInfo, mContext);
si.iconBitmap = LauncherIcons.createShortcutIcon(shortcutInfo, mContext);
- return si;
+ return Pair.create((ItemInfo) si, (Object) shortcutInfo);
} else if (providerInfo != null) {
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo
.fromProviderInfo(mContext, providerInfo);
@@ -475,9 +487,10 @@
widgetInfo.minSpanY = info.minSpanY;
widgetInfo.spanX = Math.min(info.spanX, idp.numColumns);
widgetInfo.spanY = Math.min(info.spanY, idp.numRows);
- return widgetInfo;
+ return Pair.create((ItemInfo) widgetInfo, (Object) providerInfo);
} else {
- return createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+ ShortcutInfo si = createShortcutInfo(data, LauncherAppState.getInstance(mContext));
+ return Pair.create((ItemInfo) si, null);
}
}
@@ -588,7 +601,7 @@
return new PendingInstallShortcutInfo(info, original.mContext);
}
- private static class LazyShortcutsProvider extends Provider<List<ItemInfo>> {
+ private static class LazyShortcutsProvider extends Provider<List<Pair<ItemInfo, Object>>> {
private final Context mContext;
private final ArrayList<PendingInstallShortcutInfo> mPendingItems;
@@ -603,9 +616,9 @@
* packageManager and icon cache.
*/
@Override
- public ArrayList<ItemInfo> get() {
+ public ArrayList<Pair<ItemInfo, Object>> get() {
Preconditions.assertNonUiThread();
- ArrayList<ItemInfo> installQueue = new ArrayList<>();
+ ArrayList<Pair<ItemInfo, Object>> installQueue = new ArrayList<>();
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
for (PendingInstallShortcutInfo pendingInfo : mPendingItems) {
// If the intent specifies a package, make sure the package exists
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 9e214d1..d7bebd1 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -28,7 +28,6 @@
import android.view.WindowManager;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
@@ -77,6 +76,7 @@
public int numFolderRows;
public int numFolderColumns;
public float iconSize;
+ public float landscapeIconSize;
public int iconBitmapSize;
public int fillResIconDpi;
public float iconTextSize;
@@ -85,8 +85,6 @@
* Number of icons inside the hotseat area.
*/
public int numHotseatIcons;
- float hotseatIconSize;
- public float hotseatScale;
int defaultLayoutId;
public DeviceProfile landscapeProfile;
@@ -100,12 +98,12 @@
public InvariantDeviceProfile(InvariantDeviceProfile p) {
this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
- p.iconSize, p.iconTextSize, p.numHotseatIcons, p.hotseatIconSize,
+ p.iconSize, p.landscapeIconSize, p.iconTextSize, p.numHotseatIcons,
p.defaultLayoutId);
}
InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
- float is, float its, int hs, float his, int dlId) {
+ float is, float lis, float its, int hs, int dlId) {
name = n;
minWidthDps = w;
minHeightDps = h;
@@ -115,12 +113,10 @@
numFolderColumns = fc;
minAllAppsPredictionColumns = maapc;
iconSize = is;
+ landscapeIconSize = lis;
iconTextSize = its;
numHotseatIcons = hs;
- hotseatIconSize = his;
defaultLayoutId = dlId;
-
- hotseatScale = hotseatIconSize / iconSize;
}
@TargetApi(23)
@@ -153,17 +149,15 @@
minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
iconSize = interpolatedDeviceProfileOut.iconSize;
+ landscapeIconSize = interpolatedDeviceProfileOut.landscapeIconSize;
iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
- hotseatIconSize = interpolatedDeviceProfileOut.hotseatIconSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, dm);
- hotseatScale = hotseatIconSize / iconSize;
-
Point realSize = new Point();
display.getRealSize(realSize);
// The real size never changes. smallSide and largeSide will remain the
@@ -211,9 +205,9 @@
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),
a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
- a.getFloat(R.styleable.InvariantDeviceProfile_hotseatIconSize, iconSize),
a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0)));
a.recycle();
}
@@ -305,19 +299,19 @@
private void add(InvariantDeviceProfile p) {
iconSize += p.iconSize;
+ landscapeIconSize += p.landscapeIconSize;
iconTextSize += p.iconTextSize;
- hotseatIconSize += p.hotseatIconSize;
}
private InvariantDeviceProfile multiply(float w) {
iconSize *= w;
+ landscapeIconSize *= w;
iconTextSize *= w;
- hotseatIconSize *= w;
return this;
}
public int getAllAppsButtonRank() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
throw new IllegalAccessError("Accessing all apps rank when all-apps is disabled");
}
return numHotseatIcons / 2;
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 0779a3d..11c5309 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -39,8 +39,10 @@
/**
* One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
- * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER}, or
- * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER},
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET} or
+ * {@link LauncherSettings.Favorites#ITEM_TYPE_CUSTOM_APPWIDGET}.
*/
public int itemType;
@@ -53,7 +55,9 @@
public long container = NO_ID;
/**
- * Indicates the screen in which the shortcut appears.
+ * Indicates the screen in which the shortcut appears if the container types is
+ * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. (i.e., ignore if the container type is
+ * {@link LauncherSettings.Favorites#CONTAINER_HOTSEAT})
*/
public long screenId = -1;
@@ -178,15 +182,12 @@
protected String dumpProperties() {
return "id=" + id
- + " type=" + itemType
- + " container=" + container
+ + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
+ + " container=" + LauncherSettings.Favorites.containerToString((int)container)
+ " screen=" + screenId
- + " cellX=" + cellX
- + " cellY=" + cellY
- + " spanX=" + spanX
- + " spanY=" + spanY
- + " minSpanX=" + minSpanX
- + " minSpanY=" + minSpanY
+ + " cell(" + cellX + "," + cellY + ")"
+ + " span(" + spanX + "," + spanY + ")"
+ + " minSpan(" + minSpanX + "," + minSpanY + ")"
+ " rank=" + rank
+ " user=" + user
+ " title=" + title;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ec345a7..26c5c9d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -18,7 +18,9 @@
import android.Manifest;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
@@ -65,6 +67,7 @@
import android.view.KeyEvent;
import android.view.KeyboardShortcutGroup;
import android.view.KeyboardShortcutInfo;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
@@ -76,27 +79,26 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.OvershootInterpolator;
import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.anim.AnimationLayerSet;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
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.dragndrop.PinItemDragListener;
import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.CustomActionsPopup;
@@ -123,7 +125,9 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
+import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.TestingUtils;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -147,7 +151,8 @@
public class Launcher extends BaseActivity
implements LauncherExterns, View.OnClickListener, OnLongClickListener,
LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
- AccessibilityManager.AccessibilityStateChangeListener {
+ AccessibilityManager.AccessibilityStateChangeListener,
+ WallpaperColorInfo.OnThemeChangeListener {
public static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -157,14 +162,15 @@
private static final int REQUEST_CREATE_SHORTCUT = 1;
private static final int REQUEST_CREATE_APPWIDGET = 5;
+
private static final int REQUEST_PICK_APPWIDGET = 9;
private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
- private static final int REQUEST_BIND_PENDING_APPWIDGET = 14;
- private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
+ private static final int REQUEST_BIND_PENDING_APPWIDGET = 12;
+ private static final int REQUEST_RECONFIGURE_APPWIDGET = 13;
- private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
+ private static final int REQUEST_PERMISSION_CALL_PHONE = 14;
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
@@ -209,22 +215,23 @@
private static final int ACTIVITY_START_DELAY = 1000;
// How long to wait before the new-shortcut animation automatically pans the workspace
- private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
- private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
- @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
+ private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+ private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
+ @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
+
+ private final ExtractedColors mExtractedColors = new ExtractedColors();
@Thunk Workspace mWorkspace;
private View mLauncherView;
@Thunk DragLayer mDragLayer;
private DragController mDragController;
- private View mQsbContainer;
public View mWeightWatcher;
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
- private int[] mTmpAddItemCellCoordinates = new int[2];
+ private final int[] mTmpAddItemCellCoordinates = new int[2];
@Thunk Hotseat mHotseat;
private ViewGroup mOverviewPanel;
@@ -254,19 +261,20 @@
private boolean mPaused = true;
private boolean mOnResumeNeedsLoad;
- private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
- private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
+ private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
+ private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
private ModelWriter mModelWriter;
private IconCache mIconCache;
- private ExtractedColors mExtractedColors;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
- private Handler mHandler = new Handler();
+ private final Handler mHandler = new Handler();
private boolean mIsResumeFromActionScreenOff;
private boolean mHasFocus = false;
+ private ObjectAnimator mScrimAnimator;
+
private PopupDataProvider mPopupDataProvider;
private View.OnTouchListener mHapticFeedbackTouchListener;
@@ -275,7 +283,7 @@
// match the sensor state.
private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
- private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
+ private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
@@ -288,8 +296,8 @@
// the press state and keep this reference to reset the press state when we return to launcher.
private BubbleTextView mWaitingForResume;
- protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
- new HashMap<String, CustomAppWidget>();
+ protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets =
+ new HashMap<>();
static {
if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
@@ -302,7 +310,7 @@
// simply unregister this runnable.
private Runnable mExitSpringLoadedModeRunnable;
- @Thunk Runnable mBuildLayersRunnable = new Runnable() {
+ @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
public void run() {
if (mWorkspace != null) {
mWorkspace.buildPageHardwareLayers();
@@ -358,6 +366,10 @@
mLauncherCallbacks.preOnCreate();
}
+ WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
+ wallpaperColorInfo.setOnThemeChangeListener(this);
+ overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
+
super.onCreate(savedInstanceState);
LauncherAppState app = LauncherAppState.getInstance(this);
@@ -392,11 +404,10 @@
// LauncherModel load.
mPaused = false;
- mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
+ mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
setupViews();
mDeviceProfile.layout(this, false /* notifyListeners */);
- mExtractedColors = new ExtractedColors();
loadExtractedColorsAndColorItems();
mPopupDataProvider = new PopupDataProvider(this);
@@ -458,6 +469,22 @@
// Listen for broadcasts screen off
registerReceiver(mReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+ getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+ Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+ }
+
+ @Override
+ public void onThemeChanged() {
+ recreate();
+ }
+
+ protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
+ if (isDark) {
+ setTheme(R.style.LauncherThemeDark);
+ } else if (supportsDarkText) {
+ setTheme(R.style.LauncherThemeDarkText);
+ }
}
@Override
@@ -468,6 +495,11 @@
@Override
public void onExtractedColorsChanged() {
loadExtractedColorsAndColorItems();
+ mExtractedColors.notifyChange();
+ }
+
+ public ExtractedColors getExtractedColors() {
+ return mExtractedColors;
}
@Override
@@ -483,46 +515,6 @@
mExtractedColors.load(this);
mHotseat.updateColor(mExtractedColors, !mPaused);
mWorkspace.getPageIndicator().updateColor(mExtractedColors);
- boolean lightStatusBar = (FeatureFlags.LIGHT_STATUS_BAR
- && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
- ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
- // It's possible that All Apps is visible when this is run,
- // so always use light status bar in that case. Only change nav bar color to status bar
- // color when All Apps is visible.
- activateLightSystemBars(lightStatusBar || isAllAppsVisible(), true, isAllAppsVisible());
- }
- }
-
- // TODO: use platform flag on API >= 26
- private static final int SYSTEM_UI_FLAG_LIGHT_NAV_BAR = 0x10;
-
- /**
- * Sets the status and/or nav bar to be light or not. Light status bar means dark icons.
- * @param isLight make sure the system bar is light.
- * @param statusBar if true, make the status bar theme match the isLight param.
- * @param navBar if true, make the nav bar theme match the isLight param.
- */
- public void activateLightSystemBars(boolean isLight, boolean statusBar, boolean navBar) {
- int oldSystemUiFlags = getWindow().getDecorView().getSystemUiVisibility();
- int newSystemUiFlags = oldSystemUiFlags;
- if (isLight) {
- if (statusBar) {
- newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- }
- if (navBar && Utilities.isAtLeastO()) {
- newSystemUiFlags |= SYSTEM_UI_FLAG_LIGHT_NAV_BAR;
- }
- } else {
- if (statusBar) {
- newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
- }
- if (navBar && Utilities.isAtLeastO()) {
- newSystemUiFlags &= ~(SYSTEM_UI_FLAG_LIGHT_NAV_BAR);
- }
- }
-
- if (newSystemUiFlags != oldSystemUiFlags) {
- getWindow().getDecorView().setSystemUiVisibility(newSystemUiFlags);
}
}
@@ -552,47 +544,6 @@
public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
mLauncherCallbacks = callbacks;
- mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() {
- private boolean mWorkspaceImportanceStored = false;
- private boolean mHotseatImportanceStored = false;
- private int mWorkspaceImportanceForAccessibility =
- View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
- private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
-
- @Override
- public void onSearchOverlayOpened() {
- if (mWorkspaceImportanceStored || mHotseatImportanceStored) {
- return;
- }
- // The underlying workspace and hotseat are temporarily suppressed by the search
- // overlay. So they shouldn't be accessible.
- if (mWorkspace != null) {
- mWorkspaceImportanceForAccessibility =
- mWorkspace.getImportantForAccessibility();
- mWorkspace.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- mWorkspaceImportanceStored = true;
- }
- if (mHotseat != null) {
- mHotseatImportanceForAccessibility = mHotseat.getImportantForAccessibility();
- mHotseat.setImportantForAccessibility(
- View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
- mHotseatImportanceStored = true;
- }
- }
-
- @Override
- public void onSearchOverlayClosed() {
- if (mWorkspaceImportanceStored && mWorkspace != null) {
- mWorkspace.setImportantForAccessibility(mWorkspaceImportanceForAccessibility);
- }
- if (mHotseatImportanceStored && mHotseat != null) {
- mHotseat.setImportantForAccessibility(mHotseatImportanceForAccessibility);
- }
- mWorkspaceImportanceStored = false;
- mHotseatImportanceStored = false;
- }
- });
return true;
}
@@ -954,6 +905,24 @@
if (!isWorkspaceLoading()) {
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
}
+
+ if (mIsResumeFromActionScreenOff && mDragLayer.getBackground() != null) {
+ if (mScrimAnimator != null) {
+ mScrimAnimator.cancel();
+ }
+ mDragLayer.getBackground().setAlpha(0);
+ mScrimAnimator = ObjectAnimator.ofInt(mDragLayer.getBackground(),
+ LauncherAnimUtils.DRAWABLE_ALPHA, 0, 255);
+ mScrimAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrimAnimator = null;
+ }
+ });
+ mScrimAnimator.setDuration(600);
+ mScrimAnimator.setStartDelay(getWindow().getTransitionBackgroundFadeDuration());
+ mScrimAnimator.start();
+ }
}
@Override
@@ -979,8 +948,7 @@
// Don't update the predicted apps if the user is returning to launcher in the apps
// view after launching an app, as they may be depending on the UI to be static to
// switch to another app, otherwise, if it was
- showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
- mAppsView.shouldRestoreImeState() /* focusSearchBar */);
+ showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
@@ -1053,13 +1021,12 @@
updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
mWorkspace.onResume();
- if (!isWorkspaceLoading()) {
- // Process any items that were added while Launcher was away.
- InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+ // Process any items that were added while Launcher was away.
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED, this);
- // Refresh shortcuts if the permission changed.
- mModel.refreshShortcutsIfRequired();
- }
+ // Refresh shortcuts if the permission changed.
+ mModel.refreshShortcutsIfRequired();
if (shouldShowDiscoveryBounce()) {
mAllAppsController.showDiscoveryBounce();
@@ -1074,7 +1041,7 @@
@Override
protected void onPause() {
// Ensure that items added to Launcher are queued until Launcher returns
- InstallShortcutReceiver.enableInstallQueue();
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_ACTIVITY_PAUSED);
super.onPause();
mPaused = true;
@@ -1095,13 +1062,13 @@
public interface CustomContentCallbacks {
// Custom content is completely shown. {@code fromResume} indicates whether this was caused
// by a onResume or by scrolling otherwise.
- public void onShow(boolean fromResume);
+ void onShow(boolean fromResume);
// Custom content is completely hidden
- public void onHide();
+ void onHide();
// Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
- public void onScrollProgressChanged(float progress);
+ void onScrollProgressChanged(float progress);
// Indicates whether the user is allowed to scroll away from the custom content.
boolean isScrollingAllowed();
@@ -1112,40 +1079,28 @@
/**
* Touch interaction leading to overscroll has begun
*/
- public void onScrollInteractionBegin();
+ void onScrollInteractionBegin();
/**
* Touch interaction related to overscroll has ended
*/
- public void onScrollInteractionEnd();
+ void onScrollInteractionEnd();
/**
* Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
* screen (or in the case of RTL, the rightmost screen).
*/
- public void onScrollChange(float progress, boolean rtl);
+ void onScrollChange(float progress, boolean rtl);
/**
* Called when the launcher is ready to use the overlay
* @param callbacks A set of callbacks provided by Launcher in relation to the overlay
*/
- public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
- }
-
- public interface LauncherSearchCallbacks {
- /**
- * Called when the search overlay is shown.
- */
- public void onSearchOverlayOpened();
-
- /**
- * Called when the search overlay is dismissed.
- */
- public void onSearchOverlayClosed();
+ void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
}
public interface LauncherOverlayCallbacks {
- public void onScrollChanged(float progress);
+ void onScrollChanged(float progress);
}
class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1299,9 +1254,7 @@
private void setupViews() {
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mFocusHandler = mDragLayer.getFocusIndicatorHelper();
- mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
- mQsbContainer = mDragLayer.findViewById(mDeviceProfile.isVerticalBarLayout()
- ? R.id.workspace_blocked_row : R.id.qsb_container);
+ mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1331,16 +1284,11 @@
mDragController.addDragListener(mWorkspace);
// Get the search/delete/uninstall bar
- mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
+ mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
- if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
- mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
- } else {
- mAppsView.setSearchBarController(new DefaultAppSearchController());
- }
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
@@ -1431,34 +1379,33 @@
* @return A View inflated from layoutResId.
*/
public View createShortcut(ViewGroup parent, ShortcutInfo info) {
- BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
- parent, false);
+ BubbleTextView favorite = (BubbleTextView) LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.app_icon, parent, false);
favorite.applyFromShortcutInfo(info);
- favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
favorite.setOnClickListener(this);
favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
/**
- * Add a shortcut to the workspace.
+ * Add a shortcut to the workspace or to a Folder.
*
* @param data The intent describing the shortcut.
*/
private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
int cellY, PendingRequestArgs args) {
- int[] cellXY = mTmpAddItemCellCoordinates;
- CellLayout layout = getCellLayout(container, screenId);
-
- if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
- args.getPendingIntent().getComponent() == null) {
+ if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
+ || args.getPendingIntent().getComponent() == null) {
return;
}
+ int[] cellXY = mTmpAddItemCellCoordinates;
+ CellLayout layout = getCellLayout(container, screenId);
+
ShortcutInfo info = null;
if (Utilities.isAtLeastO()) {
- info = LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
- this, PinItemRequestCompat.getPinItemRequest(data), 0);
+ info = LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
+ this, LauncherAppsCompatVO.getPinItemRequest(data), 0);
}
if (info == null) {
@@ -1477,36 +1424,55 @@
}
}
- final View view = createShortcut(info);
- boolean foundCellSpan = false;
- // First we check if we already know the exact location where we want to add this item.
- if (cellX >= 0 && cellY >= 0) {
- cellXY[0] = cellX;
- cellXY[1] = cellY;
- foundCellSpan = true;
+ if (container < 0) {
+ // Adding a shortcut to the Workspace.
+ final View view = createShortcut(info);
+ boolean foundCellSpan = false;
+ // First we check if we already know the exact location where we want to add this item.
+ if (cellX >= 0 && cellY >= 0) {
+ cellXY[0] = cellX;
+ cellXY[1] = cellY;
+ foundCellSpan = true;
- // If appropriate, either create a folder or add to an existing folder
- if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
- true, null,null)) {
+ // If appropriate, either create a folder or add to an existing folder
+ if (mWorkspace.createUserFolderIfNecessary(view, container, layout, cellXY, 0,
+ true, null, null)) {
+ return;
+ }
+ DragObject dragObject = new DragObject();
+ dragObject.dragInfo = info;
+ if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
+ true)) {
+ return;
+ }
+ } else {
+ foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
+ }
+
+ if (!foundCellSpan) {
+ mWorkspace.onNoCellFound(layout);
return;
}
- DragObject dragObject = new DragObject();
- dragObject.dragInfo = info;
- if (mWorkspace.addToExistingFolderIfNecessary(view, layout, cellXY, 0, dragObject,
- true)) {
- return;
- }
+
+ getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
+ mWorkspace.addInScreen(view, info);
} else {
- foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
- }
+ // Adding a shortcut to a Folder.
+ final long folderIconId = container;
+ FolderIcon folderIcon = (FolderIcon) mWorkspace.getFirstMatch(new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ return info != null && info.id == folderIconId;
+ }
+ });
- if (!foundCellSpan) {
- mWorkspace.onNoCellFound(layout);
- return;
+ if (folderIcon != null) {
+ FolderInfo folderInfo = (FolderInfo) folderIcon.getTag();
+ folderInfo.add(info, args.rank, false);
+ } else {
+ Log.e(TAG, "Could not find folder with id " + folderIconId + " to add shortcut.");
+ }
}
-
- getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
- mWorkspace.addInScreen(view, info);
}
/**
@@ -1640,7 +1606,6 @@
}
}
});
- return;
}
});
}
@@ -1664,10 +1629,6 @@
return mWorkspace;
}
- public View getQsbContainer() {
- return mQsbContainer;
- }
-
public Hotseat getHotseat() {
return mHotseat;
}
@@ -1759,7 +1720,7 @@
// Reset the apps view
if (!alreadyOnHome && mAppsView != null) {
- mAppsView.scrollToTop();
+ mAppsView.reset();
}
// Reset the widgets view
@@ -1781,8 +1742,9 @@
// as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
// animation.
if (isActionMain) {
- boolean callbackAllowsMoveToDefaultScreen = mLauncherCallbacks != null ?
- mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
+ boolean callbackAllowsMoveToDefaultScreen =
+ mLauncherCallbacks == null || mLauncherCallbacks
+ .shouldMoveToDefaultScreenOnHomeIntent();
if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
&& callbackAllowsMoveToDefaultScreen) {
@@ -1871,6 +1833,8 @@
((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
.removeAccessibilityStateChangeListener(this);
+ WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
+
LauncherAnimUtils.onDestroyActivity();
if (mLauncherCallbacks != null) {
@@ -2354,18 +2318,7 @@
if (!isAppsViewVisible()) {
getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.ALL_APPS_BUTTON);
- showAppsView(true /* animated */, true /* updatePredictedApps */,
- false /* focusSearchBar */);
- }
- }
-
- protected void onLongClickAllAppsButton(View v) {
- if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
- if (!isAppsViewVisible()) {
- getUserEventDispatcher().logActionOnControl(Action.Touch.LONGPRESS,
- ControlType.ALL_APPS_BUTTON);
- showAppsView(true /* animated */,
- true /* updatePredictedApps */, true /* focusSearchBar */);
+ showAppsView(true /* animated */, true /* updatePredictedApps */);
}
}
@@ -2462,7 +2415,13 @@
private void startAppShortcutOrInfoActivity(View v) {
ItemInfo item = (ItemInfo) v.getTag();
- Intent intent = item.getIntent();
+ Intent intent;
+ if (item instanceof PromiseAppInfo) {
+ PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
+ intent = promiseAppInfo.getMarketIntent();
+ } else {
+ intent = item.getIntent();
+ }
if (intent == null) {
throw new IllegalArgumentException("Input must have a valid intent");
}
@@ -2523,8 +2482,8 @@
.putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
String pickerPackage = getString(R.string.wallpaper_picker_package);
- boolean hasTargetPackage = TextUtils.isEmpty(pickerPackage);
- if (!hasTargetPackage) {
+ boolean hasTargetPackage = !TextUtils.isEmpty(pickerPackage);
+ if (hasTargetPackage) {
intent.setPackage(pickerPackage);
}
@@ -2666,9 +2625,9 @@
if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
- if (v instanceof TextView) {
+ if (v instanceof BubbleTextView) {
// Launch from center of icon, not entire view
- Drawable icon = Workspace.getTextViewIcon((TextView) v);
+ Drawable icon = ((BubbleTextView) v).getIcon();
if (icon != null) {
Rect bounds = icon.getBounds();
left = (width - bounds.width()) / 2;
@@ -2747,13 +2706,6 @@
if (isWorkspaceLocked()) return false;
if (mState != State.WORKSPACE) return false;
- if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
- (v == mAllAppsButton && mAllAppsButton != null)) {
- onLongClickAllAppsButton(v);
- return true;
- }
-
-
boolean ignoreLongPressToOverview =
mDeviceProfile.shouldIgnoreLongPressToOverview(mLastDispatchTouchEventX);
@@ -2953,13 +2905,12 @@
/**
* Shows the apps view.
*/
- public void showAppsView(boolean animated, boolean updatePredictedApps,
- boolean focusSearchBar) {
+ public void showAppsView(boolean animated, boolean updatePredictedApps) {
markAppsViewShown();
if (updatePredictedApps) {
tryAndUpdatePredictedApps();
}
- showAppsOrWidgets(State.APPS, animated, focusSearchBar);
+ showAppsOrWidgets(State.APPS, animated);
}
/**
@@ -2970,7 +2921,7 @@
if (resetPageToZero) {
mWidgetsView.scrollToTop();
}
- showAppsOrWidgets(State.WIDGETS, animated, false);
+ showAppsOrWidgets(State.WIDGETS, animated);
mWidgetsView.post(new Runnable() {
@Override
@@ -2987,7 +2938,7 @@
*/
// TODO: calling method should use the return value so that when {@code false} is returned
// the workspace transition doesn't fall into invalid state.
- private boolean showAppsOrWidgets(State toState, boolean animated, boolean focusSearchBar) {
+ private boolean showAppsOrWidgets(State toState, boolean animated) {
if (!(mState == State.WORKSPACE ||
mState == State.APPS_SPRING_LOADED ||
mState == State.WIDGETS_SPRING_LOADED ||
@@ -3005,7 +2956,7 @@
}
if (toState == State.APPS) {
- mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
+ mStateTransitionAnimation.startAnimationToAllApps(animated);
} else {
mStateTransitionAnimation.startAnimationToWidgets(animated);
}
@@ -3078,8 +3029,7 @@
public void exitSpringLoadedDragMode() {
if (mState == State.APPS_SPRING_LOADED) {
- showAppsView(true /* animated */,
- false /* updatePredictedApps */, false /* focusSearchBar */);
+ showAppsView(true /* animated */, false /* updatePredictedApps */);
} else if (mState == State.WIDGETS_SPRING_LOADED) {
showWidgetsView(true, false);
} else if (mState == State.WORKSPACE_SPRING_LOADED) {
@@ -3245,7 +3195,7 @@
orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
- mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+ LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
} else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
// If there are no screens, we need to have an empty screen
mWorkspace.addExtraEmptyScreen();
@@ -3332,7 +3282,7 @@
// 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<Animator>();
+ final Collection<Animator> bounceAnims = new ArrayList<>();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
Workspace workspace = mWorkspace;
long newItemsScreenId = -1;
@@ -3390,7 +3340,7 @@
Object tag = v.getTag();
String desc = "Collision while binding workspace item: " + item
+ ". Collides with " + tag;
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
@@ -3593,6 +3543,9 @@
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
info.restoreStatus = finalRestoreFlag;
+ if (info.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ info.pendingItemInfo = null;
+ }
mWorkspace.reinflateWidgetsIfNecessary();
getModelWriter().updateItemInDatabase(info);
@@ -3671,7 +3624,8 @@
mPendingActivityResult = null;
}
- InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_LOADER_RUNNING, this);
NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
@@ -3712,7 +3666,7 @@
* multiple calls to bind the same list.)
*/
@Thunk ArrayList<AppInfo> mTmpAppsList;
- private Runnable mBindAllApplicationsRunnable = new Runnable() {
+ private final Runnable mBindAllApplicationsRunnable = new Runnable() {
public void run() {
bindAllApplications(mTmpAppsList);
mTmpAppsList = null;
@@ -3768,6 +3722,22 @@
}
@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);
+ }
+ }
+
+ @Override
public void bindWidgetsRestored(final ArrayList<LauncherAppWidgetInfo> widgets) {
Runnable r = new Runnable() {
public void run() {
@@ -3900,7 +3870,7 @@
}
}
- private Runnable mBindAllWidgetsRunnable = new Runnable() {
+ private final Runnable mBindAllWidgetsRunnable = new Runnable() {
public void run() {
bindAllWidgets(mAllWidgets);
}
@@ -3940,7 +3910,7 @@
* refreshes the widgets and shortcuts associated with the given package/user
*/
public void refreshAndBindWidgetsForPackageUser(@Nullable PackageUserKey packageUser) {
- mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty(), packageUser);
+ mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
}
public void lockScreenOrientation() {
@@ -3971,7 +3941,7 @@
}
private boolean shouldShowDiscoveryBounce() {
- if (mState != mState.WORKSPACE) {
+ if (mState != State.WORKSPACE) {
return false;
}
if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
@@ -3980,10 +3950,7 @@
if (!mIsResumeFromActionScreenOff) {
return false;
}
- if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
- return false;
- }
- return true;
+ return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
}
protected void moveWorkspaceToDefaultScreen() {
@@ -4072,7 +4039,7 @@
switch (keyCode) {
case KeyEvent.KEYCODE_A:
if (mState == State.WORKSPACE) {
- showAppsView(true, true, false);
+ showAppsView(true, true);
return true;
}
break;
@@ -4119,9 +4086,8 @@
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
- // Finish this instance of the activity. When the activity is recreated,
- // it will initialize the rotation preference again.
- finish();
+ // Recreate the activity so that it initializes the rotation preference again.
+ recreate();
}
}
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index aa7f5ee..cfb9b57 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -21,11 +21,10 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
+import android.graphics.drawable.Drawable;
import android.util.Property;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewTreeObserver;
-import android.widget.ViewAnimator;
import java.util.HashSet;
import java.util.WeakHashMap;
@@ -130,4 +129,16 @@
return anim;
}
+ public static final Property<Drawable, Integer> DRAWABLE_ALPHA =
+ new Property<Drawable, Integer>(Integer.TYPE, "drawableAlpha") {
+ @Override
+ public Integer get(Drawable drawable) {
+ return drawable.getAlpha();
+ }
+
+ @Override
+ public void set(Drawable drawable, Integer alpha) {
+ drawable.setAlpha(alpha);
+ }
+ };
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 180c202..cf20feb 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -26,7 +26,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dynamicui.ExtractionUtils;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.Preconditions;
@@ -37,7 +37,7 @@
public class LauncherAppState {
- public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
+ public static final boolean PROFILE_STARTUP = FeatureFlags.IS_DOGFOOD_BUILD;
// We do not need any synchronization for this variable as its only written on UI thread.
private static LauncherAppState INSTANCE;
@@ -93,9 +93,7 @@
mInvariantDeviceProfile = new InvariantDeviceProfile(mContext);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
-
- mModel = new LauncherModel(this, mIconCache,
- Utilities.getOverrideObject(AppFilter.class, mContext, R.string.app_filter_class));
+ mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 13cc7ba..c7b7782 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -23,7 +23,6 @@
import android.graphics.Rect;
import android.os.Handler;
import android.os.SystemClock;
-import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -40,9 +39,7 @@
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
-import java.lang.reflect.Method;
import java.util.ArrayList;
-import java.util.concurrent.Executor;
/**
* {@inheritDoc}
@@ -50,8 +47,6 @@
public class LauncherAppWidgetHostView extends AppWidgetHostView
implements TouchCompleteListener, View.OnLongClickListener {
- private static final String TAG = "LauncherWidgetHostView";
-
// Related to the auto-advancing of widgets
private static final long ADVANCE_INTERVAL = 20000;
private static final long ADVANCE_STAGGER = 250;
@@ -98,13 +93,7 @@
setBackgroundResource(R.drawable.widget_internal_focus_bg);
if (Utilities.isAtLeastO()) {
- try {
- Method asyncMethod = AppWidgetHostView.class
- .getMethod("setExecutor", Executor.class);
- asyncMethod.invoke(this, Utilities.THREAD_POOL_EXECUTOR);
- } catch (Exception e) {
- Log.e(TAG, "Unable to set async executor", e);
- }
+ setExecutor(Utilities.THREAD_POOL_EXECUTOR);
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 1e0f285..6f23e56 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.os.Process;
+import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ContentWriter;
/**
@@ -95,6 +96,11 @@
*/
public Intent bindOptions;
+ /**
+ * Nonnull for pending widgets. We use this to get the icon and title for the widget.
+ */
+ public PackageItemInfo pendingItemInfo;
+
private boolean mHasNotifiedInitialWidgetSizeChanged;
public LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 2bac11f..d66b14c 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -17,13 +17,10 @@
package com.android.launcher3;
import android.content.Intent;
-import android.graphics.Rect;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
-import com.android.launcher3.allapps.AllAppsSearchBarController;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.util.ComponentKey;
import java.io.FileDescriptor;
@@ -44,69 +41,60 @@
* Activity life-cycle methods. These methods are triggered after
* the code in the corresponding Launcher method is executed.
*/
- public void preOnCreate();
- public void onCreate(Bundle savedInstanceState);
- public void preOnResume();
- public void onResume();
- public void onStart();
- public void onStop();
- public void onPause();
- public void onDestroy();
- public void onSaveInstanceState(Bundle outState);
- public void onPostCreate(Bundle savedInstanceState);
- public void onNewIntent(Intent intent);
- public void onActivityResult(int requestCode, int resultCode, Intent data);
- public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ void preOnCreate();
+ void onCreate(Bundle savedInstanceState);
+ void preOnResume();
+ void onResume();
+ void onStart();
+ void onStop();
+ void onPause();
+ void onDestroy();
+ void onSaveInstanceState(Bundle outState);
+ void onPostCreate(Bundle savedInstanceState);
+ void onNewIntent(Intent intent);
+ void onActivityResult(int requestCode, int resultCode, Intent data);
+ void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults);
- public void onWindowFocusChanged(boolean hasFocus);
- public void onAttachedToWindow();
- public void onDetachedFromWindow();
- public boolean onPrepareOptionsMenu(Menu menu);
- public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
- public void onHomeIntent();
- public boolean handleBackPressed();
- public void onTrimMemory(int level);
+ 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();
+ void onTrimMemory(int level);
/*
* Extension points for providing custom behavior on certain user interactions.
*/
- public void onLauncherProviderChange();
- public void finishBindingItems(final boolean upgradePath);
- public void bindAllApplications(ArrayList<AppInfo> apps);
- public void onInteractionBegin();
- public void onInteractionEnd();
+ void onLauncherProviderChange();
+ void finishBindingItems(final boolean upgradePath);
+ void bindAllApplications(ArrayList<AppInfo> apps);
+ void onInteractionBegin();
+ void onInteractionEnd();
@Deprecated
- public void onWorkspaceLockedChanged();
+ void onWorkspaceLockedChanged();
/**
* Starts a search with {@param initialQuery}. Return false if search was not started.
*/
- public boolean startSearch(
+ boolean startSearch(
String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
- public boolean hasCustomContentToLeft();
- public void populateCustomContentContainer();
- public View getQsbBar();
- public Bundle getAdditionalSearchWidgetOptions();
+ boolean hasCustomContentToLeft();
+ void populateCustomContentContainer();
+ View getQsbBar();
+ Bundle getAdditionalSearchWidgetOptions();
/*
* Extensions points for adding / replacing some other aspects of the Launcher experience.
*/
- public boolean shouldMoveToDefaultScreenOnHomeIntent();
- public boolean hasSettings();
- public AllAppsSearchBarController getAllAppsSearchBarController();
- public List<ComponentKey> getPredictedApps();
- public static final int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
+ boolean shouldMoveToDefaultScreenOnHomeIntent();
+ boolean hasSettings();
+ List<ComponentKey> getPredictedApps();
+ int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
/** Must return one of {@link #SEARCH_BAR_HEIGHT_NORMAL} or {@link #SEARCH_BAR_HEIGHT_TALL} */
- public int getSearchBarHeight();
+ int getSearchBarHeight();
- /**
- * Sets the callbacks to allow reacting the actions of search overlays of the launcher.
- *
- * @param callbacks A set of callbacks to the Launcher, is actually a LauncherSearchCallback,
- * but for implementation purposes is passed around as an object.
- */
- public void setLauncherSearchCallback(Object callbacks);
-
- public boolean shouldShowDiscoveryBounce();
+ boolean shouldShowDiscoveryBounce();
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index f881b38..82bee0e 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentProviderOperation;
@@ -24,57 +23,40 @@
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.LauncherActivityInfo;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
-import android.os.SystemClock;
-import android.os.Trace;
import android.os.UserHandle;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
-import android.util.LongSparseArray;
-import android.util.MutableInt;
+import android.util.Pair;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
-import com.android.launcher3.folder.Folder;
-import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.graphics.LauncherIcons;
-import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.AddWorkspaceItemsTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.CacheDataUpdatedTask;
-import com.android.launcher3.model.ExtendedModelTask;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.model.LoaderCursor;
+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.SdCardAvailableReceiver;
import com.android.launcher3.model.ShortcutsChangedTask;
import com.android.launcher3.model.UserLockStateChangedTask;
import com.android.launcher3.model.WidgetItem;
-import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.provider.ImportDataTask;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.MultiHashMap;
-import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Provider;
@@ -85,14 +67,9 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
@@ -103,20 +80,16 @@
*/
public class LauncherModel extends BroadcastReceiver
implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
- static final boolean DEBUG_LOADERS = false;
private static final boolean DEBUG_RECEIVER = false;
static final String TAG = "Launcher.Model";
- private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
- private static final long INVALID_SCREEN_ID = -1L;
-
+ private final MainThreadExecutor mUiExecutor = new MainThreadExecutor();
@Thunk final LauncherAppState mApp;
@Thunk final Object mLock = new Object();
- @Thunk DeferredHandler mHandler = new DeferredHandler();
- @Thunk LoaderTask mLoaderTask;
+ @Thunk
+ LoaderTask mLoaderTask;
@Thunk boolean mIsLoaderTaskRunning;
- @Thunk boolean mHasLoaderCompletedOnce;
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
@@ -135,33 +108,10 @@
}
}
- /**
- * Set of runnables to be called on the background thread after the workspace binding
- * is complete.
- */
- static final ArrayList<Runnable> mBindCompleteRunnables = new ArrayList<Runnable>();
-
@Thunk WeakReference<Callbacks> mCallbacks;
// < only access in worker thread >
private final AllAppsList mBgAllAppsList;
- // Entire list of widgets.
- private final WidgetsModel mBgWidgetsModel;
-
- private boolean mHasShortcutHostPermission;
- // Runnable to check if the shortcuts permission has changed.
- private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
- @Override
- public void run() {
- if (mModelLoaded) {
- boolean hasShortcutHostPermission =
- DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
- if (hasShortcutHostPermission != mHasShortcutHostPermission) {
- forceReload();
- }
- }
- }
- };
/**
* All the static data should be accessed on the background thread, A lock should be acquired
@@ -169,12 +119,19 @@
*/
static final BgDataModel sBgDataModel = new BgDataModel();
- // </ only access in worker thread >
-
- private final IconCache mIconCache;
-
- private final LauncherAppsCompat mLauncherApps;
- private final UserManagerCompat mUserManager;
+ // Runnable to check if the shortcuts permission has changed.
+ private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mModelLoaded) {
+ boolean hasShortcutHostPermission =
+ DeepShortcutManager.getInstance(mApp.getContext()).hasHostPermission();
+ if (hasShortcutHostPermission != sBgDataModel.hasShortcutHostPermission) {
+ forceReload();
+ }
+ }
+ }
+ };
public interface Callbacks {
public boolean setLoadOnResume();
@@ -193,6 +150,7 @@
ArrayList<ItemInfo> addAnimated,
ArrayList<AppInfo> addedApps);
public void bindAppsUpdated(ArrayList<AppInfo> apps);
+ public void bindPromiseAppProgressUpdated(PromiseAppInfo app);
public void bindShortcutsChanged(ArrayList<ShortcutInfo> updated,
ArrayList<ShortcutInfo> removed, UserHandle user);
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
@@ -209,25 +167,8 @@
}
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
- Context context = app.getContext();
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
- mBgWidgetsModel = new WidgetsModel(iconCache, appFilter);
- mIconCache = iconCache;
-
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- mUserManager = UserManagerCompat.getInstance(context);
- }
-
- /** Runs the specified runnable immediately if called from the main thread, otherwise it is
- * posted on the main thread handler. */
- private void runOnMainThread(Runnable r) {
- if (sWorkerThread.getThreadId() == Process.myTid()) {
- // If we are on the worker thread, post onto the main handler
- mHandler.post(r);
- } else {
- r.run();
- }
}
/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
@@ -258,15 +199,8 @@
/**
* Adds the provided items to the workspace.
*/
- public void addAndBindAddedWorkspaceItems(List<ItemInfo> workspaceApps) {
- addAndBindAddedWorkspaceItems(Provider.of(workspaceApps));
- }
-
- /**
- * Adds the provided items to the workspace.
- */
public void addAndBindAddedWorkspaceItems(
- Provider<List<ItemInfo>> appsProvider) {
+ Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
}
@@ -379,8 +313,6 @@
public void initialize(Callbacks callbacks) {
synchronized (mLock) {
Preconditions.assertUIThread();
- // Remove any queued UI runnables
- mHandler.cancelAll();
mCallbacks = new WeakReference<>(callbacks);
}
}
@@ -492,7 +424,7 @@
public void forceReload() {
synchronized (mLock) {
// Stop any existing loaders first, so they don't set mModelLoaded to true later
- stopLoaderLocked();
+ stopLoader();
mModelLoaded = false;
}
@@ -518,16 +450,6 @@
}
}
- /**
- * If there is already a loader task running, tell it to stop.
- */
- private void stopLoaderLocked() {
- LoaderTask oldTask = mLoaderTask;
- if (oldTask != null) {
- oldTask.stopLocked();
- }
- }
-
public boolean isCurrentCallbacks(Callbacks callbacks) {
return (mCallbacks != null && mCallbacks.get() == callbacks);
}
@@ -538,42 +460,61 @@
*/
public boolean startLoader(int synchronousBindPage) {
// Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
- InstallShortcutReceiver.enableInstallQueue();
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
synchronized (mLock) {
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
final Callbacks oldCallbacks = mCallbacks.get();
// Clear any pending bind-runnables from the synchronized load process.
- runOnMainThread(new Runnable() {
- public void run() {
- oldCallbacks.clearPendingBinds();
- }
- });
+ mUiExecutor.execute(new Runnable() {
+ public void run() {
+ oldCallbacks.clearPendingBinds();
+ }
+ });
// If there is already one running, tell it to stop.
- stopLoaderLocked();
- mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
- if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
- && mModelLoaded && !mIsLoaderTaskRunning) {
- mLoaderTask.runBindSynchronousPage(synchronousBindPage);
+ stopLoader();
+ LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
+ mBgAllAppsList, synchronousBindPage, mCallbacks);
+ if (mModelLoaded && !mIsLoaderTaskRunning) {
+ // Divide the set of loaded items into those that we are binding synchronously,
+ // and everything else that is to be bound normally (asynchronously).
+ loaderResults.bindWorkspace();
+ // For now, continue posting the binding of AllApps as there are other
+ // issues that arise from that.
+ loaderResults.bindAllApps();
+ loaderResults.bindDeepShortcuts();
+ loaderResults.bindWidgets();
return true;
} else {
- sWorkerThread.setPriority(Thread.NORM_PRIORITY);
- sWorker.post(mLoaderTask);
+ startLoaderForResults(loaderResults);
}
}
}
return false;
}
+ /**
+ * If there is already a loader task running, tell it to stop.
+ */
public void stopLoader() {
synchronized (mLock) {
- if (mLoaderTask != null) {
- mLoaderTask.stopLocked();
+ LoaderTask oldTask = mLoaderTask;
+ mLoaderTask = null;
+ if (oldTask != null) {
+ oldTask.stopLocked();
}
}
}
+ public void startLoaderForResults(LoaderResults results) {
+ synchronized (mLock) {
+ stopLoader();
+ mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
+ runOnWorkerThread(mLoaderTask);
+ }
+ }
+
/**
* Loads the workspace screen ids in an ordered list.
*/
@@ -586,1211 +527,61 @@
screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
}
- /**
- * Runnable for the thread that loads the contents of the launcher:
- * - workspace icons
- * - widgets
- * - all apps icons
- * - deep shortcuts within apps
- */
- private class LoaderTask implements Runnable {
- private Context mContext;
- private int mPageToBindFirst;
-
- @Thunk boolean mIsLoadingAndBindingWorkspace;
- private boolean mStopped;
- @Thunk boolean mLoadAndBindStepFinished;
-
- LoaderTask(Context context, int pageToBindFirst) {
- mContext = context;
- mPageToBindFirst = pageToBindFirst;
- }
-
- private void waitForIdle() {
- // Wait until the either we're stopped or the other threads are done.
- // This way we don't start loading all apps until the workspace has settled
- // down.
- synchronized (LoaderTask.this) {
- final long workspaceWaitTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
- mHandler.postIdle(new Runnable() {
- public void run() {
- synchronized (LoaderTask.this) {
- mLoadAndBindStepFinished = true;
- if (DEBUG_LOADERS) {
- Log.d(TAG, "done with previous binding step");
- }
- LoaderTask.this.notify();
- }
+ public void onInstallSessionCreated(final PackageInstallInfo sessionInfo) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
+ @Override
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ apps.addPromiseApp(app.getContext(), sessionInfo);
+ if (!apps.added.isEmpty()) {
+ final ArrayList<AppInfo> arrayList = new ArrayList<>(apps.added);
+ apps.added.clear();
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAppsAdded(null, null, null, arrayList);
}
});
-
- while (!mStopped && !mLoadAndBindStepFinished) {
- try {
- // Just in case mFlushingWorkerThread changes but we aren't woken up,
- // wait no longer than 1sec at a time
- this.wait(1000);
- } catch (InterruptedException ex) {
- // Ignore
- }
- }
- if (DEBUG_LOADERS) {
- Log.d(TAG, "waited "
- + (SystemClock.uptimeMillis()-workspaceWaitTime)
- + "ms for previous step to finish binding");
}
}
- }
+ });
+ }
- void runBindSynchronousPage(int synchronousBindPage) {
- if (synchronousBindPage == PagedView.INVALID_RESTORE_PAGE) {
- // Ensure that we have a valid page index to load synchronously
- throw new RuntimeException("Should not call runBindSynchronousPage() without " +
- "valid page index");
- }
- if (!mModelLoaded) {
- // Ensure that we don't try and bind a specified page when the pages have not been
- // loaded already (we should load everything asynchronously in that case)
- throw new RuntimeException("Expecting AllApps and Workspace to be loaded");
- }
+ public class LoaderTransaction implements AutoCloseable {
+
+ private final LoaderTask mTask;
+
+ private LoaderTransaction(LoaderTask task) throws CancellationException {
synchronized (mLock) {
- if (mIsLoaderTaskRunning) {
- // Ensure that we are never running the background loading at this point since
- // we also touch the background collections
- throw new RuntimeException("Error! Background loading is already running");
+ if (mLoaderTask != task) {
+ throw new CancellationException("Loader already stopped");
}
- }
-
- // XXX: Throw an exception if we are already loading (since we touch the worker thread
- // data structures, we can't allow any other thread to touch that data, but because
- // this call is synchronous, we can get away with not locking).
-
- // The LauncherModel is static in the LauncherAppState and mHandler may have queued
- // operations from the previous activity. We need to ensure that all queued operations
- // are executed before any synchronous binding work is done.
- mHandler.flush();
-
- // Divide the set of loaded items into those that we are binding synchronously, and
- // everything else that is to be bound normally (asynchronously).
- bindWorkspace(synchronousBindPage);
- // XXX: For now, continue posting the binding of AllApps as there are other issues that
- // arise from that.
- onlyBindAllApps();
-
- bindDeepShortcuts();
- }
-
- private void verifyNotStopped() throws CancellationException {
- synchronized (LoaderTask.this) {
- if (mStopped) {
- throw new CancellationException("Loader stopped");
- }
- }
- }
-
- public void run() {
- synchronized (mLock) {
- if (mStopped) {
- return;
- }
+ mTask = task;
mIsLoaderTaskRunning = true;
- }
-
- try {
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
- // Set to false in bindWorkspace()
- mIsLoadingAndBindingWorkspace = true;
- loadWorkspace();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
- bindWorkspace(mPageToBindFirst);
-
- // Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 1 completed, wait for idle");
- waitForIdle();
- verifyNotStopped();
-
- // second step
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
- loadAllApps();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Update icon cache");
- updateIconCache();
-
- // Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 2 completed, wait for idle");
- waitForIdle();
- verifyNotStopped();
-
- // third step
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
- loadDeepShortcuts();
-
- verifyNotStopped();
- if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
- bindDeepShortcuts();
-
- // Take a break
- if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
- waitForIdle();
- verifyNotStopped();
-
- // fourth step
- if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
- refreshAndBindWidgetsAndShortcuts(getCallback(), false /* bindFirst */,
- null /* packageUser */);
-
- synchronized (mLock) {
- // Everything loaded bind the data.
- mModelLoaded = true;
- mHasLoaderCompletedOnce = true;
- }
- } catch (CancellationException e) {
- // Loader stopped, ignore
- } finally {
- // Clear out this reference, otherwise we end up holding it until all of the
- // callback runnables are done.
- mContext = null;
-
- synchronized (mLock) {
- // If we are still the last one to be scheduled, remove ourselves.
- if (mLoaderTask == this) {
- mLoaderTask = null;
- }
- mIsLoaderTaskRunning = false;
- }
+ mModelLoaded = false;
}
}
- public void stopLocked() {
- synchronized (LoaderTask.this) {
- mStopped = true;
- this.notify();
- }
- }
-
- /**
- * Gets the callbacks object. If we've been stopped, or if the launcher object
- * has somehow been garbage collected, return null instead. Pass in the Callbacks
- * object that was around when the deferred message was scheduled, and if there's
- * a new Callbacks object around then also return null. This will save us from
- * calling onto it with data that will be ignored.
- */
- Callbacks tryGetCallbacks(Callbacks oldCallbacks) {
+ public void commit() {
synchronized (mLock) {
- if (mStopped) {
- return null;
- }
-
- if (mCallbacks == null) {
- return null;
- }
-
- final Callbacks callbacks = mCallbacks.get();
- if (callbacks != oldCallbacks) {
- return null;
- }
- if (callbacks == null) {
- Log.w(TAG, "no mCallbacks");
- return null;
- }
-
- return callbacks;
+ // Everything loaded bind the data.
+ mModelLoaded = true;
}
}
- private void loadWorkspace() {
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.beginSection("Loading Workspace");
- }
-
- final Context context = mContext;
- final ContentResolver contentResolver = context.getContentResolver();
- final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
- final boolean isSafeMode = pmHelper.isSafeMode();
- final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
- final DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(context);
- final boolean isSdCardReady = Utilities.isBootCompleted();
- final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
-
- boolean clearDb = false;
- try {
- ImportDataTask.performImportIfPossible(context);
- } catch (Exception e) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (!clearDb && GridSizeMigrationTask.ENABLED &&
- !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
- // Migration failed. Clear workspace.
- clearDb = true;
- }
-
- if (clearDb) {
- Log.d(TAG, "loadWorkspace: resetting launcher database");
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
- }
-
- Log.d(TAG, "loadWorkspace: loading default favorites");
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
-
- synchronized (sBgDataModel) {
- sBgDataModel.clear();
-
- final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
- .getInstance(mContext).updateAndGetActiveSessionCache();
- sBgDataModel.workspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
-
- Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(contentResolver.query(
- LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
-
- HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
-
- try {
- final int appWidgetIdIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_ID);
- final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.APPWIDGET_PROVIDER);
- final int spanXIndex = c.getColumnIndexOrThrow
- (LauncherSettings.Favorites.SPANX);
- final int spanYIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.SPANY);
- final int rankIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.RANK);
- final int optionsIndex = c.getColumnIndexOrThrow(
- LauncherSettings.Favorites.OPTIONS);
-
- final LongSparseArray<UserHandle> allUsers = c.allUsers;
- final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
- final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
- for (UserHandle user : mUserManager.getUserProfiles()) {
- long serialNo = mUserManager.getSerialNumberForUser(user);
- allUsers.put(serialNo, user);
- quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
-
- boolean userUnlocked = mUserManager.isUserUnlocked(user);
-
- // We can only query for shortcuts when the user is unlocked.
- if (userUnlocked) {
- List<ShortcutInfoCompat> pinnedShortcuts =
- shortcutManager.queryForPinnedShortcuts(null, user);
- if (shortcutManager.wasLastCallSuccess()) {
- for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
- shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
- shortcut);
- }
- } else {
- // Shortcut manager can fail due to some race condition when the
- // lock state changes too frequently. For the purpose of the loading
- // shortcuts, consider the user is still locked.
- userUnlocked = false;
- }
- }
- unlockedUsers.put(serialNo, userUnlocked);
- }
-
- ShortcutInfo info;
- LauncherAppWidgetInfo appWidgetInfo;
- Intent intent;
- String targetPkg;
-
- while (!mStopped && c.moveToNext()) {
- try {
- if (c.user == null) {
- // User has been deleted, remove the item.
- c.markDeleted("User has been deleted");
- continue;
- }
-
- boolean allowMissingTarget = false;
- switch (c.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- intent = c.parseIntent();
- if (intent == null) {
- c.markDeleted("Invalid or null intent");
- continue;
- }
-
- int disabledState = quietMode.get(c.serialNumber) ?
- ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
- ComponentName cn = intent.getComponent();
- targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
-
- if (!Process.myUserHandle().equals(c.user)) {
- if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Legacy shortcuts are only allowed for default user");
- continue;
- } else if (c.restoreFlag != 0) {
- // Don't restore items for other profiles.
- c.markDeleted("Restore from managed profile not supported");
- continue;
- }
- }
- if (TextUtils.isEmpty(targetPkg) &&
- c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- c.markDeleted("Only legacy shortcuts can have null package");
- continue;
- }
-
- // If there is no target package, its an implicit intent
- // (legacy shortcut) which is always valid
- boolean validTarget = TextUtils.isEmpty(targetPkg) ||
- launcherApps.isPackageEnabledForProfile(targetPkg, c.user);
-
- if (cn != null && validTarget) {
- // If the apk is present and the shortcut points to a specific
- // component.
-
- // If the component is already present
- if (launcherApps.isActivityEnabledForProfile(cn, c.user)) {
- // no special handling necessary for this item
- c.markRestored();
- } else {
- if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
- // We allow auto install apps to have their intent
- // updated after an install.
- intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
- if (intent != null) {
- c.restoreFlag = 0;
- c.updater().put(
- LauncherSettings.Favorites.INTENT,
- intent.toUri(0)).commit();
- cn = intent.getComponent();
- } else {
- c.markDeleted("Unable to find a launch target");
- continue;
- }
- } else {
- // The app is installed but the component is no
- // longer available.
- c.markDeleted("Invalid component removed: " + cn);
- continue;
- }
- }
- }
- // else if cn == null => can't infer much, leave it
- // else if !validPkg => could be restored icon or missing sd-card
-
- if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
- // Points to a valid app (superset of cn != null) but the apk
- // is not available.
-
- if (c.restoreFlag != 0) {
- // Package is not yet available but might be
- // installed later.
- FileLog.d(TAG, "package not yet restored: " + targetPkg);
-
- if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
- // Restore has started once.
- } else if (installingPkgs.containsKey(targetPkg)) {
- // App restore has started. Update the flag
- c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
- c.updater().commit();
- } else {
- c.markDeleted("Unrestored app removed: " + targetPkg);
- continue;
- }
- } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
- // Package is present but not available.
- disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
- // Add the icon on the workspace anyway.
- allowMissingTarget = true;
- } else if (!isSdCardReady) {
- // SdCard is not ready yet. Package might get available,
- // once it is ready.
- Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
- pendingPackages.addToList(c.user, targetPkg);
- // Add the icon on the workspace anyway.
- allowMissingTarget = true;
- } else {
- // Do not wait for external media load anymore.
- c.markDeleted("Invalid package removed: " + targetPkg);
- continue;
- }
- }
-
- if (validTarget) {
- // The shortcut points to a valid target (either no target
- // or something which is ready to be used)
- c.markRestored();
- }
-
- boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
- c.getInt(rankIndex) >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
-
- if (c.restoreFlag != 0) {
- // Already verified above that user is same as default user
- info = c.getRestoredItemInfo(intent);
- } else if (c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- info = c.getAppShortcutInfo(
- intent, allowMissingTarget, useLowResIcon);
- } else if (c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
-
- ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
- if (unlockedUsers.get(c.serialNumber)) {
- ShortcutInfoCompat pinnedShortcut =
- shortcutKeyToPinnedShortcuts.get(key);
- if (pinnedShortcut == null) {
- // The shortcut is no longer valid.
- c.markDeleted("Pinned shortcut not found");
- continue;
- }
- info = new ShortcutInfo(pinnedShortcut, context);
- info.iconBitmap = LauncherIcons
- .createShortcutIcon(pinnedShortcut, context);
- if (pmHelper.isAppSuspended(
- pinnedShortcut.getPackage(), info.user)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
- }
- intent = info.intent;
- } else {
- // Create a shortcut info in disabled mode for now.
- info = c.loadSimpleShortcut();
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
- }
- } else { // item type == ITEM_TYPE_SHORTCUT
- info = c.loadSimpleShortcut();
-
- // Shortcuts are only available on the primary profile
- if (!TextUtils.isEmpty(targetPkg)
- && pmHelper.isAppSuspended(targetPkg, c.user)) {
- disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
- }
-
- // App shortcuts that used to be automatically added to Launcher
- // didn't always have the correct intent flags set, so do that
- // here
- if (intent.getAction() != null &&
- intent.getCategories() != null &&
- intent.getAction().equals(Intent.ACTION_MAIN) &&
- intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
- intent.addFlags(
- Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- }
- }
-
- if (info != null) {
- c.applyCommonProperties(info);
-
- info.intent = intent;
- info.rank = c.getInt(rankIndex);
- info.spanX = 1;
- info.spanY = 1;
- info.isDisabled |= disabledState;
- if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
- info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
- }
-
- if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
- Integer progress = installingPkgs.get(targetPkg);
- if (progress != null) {
- info.setInstallProgress(progress);
- } else {
- info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
- }
- }
-
- c.checkAndAddItem(info, sBgDataModel);
- } else {
- throw new RuntimeException("Unexpected null ShortcutInfo");
- }
- break;
-
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- FolderInfo folderInfo = sBgDataModel.findOrMakeFolder(c.id);
- c.applyCommonProperties(folderInfo);
-
- // Do not trim the folder label, as is was set by the user.
- folderInfo.title = c.getString(c.titleIndex);
- folderInfo.spanX = 1;
- folderInfo.spanY = 1;
- folderInfo.options = c.getInt(optionsIndex);
-
- // no special handling required for restored folders
- c.markRestored();
-
- c.checkAndAddItem(folderInfo, sBgDataModel);
- break;
-
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
- // Read all Launcher-specific widget details
- boolean customWidget = c.itemType ==
- LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
-
- int appWidgetId = c.getInt(appWidgetIdIndex);
- String savedProvider = c.getString(appWidgetProviderIndex);
-
- final ComponentName component =
- ComponentName.unflattenFromString(savedProvider);
-
- final boolean isIdValid = !c.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
- final boolean wasProviderReady = !c.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
-
- if (widgetProvidersMap == null) {
- widgetProvidersMap = AppWidgetManagerCompat
- .getInstance(mContext).getAllProvidersMap();
- }
- final AppWidgetProviderInfo provider = widgetProvidersMap.get(
- new ComponentKey(
- ComponentName.unflattenFromString(savedProvider),
- c.user));
-
- final boolean isProviderReady = isValidProvider(provider);
- if (!isSafeMode && !customWidget &&
- wasProviderReady && !isProviderReady) {
- c.markDeleted(
- "Deleting widget that isn't installed anymore: "
- + provider);
- } else {
- if (isProviderReady) {
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- provider.provider);
-
- // The provider is available. So the widget is either
- // available or not available. We do not need to track
- // any future restore updates.
- int status = c.restoreFlag &
- ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- if (!wasProviderReady) {
- // If provider was not previously ready, update the
- // status and UI flag.
-
- // Id would be valid only if the widget restore broadcast was received.
- if (isIdValid) {
- status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- } else {
- status &= ~LauncherAppWidgetInfo
- .FLAG_PROVIDER_NOT_READY;
- }
- }
- appWidgetInfo.restoreStatus = status;
- } else {
- Log.v(TAG, "Widget restore pending id=" + c.id
- + " appWidgetId=" + appWidgetId
- + " status =" + c.restoreFlag);
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- component);
- appWidgetInfo.restoreStatus = c.restoreFlag;
- Integer installProgress = installingPkgs.get(component.getPackageName());
-
- if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
- // Restore has started once.
- } else if (installProgress != null) {
- // App restore has started. Update the flag
- appWidgetInfo.restoreStatus |=
- LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
- } else if (!isSafeMode) {
- c.markDeleted("Unrestored widget removed: " + component);
- continue;
- }
-
- appWidgetInfo.installProgress =
- installProgress == null ? 0 : installProgress;
- }
- if (appWidgetInfo.hasRestoreFlag(
- LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
- appWidgetInfo.bindOptions = c.parseIntent();
- }
-
- c.applyCommonProperties(appWidgetInfo);
- appWidgetInfo.spanX = c.getInt(spanXIndex);
- appWidgetInfo.spanY = c.getInt(spanYIndex);
- appWidgetInfo.user = c.user;
-
- if (!c.isOnWorkspaceOrHotseat()) {
- c.markDeleted("Widget found where container != " +
- "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
- continue;
- }
-
- if (!customWidget) {
- String providerName =
- appWidgetInfo.providerName.flattenToString();
- if (!providerName.equals(savedProvider) ||
- (appWidgetInfo.restoreStatus != c.restoreFlag)) {
- c.updater()
- .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
- providerName)
- .put(LauncherSettings.Favorites.RESTORED,
- appWidgetInfo.restoreStatus)
- .commit();
- }
- }
- c.checkAndAddItem(appWidgetInfo, sBgDataModel);
- }
- break;
- }
- } catch (Exception e) {
- Log.e(TAG, "Desktop items loading interrupted", e);
- }
- }
- } finally {
- Utilities.closeSilently(c);
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ // If we are still the last one to be scheduled, remove ourselves.
+ if (mLoaderTask == mTask) {
+ mLoaderTask = null;
}
-
- // Break early if we've stopped loading
- if (mStopped) {
- sBgDataModel.clear();
- return;
- }
-
- // Remove dead items
- if (c.commitDeleted()) {
- // Remove any empty folder
- ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
- .call(contentResolver,
- LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
- .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
- for (long folderId : deletedFolderIds) {
- sBgDataModel.workspaceItems.remove(sBgDataModel.folders.get(folderId));
- sBgDataModel.folders.remove(folderId);
- sBgDataModel.itemsIdMap.remove(folderId);
- }
-
- // Remove any ghost widgets
- LauncherSettings.Settings.call(contentResolver,
- LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
- }
-
- // Unpin shortcuts that don't exist on the workspace.
- HashSet<ShortcutKey> pendingShortcuts =
- InstallShortcutReceiver.getPendingShortcuts(context);
- for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
- MutableInt numTimesPinned = sBgDataModel.pinnedShortcutCounts.get(key);
- if ((numTimesPinned == null || numTimesPinned.value == 0)
- && !pendingShortcuts.contains(key)) {
- // Shortcut is pinned but doesn't exist on the workspace; unpin it.
- shortcutManager.unpinShortcut(key);
- }
- }
-
- // Sort all the folder items and make sure the first 3 items are high resolution.
- for (FolderInfo folder : sBgDataModel.folders) {
- Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
- int pos = 0;
- for (ShortcutInfo info : folder.contents) {
- if (info.usingLowResIcon &&
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- mIconCache.getTitleAndIcon(info, false);
- }
- pos ++;
- if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
- break;
- }
- }
- }
-
- c.commitRestoredItems();
- if (!isSdCardReady && !pendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(
- LauncherModel.this, mContext, pendingPackages),
- new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
- null,
- sWorker);
- }
-
- // Remove any empty screens
- ArrayList<Long> unusedScreens = new ArrayList<>(sBgDataModel.workspaceScreens);
- for (ItemInfo item: sBgDataModel.itemsIdMap) {
- long screenId = item.screenId;
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- unusedScreens.contains(screenId)) {
- unusedScreens.remove(screenId);
- }
- }
-
- // If there are any empty screens remove them, and update.
- if (unusedScreens.size() != 0) {
- sBgDataModel.workspaceScreens.removeAll(unusedScreens);
- updateWorkspaceScreenOrder(context, sBgDataModel.workspaceScreens);
- }
- }
- if (LauncherAppState.PROFILE_STARTUP) {
- Trace.endSection();
- }
- }
-
- /** Filters the set of items who are directly or indirectly (via another container) on the
- * specified screen. */
- private void filterCurrentWorkspaceItems(long currentScreenId,
- ArrayList<ItemInfo> allWorkspaceItems,
- ArrayList<ItemInfo> currentScreenItems,
- ArrayList<ItemInfo> otherScreenItems) {
- // Purge any null ItemInfos
- Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
- while (iter.hasNext()) {
- ItemInfo i = iter.next();
- if (i == null) {
- iter.remove();
- }
- }
-
- // Order the set of items by their containers first, this allows use to walk through the
- // list sequentially, build up a list of containers that are in the specified screen,
- // as well as all items in those containers.
- Set<Long> itemsOnScreen = new HashSet<Long>();
- Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
- @Override
- public int compare(ItemInfo lhs, ItemInfo rhs) {
- return Utilities.longCompare(lhs.container, rhs.container);
- }
- });
- for (ItemInfo info : allWorkspaceItems) {
- if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- if (info.screenId == currentScreenId) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- if (itemsOnScreen.contains(info.container)) {
- currentScreenItems.add(info);
- itemsOnScreen.add(info.id);
- } else {
- otherScreenItems.add(info);
- }
- }
- }
- }
-
- /** Filters the set of widgets which are on the specified screen. */
- private void filterCurrentAppWidgets(long currentScreenId,
- ArrayList<LauncherAppWidgetInfo> appWidgets,
- ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
- ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
-
- for (LauncherAppWidgetInfo widget : appWidgets) {
- if (widget == null) continue;
- if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- widget.screenId == currentScreenId) {
- currentScreenWidgets.add(widget);
- } else {
- otherScreenWidgets.add(widget);
- }
- }
- }
-
- /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
- * right) */
- private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
- final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
- Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
- @Override
- public int compare(ItemInfo lhs, ItemInfo rhs) {
- if (lhs.container == rhs.container) {
- // Within containers, order by their spatial position in that container
- switch ((int) lhs.container) {
- case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
- long lr = (lhs.screenId * screenCellCount +
- lhs.cellY * screenCols + lhs.cellX);
- long rr = (rhs.screenId * screenCellCount +
- rhs.cellY * screenCols + rhs.cellX);
- return Utilities.longCompare(lr, rr);
- }
- case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
- // We currently use the screen id as the rank
- return Utilities.longCompare(lhs.screenId, rhs.screenId);
- }
- default:
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- throw new RuntimeException("Unexpected container type when " +
- "sorting workspace items.");
- }
- return 0;
- }
- } else {
- // Between containers, order by hotseat, desktop
- return Utilities.longCompare(lhs.container, rhs.container);
- }
- }
- });
- }
-
- private void bindWorkspaceScreens(final Callbacks oldCallbacks,
- final ArrayList<Long> orderedScreens) {
- final Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindScreens(orderedScreens);
- }
- }
- };
- runOnMainThread(r);
- }
-
- private void bindWorkspaceItems(final Callbacks oldCallbacks,
- final ArrayList<ItemInfo> workspaceItems,
- final ArrayList<LauncherAppWidgetInfo> appWidgets,
- final Executor executor) {
-
- // Bind the workspace items
- int N = workspaceItems.size();
- for (int i = 0; i < N; i += ITEMS_CHUNK) {
- final int start = i;
- final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
- final Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindItems(workspaceItems, start, start+chunkSize,
- false);
- }
- }
- };
- executor.execute(r);
- }
-
- // Bind the widgets, one at a time
- N = appWidgets.size();
- for (int i = 0; i < N; i++) {
- final LauncherAppWidgetInfo widget = appWidgets.get(i);
- final Runnable r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAppWidget(widget);
- }
- }
- };
- executor.execute(r);
- }
- }
-
- /**
- * Binds all loaded data to actual views on the main thread.
- */
- private void bindWorkspace(int synchronizeBindPage) {
- final long t = SystemClock.uptimeMillis();
- Runnable r;
-
- // Don't use these two variables in any of the callback runnables.
- // Otherwise we hold a reference to them.
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
- // This launcher has exited and nobody bothered to tell us. Just bail.
- Log.w(TAG, "LoaderTask running with no launcher");
- return;
- }
-
- // Save a copy of all the bg-thread collections
- ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
- ArrayList<Long> orderedScreenIds = new ArrayList<>();
-
- synchronized (sBgDataModel) {
- workspaceItems.addAll(sBgDataModel.workspaceItems);
- appWidgets.addAll(sBgDataModel.appWidgets);
- orderedScreenIds.addAll(sBgDataModel.workspaceScreens);
- }
-
- final int currentScreen;
- {
- int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
- ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
- if (currScreen >= orderedScreenIds.size()) {
- // There may be no workspace screens (just hotseat items and an empty page).
- currScreen = PagedView.INVALID_RESTORE_PAGE;
- }
- currentScreen = currScreen;
- }
- final boolean validFirstPage = currentScreen >= 0;
- final long currentScreenId =
- validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
-
- // Separate the items that are on the current screen, and all the other remaining items
- ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
- ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
- ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
-
- filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
- otherWorkspaceItems);
- filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
- otherAppWidgets);
- sortWorkspaceItemsSpatially(currentWorkspaceItems);
- sortWorkspaceItemsSpatially(otherWorkspaceItems);
-
- // Tell the workspace that we're about to start binding items
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.clearPendingBinds();
- callbacks.startBinding();
- }
- }
- };
- runOnMainThread(r);
-
- bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
-
- Executor mainExecutor = new DeferredMainThreadExecutor();
- // Load items on the current page.
- bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
-
- // In case of validFirstPage, only bind the first screen, and defer binding the
- // remaining screens after first onDraw (and an optional the fade animation whichever
- // happens later).
- // This ensures that the first screen is immediately visible (eg. during rotation)
- // In case of !validFirstPage, bind all pages one after other.
- final Executor deferredExecutor =
- validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor;
-
- mainExecutor.execute(new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.finishFirstPageBind(
- validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
- }
- }
- });
-
- bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
-
- // Tell the workspace that we're done binding items
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.finishBindingItems();
- }
-
- mIsLoadingAndBindingWorkspace = false;
-
- // Run all the bind complete runnables after workspace is bound.
- if (!mBindCompleteRunnables.isEmpty()) {
- synchronized (mBindCompleteRunnables) {
- for (final Runnable r : mBindCompleteRunnables) {
- runOnWorkerThread(r);
- }
- mBindCompleteRunnables.clear();
- }
- }
-
- // If we're profiling, ensure this is the last thing in the queue.
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound workspace in "
- + (SystemClock.uptimeMillis()-t) + "ms");
- }
-
- }
- };
- deferredExecutor.execute(r);
-
- if (validFirstPage) {
- r = new Runnable() {
- public void run() {
- Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- // We are loading synchronously, which means, some of the pages will be
- // bound after first draw. Inform the callbacks that page binding is
- // not complete, and schedule the remaining pages.
- if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
- callbacks.onPageBoundSynchronously(currentScreen);
- }
- callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
- }
- }
- };
- runOnMainThread(r);
- }
- }
-
- private void updateIconCache() {
- // Ignore packages which have a promise icon.
- HashSet<String> packagesToIgnore = new HashSet<>();
- synchronized (sBgDataModel) {
- for (ItemInfo info : sBgDataModel.itemsIdMap) {
- if (info instanceof ShortcutInfo) {
- ShortcutInfo si = (ShortcutInfo) info;
- if (si.isPromise() && si.getTargetComponent() != null) {
- packagesToIgnore.add(si.getTargetComponent().getPackageName());
- }
- } else if (info instanceof LauncherAppWidgetInfo) {
- LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
- if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
- packagesToIgnore.add(lawi.providerName.getPackageName());
- }
- }
- }
- }
- mIconCache.updateDbIcons(packagesToIgnore);
- }
-
- private void onlyBindAllApps() {
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
- // This launcher has exited and nobody bothered to tell us. Just bail.
- Log.w(TAG, "LoaderTask running with no launcher (onlyBindAllApps)");
- return;
- }
-
- // shallow copy
- @SuppressWarnings("unchecked")
- final ArrayList<AppInfo> list
- = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
- Runnable r = new Runnable() {
- public void run() {
- final long t = SystemClock.uptimeMillis();
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAllApplications(list);
- }
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound all " + list.size() + " apps from cache in "
- + (SystemClock.uptimeMillis() - t) + "ms");
- }
- }
- };
- runOnMainThread(r);
- }
-
- private void loadAllApps() {
- final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
-
- final Callbacks oldCallbacks = mCallbacks.get();
- if (oldCallbacks == null) {
- // This launcher has exited and nobody bothered to tell us. Just bail.
- Log.w(TAG, "LoaderTask running with no launcher (loadAllApps)");
- return;
- }
-
- final List<UserHandle> profiles = mUserManager.getUserProfiles();
-
- // Clear the list of apps
- mBgAllAppsList.clear();
- for (UserHandle user : profiles) {
- // Query for the set of apps
- final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "getActivityList took "
- + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
- Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
- }
- // Fail if we don't have any apps
- // TODO: Fix this. Only fail for the current user.
- if (apps == null || apps.isEmpty()) {
- return;
- }
- boolean quietMode = mUserManager.isQuietModeEnabled(user);
- // Create the ApplicationInfos
- for (int i = 0; i < apps.size(); i++) {
- LauncherActivityInfo app = apps.get(i);
- // This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
- }
-
- final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
- if (heuristic != null) {
- final Runnable r = new Runnable() {
-
- @Override
- public void run() {
- heuristic.processUserApps(apps);
- }
- };
- runOnMainThread(new Runnable() {
-
- @Override
- public void run() {
- // Check isLoadingWorkspace on the UI thread, as it is updated on
- // the UI thread.
- if (mIsLoadingAndBindingWorkspace) {
- synchronized (mBindCompleteRunnables) {
- mBindCompleteRunnables.add(r);
- }
- } else {
- runOnWorkerThread(r);
- }
- }
- });
- }
- }
- // Huh? Shouldn't this be inside the Runnable below?
- final ArrayList<AppInfo> added = mBgAllAppsList.added;
- mBgAllAppsList.added = new ArrayList<AppInfo>();
-
- // Post callback on main thread
- mHandler.post(new Runnable() {
- public void run() {
-
- final long bindTime = SystemClock.uptimeMillis();
- final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
- if (callbacks != null) {
- callbacks.bindAllApplications(added);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "bound " + added.size() + " apps in "
- + (SystemClock.uptimeMillis() - bindTime) + "ms");
- }
- } else {
- Log.i(TAG, "not binding apps: no Launcher activity");
- }
- }
- });
- // Cleanup any data stored for a deleted user.
- ManagedProfileHeuristic.processAllUsers(profiles, mContext);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "Icons processed in "
- + (SystemClock.uptimeMillis() - loadTime) + "ms");
- }
- }
-
- private void loadDeepShortcuts() {
- sBgDataModel.deepShortcutMap.clear();
- DeepShortcutManager shortcutManager = DeepShortcutManager.getInstance(mContext);
- mHasShortcutHostPermission = shortcutManager.hasHostPermission();
- if (mHasShortcutHostPermission) {
- for (UserHandle user : mUserManager.getUserProfiles()) {
- if (mUserManager.isUserUnlocked(user)) {
- List<ShortcutInfoCompat> shortcuts =
- shortcutManager.queryForAllShortcuts(user);
- sBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
- }
- }
+ mIsLoaderTaskRunning = false;
}
}
}
- public void bindDeepShortcuts() {
- final MultiHashMap<ComponentKey, String> shortcutMapCopy =
- sBgDataModel.deepShortcutMap.clone();
- Runnable r = new Runnable() {
- @Override
- public void run() {
- Callbacks callbacks = getCallback();
- if (callbacks != null) {
- callbacks.bindDeepShortcutMap(shortcutMapCopy);
- }
- }
- };
- runOnMainThread(r);
+ public LoaderTransaction beginLoader(LoaderTask task) throws CancellationException {
+ return new LoaderTransaction(task);
}
/**
@@ -1815,14 +606,8 @@
CacheDataUpdatedTask.OP_CACHE_UPDATE, user, updatedPackages));
}
- void enqueueModelUpdateTask(BaseModelUpdateTask task) {
- if (!mModelLoaded && mLoaderTask == null) {
- if (DEBUG_LOADERS) {
- Log.d(TAG, "enqueueModelUpdateTask Ignoring task since loader is pending=" + task);
- }
- return;
- }
- task.init(this);
+ public void enqueueModelUpdateTask(ModelUpdateTask task) {
+ task.init(mApp, this, sBgDataModel, mBgAllAppsList, mUiExecutor);
runOnWorkerThread(task);
}
@@ -1838,51 +623,14 @@
/**
* A runnable which changes/updates the data model of the launcher based on certain events.
*/
- public static abstract class BaseModelUpdateTask implements Runnable {
-
- private LauncherModel mModel;
- private DeferredHandler mUiHandler;
-
- /* package private */
- void init(LauncherModel model) {
- mModel = model;
- mUiHandler = mModel.mHandler;
- }
-
- @Override
- public void run() {
- if (!mModel.mHasLoaderCompletedOnce) {
- // Loader has not yet run.
- return;
- }
- execute(mModel.mApp, sBgDataModel, mModel.mBgAllAppsList);
- }
+ public interface ModelUpdateTask extends Runnable {
/**
- * Execute the actual task. Called on the worker thread.
+ * Called before the task is posted to initialize the internal state.
*/
- public abstract void execute(
- LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+ void init(LauncherAppState app, LauncherModel model,
+ BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor);
- /**
- * Schedules a {@param task} to be executed on the current callbacks.
- */
- public final void scheduleCallbackTask(final CallbackTask task) {
- final Callbacks callbacks = mModel.getCallback();
- mUiHandler.post(new Runnable() {
- public void run() {
- Callbacks cb = mModel.getCallback();
- if (callbacks == cb && cb != null) {
- task.execute(callbacks);
- }
- }
- });
- }
-
- public ModelWriter getModelWriter() {
- // Updates from model task, do not deal with icon position in hotseat.
- return mModel.getWriter(false /* hasVerticalHotseat */);
- }
}
public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
@@ -1900,7 +648,7 @@
* Utility method to update a shortcut on the background thread.
*/
public void updateAndBindShortcutInfo(final Provider<ShortcutInfo> shortcutProvider) {
- enqueueModelUpdateTask(new ExtendedModelTask() {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
ShortcutInfo info = shortcutProvider.get();
@@ -1911,43 +659,16 @@
});
}
- private void bindWidgetsModel(final Callbacks callbacks) {
- final MultiHashMap<PackageItemInfo, WidgetItem> widgets
- = mBgWidgetsModel.getWidgetsMap().clone();
- mHandler.post(new Runnable() {
+ public void refreshAndBindWidgetsAndShortcuts(@Nullable final PackageUserKey packageUser) {
+ enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindAllWidgets(widgets);
- }
+ public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+ dataModel.widgetsModel.update(app, packageUser);
+ bindUpdatedWidgets(dataModel);
}
});
}
- public void refreshAndBindWidgetsAndShortcuts(final Callbacks callbacks,
- final boolean bindFirst, @Nullable final PackageUserKey packageUser) {
- runOnWorkerThread(new Runnable() {
- @Override
- public void run() {
- if (bindFirst && !mBgWidgetsModel.isEmpty()) {
- bindWidgetsModel(callbacks);
- }
- ArrayList<WidgetItem> widgets = mBgWidgetsModel.update(
- mApp.getContext(), packageUser);
- bindWidgetsModel(callbacks);
-
- // update the Widget entries inside DB on the worker thread.
- mApp.getWidgetCache().removeObsoletePreviews(widgets, packageUser);
- }
- });
- }
-
- static boolean isValidProvider(AppWidgetProviderInfo provider) {
- return (provider != null) && (provider.provider != null)
- && (provider.provider.getPackageName() != null);
- }
-
public void dumpState(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
if (args.length > 0 && TextUtils.equals(args[0], "--all")) {
writer.println(prefix + "All apps list: size=" + mBgAllAppsList.data.size());
@@ -1964,23 +685,6 @@
}
/**
- * @return {@link FolderInfo} if its already loaded.
- */
- public FolderInfo findFolderById(Long folderId) {
- synchronized (sBgDataModel) {
- return sBgDataModel.folders.get(folderId);
- }
- }
-
- @Thunk class DeferredMainThreadExecutor implements Executor {
-
- @Override
- public void execute(Runnable command) {
- runOnMainThread(command);
- }
- }
-
- /**
* @return the looper for the worker thread which can be used to start background tasks.
*/
public static Looper getWorkerLooper() {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 3150d5b..a4cd191 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.annotation.TargetApi;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.content.ComponentName;
@@ -38,6 +39,7 @@
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@@ -53,43 +55,40 @@
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.graphics.IconShapeOverride;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.DbDowngradeHelper;
import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.NoLocaleSqliteContext;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
private static final boolean LOGD = false;
+ private static final String DOWNGRADE_SCHEMA_FILE = "downgrade_schema.json";
+
/**
* Represents the schema of the database. Changes in scheme need not be backwards compatible.
*/
- private static final int SCHEMA_VERSION = 27;
- /**
- * Represents the actual data. It could include additional validations and normalizations added
- * overtime. These must be backwards compatible, else we risk breaking old devices during
- * restore or binary version downgrade.
- */
- private static final int DATA_VERSION = 3;
+ public static final int SCHEMA_VERSION = 27;
- private static final String PREF_KEY_DATA_VERISON = "provider_data_version";
-
- public static final String AUTHORITY = ProviderConfig.AUTHORITY;
+ public static final String AUTHORITY = (BuildConfig.APPLICATION_ID + ".settings").intern();
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
@@ -114,7 +113,7 @@
@Override
public boolean onCreate() {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.d(TAG, "Launcher process started");
}
mListenerHandler = new Handler(mListenerWrapper);
@@ -305,8 +304,7 @@
SqlArguments args = new SqlArguments(uri);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
int numValues = values.length;
for (int i = 0; i < numValues; i++) {
addModifiedTime(values[i]);
@@ -314,9 +312,7 @@
return 0;
}
}
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
+ t.commit();
}
notifyListeners();
@@ -328,15 +324,11 @@
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
throws OperationApplicationException {
createDbIfNotExists();
- SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(mOpenHelper.getWritableDatabase())) {
ContentProviderResult[] result = super.applyBatch(operations);
- db.setTransactionSuccessful();
+ t.commit();
reloadLauncherIfExternal();
return result;
- } finally {
- db.endTransaction();
}
}
@@ -442,31 +434,26 @@
private ArrayList<Long> deleteEmptyFolders() {
ArrayList<Long> folderIds = new ArrayList<>();
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Select folders whose id do not match any container value.
String selection = LauncherSettings.Favorites.ITEM_TYPE + " = "
+ LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND "
+ LauncherSettings.Favorites._ID + " NOT IN (SELECT " +
LauncherSettings.Favorites.CONTAINER + " FROM "
+ Favorites.TABLE_NAME + ")";
- Cursor c = db.query(Favorites.TABLE_NAME,
+ try (Cursor c = db.query(Favorites.TABLE_NAME,
new String[] {LauncherSettings.Favorites._ID},
- selection, null, null, null, null);
- while (c.moveToNext()) {
- folderIds.add(c.getLong(0));
+ selection, null, null, null, null)) {
+ LauncherDbUtils.iterateCursor(c, 0, folderIds);
}
- c.close();
if (!folderIds.isEmpty()) {
db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, folderIds), null);
}
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
folderIds.clear();
- } finally {
- db.endTransaction();
}
return folderIds;
}
@@ -714,50 +701,111 @@
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
- SharedPreferences prefs = mContext
- .getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
- int oldVersion = prefs.getInt(PREF_KEY_DATA_VERISON, 0);
- if (oldVersion != DATA_VERSION) {
- // Only run the data upgrade path for an existing db.
- if (!Utilities.getPrefs(mContext).getBoolean(EMPTY_DATABASE_CREATED, false)) {
- db.beginTransaction();
- try {
- onDataUpgrade(db, oldVersion);
- db.setTransactionSuccessful();
- } catch (Exception e) {
- Log.d(TAG, "Error updating data version, ignoring", e);
- return;
- } finally {
- db.endTransaction();
- }
- }
- prefs.edit().putInt(PREF_KEY_DATA_VERISON, DATA_VERSION).apply();
+
+ File schemaFile = mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE);
+ if (!schemaFile.exists()) {
+ handleOneTimeDataUpgrade(db);
}
+ DbDowngradeHelper.updateSchemaFile(schemaFile, SCHEMA_VERSION, mContext,
+ R.raw.downgrade_schema);
}
/**
- * Called when the data is updated as part of app update. It can be called multiple times
- * with old version, even though it had been run before. The changes made here must be
- * backwards compatible, else we risk breaking old devices during restore or binary
- * version downgrade.
+ * One-time data updated before support of onDowngrade was added. This update is backwards
+ * compatible and can safely be run multiple times.
+ * Note: No new logic should be added here after release, as the new logic might not get
+ * executed on an existing device.
+ * TODO: Move this to db upgrade path, once the downgrade path is released.
*/
- protected void onDataUpgrade(SQLiteDatabase db, int oldVersion) {
- switch (oldVersion) {
- case 0:
- case 1: {
- // Remove "profile extra"
- UserManagerCompat um = UserManagerCompat.getInstance(mContext);
- for (UserHandle user : um.getUserProfiles()) {
- long serial = um.getSerialNumberForUser(user);
- String sql = "update favorites set intent = replace(intent, "
- + "';l.profile=" + serial + ";', ';') where itemType = 0;";
- db.execSQL(sql);
+ protected void handleOneTimeDataUpgrade(SQLiteDatabase db) {
+ // Remove "profile extra"
+ UserManagerCompat um = UserManagerCompat.getInstance(mContext);
+ for (UserHandle user : um.getUserProfiles()) {
+ long serial = um.getSerialNumberForUser(user);
+ String sql = "update favorites set intent = replace(intent, "
+ + "';l.profile=" + serial + ";', ';') where itemType = 0;";
+ db.execSQL(sql);
+ }
+
+ updateExistingFoldersToMatchPrePermutationLayout(db);
+ }
+
+ /**
+ * We have changed the way we display items in Folders, but we want existing folders to
+ * appear the same.
+ *
+ * To make this change invisible to existing Folders, we need to update the ranks of the
+ * items such that, when displayed using the permutation, the order remains the same.
+ */
+ private void updateExistingFoldersToMatchPrePermutationLayout(SQLiteDatabase db) {
+ InvariantDeviceProfile idp = new InvariantDeviceProfile(mContext);
+ int maxCols = idp.numFolderColumns;
+ int maxRows = idp.numFolderRows;
+
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
+ Cursor c = db.query(Favorites.TABLE_NAME,
+ new String[] {Favorites._ID, Favorites.OPTIONS},
+ "itemType=" + Favorites.ITEM_TYPE_FOLDER, null, null, null, null);
+
+ // For every Folder
+ while (c.moveToNext()) {
+ final long folderId = c.getLong(c.getColumnIndexOrThrow(Favorites._ID));
+ int options = c.getInt(c.getColumnIndexOrThrow(Favorites.OPTIONS));
+
+ if ((options & FolderInfo.FLAG_ITEM_RANKS_UPDATED) != 0) {
+ // The folder has already been updated.
+ continue;
}
+
+ // For each item in the Folder
+ Cursor c2 = db.query(Favorites.TABLE_NAME, new String[] {
+ Favorites._ID, Favorites.RANK, Favorites.CELLX, Favorites.CELLY,
+ Favorites.OPTIONS},
+ "container=" + folderId, null, null, null, null);
+
+ int numItemsInFolder = c2.getCount();
+
+ // Calculate the grid size.
+ int[] gridXY = new int[2];
+ FolderPagedView.calculateGridSize(numItemsInFolder, 0, 0, maxCols, maxRows,
+ maxCols * maxRows, gridXY);
+ int gridX = gridXY[0];
+ int gridY = gridXY[1];
+ int maxItemsPerPage = gridX * gridY;
+
+ // We create a mapping from the permutation to the original rank (ie. the
+ // inverse permutation). This is what we'll use to set the folder items so that
+ // they appear in their original order.
+ int[] inversion = new int[numItemsInFolder];
+ for (int i = 0; i < numItemsInFolder; ++i) {
+ int permutation = FolderPagedView.getReadingOrderPosForRank(i,
+ maxItemsPerPage, gridX, null);
+ inversion[permutation] = i;
+ }
+
+ // Now we update the ranks of the folder items. Note that cellX/cellY stay the
+ // same, due to the permutation.
+ for (int i = 0; i < numItemsInFolder && c2.moveToNext(); ++i) {
+ final int rank = c2.getInt(c2.getColumnIndexOrThrow(Favorites.RANK));
+
+ SQLiteStatement updateItem = db.compileStatement(
+ "UPDATE favorites SET rank=" + inversion[rank] + " WHERE _id=?");
+ updateItem.bindLong(1, c2.getInt(c2.getColumnIndexOrThrow(Favorites._ID)));
+ updateItem.executeUpdateDelete();
+ }
+ c2.close();
+
+ // Mark the folder as having been updated.
+ options |= FolderInfo.FLAG_ITEM_RANKS_UPDATED;
+ SQLiteStatement updateFolder = db.compileStatement(
+ "UPDATE favorites SET options=" + options + " WHERE _id=?");
+ updateFolder.bindLong(1, folderId);
+ updateFolder.executeUpdateDelete();
}
- case 2:
- case 3:
- // data updated
- return;
+ c.close();
+ t.commit();
+ } catch (SQLException ex) {
+ Log.w(TAG, "Error updating folder items to match permutation.", ex);
}
}
@@ -774,35 +822,29 @@
addWorkspacesTable(db, false);
}
case 13: {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Insert new column for holding widget provider name
db.execSQL("ALTER TABLE favorites " +
"ADD COLUMN appWidgetProvider TEXT;");
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
// Old version remains, which means we wipe old data
break;
- } finally {
- db.endTransaction();
}
}
case 14: {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Insert new column for holding update timestamp
db.execSQL("ALTER TABLE favorites " +
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
db.execSQL("ALTER TABLE workspaceScreens " +
"ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
// Old version remains, which means we wipe old data
break;
- } finally {
- db.endTransaction();
}
}
case 15: {
@@ -870,29 +912,25 @@
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion == 28 && newVersion == 27) {
- // TODO: remove this check. This is only applicable for internal development/testing
- // and for any released version of Launcher.
- return;
+ try {
+ DbDowngradeHelper.parse(mContext.getFileStreamPath(DOWNGRADE_SCHEMA_FILE))
+ .onDowngrade(db, oldVersion, newVersion);
+ } catch (Exception e) {
+ Log.d(TAG, "Unable to downgrade from: " + oldVersion + " to " + newVersion +
+ ". Wiping databse.", e);
+ createEmptyDB(db);
}
- // This shouldn't happen -- throw our hands up in the air and start over.
- Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
- ". Wiping databse.");
- createEmptyDB(db);
}
/**
* Clears all the data for a fresh start.
*/
public void createEmptyDB(SQLiteDatabase db) {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
onCreate(db);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
+ t.commit();
}
}
@@ -900,40 +938,39 @@
* Removes widgets which are registered to the Launcher's host, but are not present
* in our model.
*/
+ @TargetApi(Build.VERSION_CODES.O)
public void removeGhostWidgets(SQLiteDatabase db) {
// Get all existing widget ids.
final AppWidgetHost host = newLauncherWidgetHost();
final int[] allWidgets;
try {
- Method getter = AppWidgetHost.class.getDeclaredMethod("getAppWidgetIds");
- getter.setAccessible(true);
- allWidgets = (int[]) getter.invoke(host);
- } catch (Exception e) {
+ // Although the method was defined in O, it has existed since the beginning of time,
+ // so it might work on older platforms as well.
+ allWidgets = host.getAppWidgetIds();
+ } catch (IncompatibleClassChangeError e) {
Log.e(TAG, "getAppWidgetIds not supported", e);
return;
}
- try {
- Cursor c = db.query(Favorites.TABLE_NAME,
- new String[] {Favorites.APPWIDGET_ID },
- "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null);
- HashSet<Integer> validWidgets = new HashSet<>();
+ final HashSet<Integer> validWidgets = new HashSet<>();
+ try (Cursor c = db.query(Favorites.TABLE_NAME,
+ new String[] {Favorites.APPWIDGET_ID },
+ "itemType=" + Favorites.ITEM_TYPE_APPWIDGET, null, null, null, null)) {
while (c.moveToNext()) {
validWidgets.add(c.getInt(0));
}
- c.close();
-
- for (int widgetId : allWidgets) {
- if (!validWidgets.contains(widgetId)) {
- try {
- FileLog.d(TAG, "Deleting invalid widget " + widgetId);
- host.deleteAppWidgetId(widgetId);
- } catch (RuntimeException e) {
- // Ignore
- }
- }
- }
} catch (SQLException ex) {
Log.w(TAG, "Error getting widgets list", ex);
+ return;
+ }
+ for (int widgetId : allWidgets) {
+ if (!validWidgets.contains(widgetId)) {
+ try {
+ FileLog.d(TAG, "Deleting invalid widget " + widgetId);
+ host.deleteAppWidgetId(widgetId);
+ } catch (RuntimeException e) {
+ // Ignore
+ }
+ }
}
}
@@ -942,22 +979,16 @@
* launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}.
*/
@Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) {
- db.beginTransaction();
- Cursor c = null;
- SQLiteStatement updateStmt = null;
-
- try {
- // Only consider the primary user as other users can't have a shortcut.
- long userSerial = getDefaultUserSerial();
- c = db.query(Favorites.TABLE_NAME, new String[] {
- Favorites._ID,
- Favorites.INTENT,
- }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial,
- null, null, null, null);
-
- updateStmt = db.compileStatement("UPDATE favorites SET itemType="
- + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?");
-
+ try (SQLiteTransaction t = new SQLiteTransaction(db);
+ // Only consider the primary user as other users can't have a shortcut.
+ Cursor c = db.query(Favorites.TABLE_NAME,
+ new String[] { Favorites._ID, Favorites.INTENT},
+ "itemType=" + Favorites.ITEM_TYPE_SHORTCUT +
+ " AND profileId=" + getDefaultUserSerial(),
+ null, null, null, null);
+ SQLiteStatement updateStmt = db.compileStatement("UPDATE favorites SET itemType="
+ + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?")
+ ) {
final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
@@ -979,17 +1010,9 @@
updateStmt.bindLong(1, id);
updateStmt.executeUpdateDelete();
}
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.w(TAG, "Error deduping shortcuts", ex);
- } finally {
- db.endTransaction();
- if (c != null) {
- c.close();
- }
- if (updateStmt != null) {
- updateStmt.close();
- }
}
}
@@ -997,26 +1020,17 @@
* Recreates workspace table and migrates data to the new table.
*/
public boolean recreateWorkspaceTable(SQLiteDatabase db) {
- db.beginTransaction();
- try {
- Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
+ final ArrayList<Long> sortedIDs;
+
+ try (Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
new String[] {LauncherSettings.WorkspaceScreens._ID},
null, null, null, null,
- LauncherSettings.WorkspaceScreens.SCREEN_RANK);
- ArrayList<Long> sortedIDs = new ArrayList<Long>();
- long maxId = 0;
- try {
- while (c.moveToNext()) {
- Long id = c.getLong(0);
- if (!sortedIDs.contains(id)) {
- sortedIDs.add(id);
- maxId = Math.max(maxId, id);
- }
- }
- } finally {
- c.close();
+ LauncherSettings.WorkspaceScreens.SCREEN_RANK)) {
+ // Use LinkedHashSet so that ordering is preserved
+ sortedIDs = new ArrayList<>(
+ LauncherDbUtils.iterateCursor(c, 0, new LinkedHashSet<Long>()));
}
-
db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
addWorkspacesTable(db, false);
@@ -1029,21 +1043,18 @@
addModifiedTime(values);
db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values);
}
- db.setTransactionSuccessful();
- mMaxScreenId = maxId;
+ t.commit();
+ mMaxScreenId = sortedIDs.isEmpty() ? 0 : Collections.max(sortedIDs);
} catch (SQLException ex) {
// Old version remains, which means we wipe old data
Log.e(TAG, ex.getMessage(), ex);
return false;
- } finally {
- db.endTransaction();
}
return true;
}
@Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
if (addRankColumn) {
// Insert new column for holding rank
db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;");
@@ -1062,13 +1073,11 @@
}
c.close();
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
// Old version remains, which means we wipe old data
Log.e(TAG, ex.getMessage(), ex);
return false;
- } finally {
- db.endTransaction();
}
return true;
}
@@ -1078,16 +1087,13 @@
}
private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
db.execSQL("ALTER TABLE favorites ADD COLUMN "
+ columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";");
- db.setTransactionSuccessful();
+ t.commit();
} catch (SQLException ex) {
Log.e(TAG, ex.getMessage(), ex);
return false;
- } finally {
- db.endTransaction();
}
return true;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index b25b256..87f62eb 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -22,8 +22,6 @@
import android.os.Bundle;
import android.provider.BaseColumns;
-import com.android.launcher3.config.ProviderConfig;
-
/**
* Settings related utilities.
*/
@@ -101,7 +99,7 @@
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
- ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
* The rank of this screen -- ie. how it is ordered relative to the other screens.
@@ -121,7 +119,7 @@
* The content:// style URL for this table
*/
public static final Uri CONTENT_URI = Uri.parse("content://" +
- ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
+ LauncherProvider.AUTHORITY + "/" + TABLE_NAME);
/**
* The content:// style URL for a given row, identified by its id.
@@ -131,7 +129,7 @@
* @return The unique content URL for the specified row.
*/
public static Uri getContentUri(long id) {
- return Uri.parse("content://" + ProviderConfig.AUTHORITY +
+ return Uri.parse("content://" + LauncherProvider.AUTHORITY +
"/" + TABLE_NAME + "/" + id);
}
@@ -155,6 +153,18 @@
}
}
+ static final String itemTypeToString(int type) {
+ switch(type) {
+ case ITEM_TYPE_APPLICATION: return "APP";
+ case ITEM_TYPE_SHORTCUT: return "SHORTCUT";
+ case ITEM_TYPE_FOLDER: return "FOLDER";
+ case ITEM_TYPE_APPWIDGET: return "WIDGET";
+ case ITEM_TYPE_CUSTOM_APPWIDGET: return "CUSTOMWIDGET";
+ case ITEM_TYPE_DEEP_SHORTCUT: return "DEEPSHORTCUT";
+ default: return String.valueOf(type);
+ }
+ }
+
/**
* The screen holding the favorite (if container is CONTAINER_DESKTOP)
* <P>Type: INTEGER</P>
@@ -280,7 +290,7 @@
public static final class Settings {
public static final Uri CONTENT_URI = Uri.parse("content://" +
- ProviderConfig.AUTHORITY + "/settings");
+ LauncherProvider.AUTHORITY + "/settings");
public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
public static final String METHOD_WAS_EMPTY_DB_CREATED = "get_empty_db_flag";
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 39c466d..e7349f0 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -31,8 +31,8 @@
import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetsContainerView;
@@ -127,12 +127,8 @@
/**
* Starts an animation to the apps view.
- *
- * @param startSearchAfterTransition Immediately starts app search after the transition to
- * All Apps is completed.
*/
- public void startAnimationToAllApps(
- final boolean animated, final boolean startSearchAfterTransition) {
+ public void startAnimationToAllApps(final boolean animated) {
final AllAppsContainerView toView = mLauncher.getAppsView();
final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@@ -156,9 +152,6 @@
@Override
void onTransitionComplete() {
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
- if (startSearchAfterTransition) {
- toView.startAppsSearch();
- }
}
};
int animType = CIRCULAR_REVEAL;
diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java
index 4ca0a59..5094682 100644
--- a/src/com/android/launcher3/MainThreadExecutor.java
+++ b/src/com/android/launcher3/MainThreadExecutor.java
@@ -18,14 +18,14 @@
import android.os.Looper;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
/**
* An executor service that executes its tasks on the main thread.
*
* Shutting down this executor is not supported.
*/
-public class MainThreadExecutor extends LooperExecuter {
+public class MainThreadExecutor extends LooperExecutor {
public MainThreadExecutor() {
super(Looper.getMainLooper());
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index fb6a611..dcc7a9d 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -24,7 +24,6 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
@@ -49,7 +48,7 @@
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.LauncherEdgeEffect;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -69,6 +68,11 @@
public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
+ // Overscroll constants
+ private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
+ private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
+
private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
// The page is moved more than halfway, automatically move to the next page on touch up.
private static final float SIGNIFICANT_MOVE_THRESHOLD = 0.4f;
@@ -144,6 +148,13 @@
protected boolean mWasInOverscroll = false;
+ // mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
+ // it is equal to the scaled overscroll position. We use a separate value so as to prevent
+ // the screens from continuing to translate beyond the normal bounds.
+ protected int mOverScrollX;
+
+ protected int mUnboundedScrollX;
+
// Page Indicator
@Thunk int mPageIndicatorViewId;
protected PageIndicator mPageIndicator;
@@ -183,10 +194,6 @@
protected final Rect mInsets = new Rect();
protected final boolean mIsRtl;
- // Edge effect
- private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
- private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
-
public PagedView(Context context) {
this(context, null);
}
@@ -226,11 +233,8 @@
mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
setOnHierarchyChangeListener(this);
setWillNotDraw(false);
- }
- protected void setEdgeGlowColor(int color) {
- mEdgeGlowLeft.setColor(color);
- mEdgeGlowRight.setColor(color);
+ int edgeEffectColor = Themes.getAttrColor(getContext(), android.R.attr.colorEdgeEffect);
}
protected void setDefaultInterpolator(Interpolator interpolator) {
@@ -404,9 +408,10 @@
if (getChildCount() == 0) {
return;
}
+ int prevPage = mCurrentPage;
mCurrentPage = validateNewPage(currentPage);
updateCurrentPageScroll();
- notifyPageSwitchListener();
+ notifyPageSwitchListener(prevPage);
invalidate();
}
@@ -414,7 +419,7 @@
* Should be called whenever the page changes. In the case of a scroll, we wait until the page
* has settled.
*/
- protected void notifyPageSwitchListener() {
+ protected void notifyPageSwitchListener(int prevPage) {
updatePageIndicator();
}
@@ -476,7 +481,7 @@
}
protected int getUnboundedScrollX() {
- return getScrollX();
+ return mUnboundedScrollX;
}
@Override
@@ -499,6 +504,8 @@
x = Math.max(x, mFreeScrollMinScrollX);
}
+ mUnboundedScrollX = x;
+
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
if (isXBeforeFirstPage) {
@@ -526,6 +533,7 @@
overScroll(0);
mWasInOverscroll = false;
}
+ mOverScrollX = x;
super.scrollTo(x, y);
}
@@ -565,7 +573,8 @@
if (mScroller.computeScrollOffset()) {
// Don't bother scrolling if the page does not need to be moved
if (getUnboundedScrollX() != mScroller.getCurrX()
- || getScrollY() != mScroller.getCurrY()) {
+ || getScrollY() != mScroller.getCurrY()
+ || mOverScrollX != mScroller.getCurrX()) {
float scaleX = mFreeScroll ? getScaleX() : 1f;
int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX));
scrollTo(scrollX, mScroller.getCurrY());
@@ -577,9 +586,10 @@
} else if (mNextPage != INVALID_PAGE && shouldInvalidate) {
sendScrollAccessibilityEvent();
+ int prevPage = mCurrentPage;
mCurrentPage = validateNewPage(mNextPage);
mNextPage = INVALID_PAGE;
- notifyPageSwitchListener();
+ notifyPageSwitchListener(prevPage);
// We don't want to trigger a page end moving unless the page has settled
// and the user has stopped scrolling
@@ -973,47 +983,6 @@
}
@Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- if (getPageCount() > 0) {
- if (!mEdgeGlowLeft.isFinished()) {
- final int restoreCount = canvas.save();
- Rect display = mViewport;
- canvas.translate(display.left, display.top);
- canvas.rotate(270);
-
- getEdgeVerticalPosition(sTmpIntPoint);
- canvas.translate(display.top - sTmpIntPoint[1], 0);
- mEdgeGlowLeft.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
- if (mEdgeGlowLeft.draw(canvas)) {
- postInvalidateOnAnimation();
- }
- canvas.restoreToCount(restoreCount);
- }
- if (!mEdgeGlowRight.isFinished()) {
- final int restoreCount = canvas.save();
- Rect display = mViewport;
- canvas.translate(display.left + mPageScrolls[mIsRtl ? 0 : (getPageCount() - 1)], display.top);
- canvas.rotate(90);
-
- getEdgeVerticalPosition(sTmpIntPoint);
-
- canvas.translate(sTmpIntPoint[0] - display.top, -display.width());
- mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
- if (mEdgeGlowRight.draw(canvas)) {
- postInvalidateOnAnimation();
- }
- canvas.restoreToCount(restoreCount);
- }
- }
- }
-
- /**
- * Returns the top and bottom position for the edge effect.
- */
- protected abstract void getEdgeVerticalPosition(int[] pos);
-
- @Override
public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
int page = indexToPage(indexOfChild(child));
if (page != mCurrentPage || !mScroller.isFinished()) {
@@ -1335,6 +1304,29 @@
}
}
+ // This curve determines how the effect of scrolling over the limits of the page dimishes
+ // as the user pulls further and further from the bounds
+ private float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ protected float acceleratedOverFactor(float amount) {
+ int screenSize = getViewportWidth();
+
+ // We want to reach the max over scroll effect when the user has
+ // over scrolled half the size of the screen
+ float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
+
+ if (Float.compare(f, 0f) == 0) return 0;
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+ return f;
+ }
+
// While layout transitions are occurring, a child's position may stray from its baseline
// position. This method returns the magnitude of this stray at any given time.
public int getLayoutTransitionOffsetForPage(int index) {
@@ -1356,13 +1348,25 @@
protected void dampedOverScroll(float amount) {
int screenSize = getViewportWidth();
+
float f = (amount / screenSize);
- if (f < 0) {
- mEdgeGlowLeft.onPull(-f);
- } else if (f > 0) {
- mEdgeGlowRight.onPull(f);
+
+ if (Float.compare(f, 0f) == 0) return;
+
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
+ if (amount < 0) {
+ mOverScrollX = overScrollAmount;
+ super.scrollTo(mOverScrollX, getScrollY());
} else {
- return;
+ mOverScrollX = mMaxScrollX + overScrollAmount;
+ super.scrollTo(mOverScrollX, getScrollY());
}
invalidate();
}
@@ -1371,6 +1375,14 @@
dampedOverScroll(amount);
}
+ protected float maxOverScroll() {
+ // Using the formula in overScroll, assuming that f = 1.0 (which it should generally not
+ // exceed). Used to find out how much extra wallpaper we need for the over scroll effect
+ float f = 1.0f;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+ return OVERSCROLL_DAMP_FACTOR * f;
+ }
+
/**
* return true if freescroll has been enabled, false otherwise
*/
@@ -1599,7 +1611,7 @@
mTotalMotionX += Math.abs(mLastMotionX + mLastMotionXRemainder - x);
boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING &&
- Math.abs(velocityX) > mFlingThresholdVelocity;
+ shouldFlingForVelocity(velocityX);
if (!mFreeScroll) {
// In the case that the page is moved far to one direction and then is flung
@@ -1705,14 +1717,16 @@
return true;
}
+ protected boolean shouldFlingForVelocity(int velocityX) {
+ return Math.abs(velocityX) > mFlingThresholdVelocity;
+ }
+
private void resetTouchState() {
releaseVelocityTracker();
endReordering();
mCancelTap = false;
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
- mEdgeGlowLeft.onRelease();
- mEdgeGlowRight.onRelease();
}
/**
@@ -1826,7 +1840,18 @@
}
protected void snapToDestination() {
- snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+ snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
+ }
+
+ protected boolean isInOverScroll() {
+ return (mOverScrollX > mMaxScrollX || mOverScrollX < 0);
+ }
+
+ protected int getPageSnapDuration() {
+ if (isInOverScroll()) {
+ return OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION;
+ }
+ return PAGE_SNAP_ANIMATION_DURATION;
}
public static class ScrollInterpolator implements Interpolator {
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index b163464..de424ab 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -80,10 +80,13 @@
updateAppWidget(null);
setOnClickListener(mLauncher);
- // Load icon
- PackageItemInfo item = new PackageItemInfo(info.providerName.getPackageName());
- item.user = info.user;
- cache.updateIconInBackground(this, item);
+ if (info.pendingItemInfo == null) {
+ info.pendingItemInfo = new PackageItemInfo(info.providerName.getPackageName());
+ info.pendingItemInfo.user = info.user;
+ cache.updateIconInBackground(this, info.pendingItemInfo);
+ } else {
+ reapplyItemInfo(info.pendingItemInfo);
+ }
}
@Override
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
index f8196e5..c3d3bb3 100644
--- a/src/com/android/launcher3/PinchAnimationManager.java
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -56,11 +56,10 @@
private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
private static final int INDEX_HOTSEAT = 0;
- private static final int INDEX_QSB = 1;
- private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 2;
- private static final int INDEX_SCRIM = 3;
+ private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 1;
+ private static final int INDEX_SCRIM = 2;
- private final Animator[] mAnimators = new Animator[4];
+ private final Animator[] mAnimators = new Animator[3];
private Launcher mLauncher;
private Workspace mWorkspace;
@@ -196,8 +195,6 @@
private void animateHotseatAndQsb(boolean show) {
startAnimator(INDEX_HOTSEAT,
mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION);
- startAnimator(INDEX_QSB, mWorkspace.mQsbAlphaController.animateAlphaAtIndex(
- show ? 1 : 0, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE), THRESHOLD_ANIM_DURATION);
}
private void animateOverviewPanelButtons(boolean show) {
diff --git a/src/com/android/launcher3/PromiseAppInfo.java b/src/com/android/launcher3/PromiseAppInfo.java
new file mode 100644
index 0000000..07515d0
--- /dev/null
+++ b/src/com/android/launcher3/PromiseAppInfo.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;
+
+import android.content.Intent;
+import android.support.annotation.NonNull;
+
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.util.PackageManagerHelper;
+
+public class PromiseAppInfo extends AppInfo {
+
+ public int level = 0;
+
+ public PromiseAppInfo(@NonNull PackageInstallerCompat.PackageInstallInfo installInfo) {
+ componentName = installInfo.componentName;
+ intent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(componentName)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ }
+
+ @Override
+ public ShortcutInfo makeShortcut() {
+ ShortcutInfo shortcut = new ShortcutInfo(this);
+ shortcut.setInstallProgress(level);
+ // We need to update the component name when the apk is installed
+ shortcut.status |= ShortcutInfo.FLAG_AUTOINSTALL_ICON;
+ // Since the user is manually placing it on homescreen, it should not be auto-removed later
+ shortcut.status |= ShortcutInfo.FLAG_RESTORE_STARTED;
+ return shortcut;
+ }
+
+ public Intent getMarketIntent() {
+ return PackageManagerHelper.getMarketIntent(componentName.getPackageName());
+ }
+}
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 61bcc17..8caba75 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -67,18 +67,19 @@
SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
- if (TextUtils.isEmpty(info.getAppPackageName()) ||
- info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
- return;
+ if (Process.myUserHandle().equals(user)) {
+ if (TextUtils.isEmpty(info.getAppPackageName()) ||
+ info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
+ return;
+ }
}
- if (!Process.myUserHandle().equals(user)) {
- // Managed profile is handled using ManagedProfileHeuristic
- return;
- }
+ queueAppIconAddition(context, info.getAppPackageName(), user);
+ }
+ public static void queueAppIconAddition(Context context, String packageName, UserHandle user) {
List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
- .getActivityList(info.getAppPackageName(), user);
+ .getActivityList(packageName, user);
if (activities == null || activities.isEmpty()) {
// no activity found
return;
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 0902b20..b7b75f8 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -26,7 +26,6 @@
import android.preference.PreferenceFragment;
import android.provider.Settings;
import android.provider.Settings.System;
-import android.support.v4.os.BuildCompat;
import com.android.launcher3.graphics.IconShapeOverride;
@@ -85,7 +84,7 @@
}
Preference iconBadgingPref = findPreference(ICON_BADGING_PREFERENCE_KEY);
- if (!BuildCompat.isAtLeastO()) {
+ if (!Utilities.isAtLeastO()) {
getPreferenceScreen().removePreference(
findPreference(SessionCommitReceiver.ADD_ICON_PREFERENCE_KEY));
getPreferenceScreen().removePreference(iconBadgingPref);
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index f8742f8..fd708c0 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -120,7 +120,9 @@
// Center the icon/folder
int cHeight = getCellContentHeight();
int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
- int cellPaddingX = (int) (profile.edgeMarginPx / 2f);
+ int cellPaddingX = mContainerType == CellLayout.WORKSPACE
+ ? profile.workspaceCellPaddingXPx
+ : (int) (profile.edgeMarginPx / 2f);
child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
}
} else {
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 6f0417c..f0d9367 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -45,10 +45,10 @@
* be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
* parsing.
*/
- public static final int FLAG_AUTOINTALL_ICON = 2; //0B10;
+ public static final int FLAG_AUTOINSTALL_ICON = 2; //0B10;
/**
- * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINTALL_ICON}
+ * The icon is being installed. If {@link #FLAG_RESTORED_ICON} or {@link #FLAG_AUTOINSTALL_ICON}
* is set, then the icon is either being installed or is in a broken state.
*/
public static final int FLAG_INSTALL_SESSION_ACTIVE = 4; // 0B100;
@@ -185,7 +185,7 @@
public final boolean isPromise() {
- return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
+ return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON);
}
public int getInstallProgress() {
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0fac29f..45c14d6 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -28,10 +28,13 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ setupUi();
+ }
+
+ protected void setupUi() {
// Get the hover color
mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
-
- setDrawable(R.drawable.ic_uninstall_launcher);
+ setDrawable(R.drawable.ic_uninstall_shadow);
}
@Override
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b5c44bb..9100fe2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -28,6 +28,7 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
@@ -37,7 +38,6 @@
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;
@@ -51,7 +51,7 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
@@ -85,7 +85,7 @@
private static final Matrix sInverseMatrix = new Matrix();
public static boolean isAtLeastO() {
- return BuildCompat.isAtLeastO();
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
}
public static final boolean ATLEAST_NOUGAT_MR1 =
@@ -110,6 +110,7 @@
public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
public static final int COLOR_EXTRACTION_JOB_ID = 1;
+ public static final int WALLPAPER_COMPAT_JOB_ID = 2;
// These values are same as that in {@link AsyncTask}.
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
@@ -260,7 +261,7 @@
return scale;
}
- static boolean isSystemApp(Context context, Intent intent) {
+ public static boolean isSystemApp(Context context, Intent intent) {
PackageManager pm = context.getPackageManager();
ComponentName cn = intent.getComponent();
String packageName = null;
@@ -550,6 +551,11 @@
LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
}
+ public static SharedPreferences getDevicePrefs(Context context) {
+ return context.getSharedPreferences(
+ LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
+ }
+
public static boolean isPowerSaverOn(Context context) {
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
return powerManager.isPowerSaveMode();
@@ -571,7 +577,7 @@
try {
c.close();
} catch (IOException e) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.d(TAG, "Error closing", e);
}
}
@@ -647,4 +653,28 @@
hashSet.add(elem);
return hashSet;
}
+
+ /**
+ * @return creates a new alpha mask bitmap out of an existing bitmap
+ */
+ public static Bitmap convertToAlphaMask(Bitmap b, int applyAlpha) {
+ Bitmap a = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.ALPHA_8);
+ Canvas c = new Canvas(a);
+ Paint paint = new Paint();
+ paint.setAlpha(applyAlpha);
+ c.drawBitmap(b, 0f, 0f, paint);
+ return a;
+ }
+
+ /**
+ * @return a new white 1x1 bitmap with ALPHA_8
+ */
+ public static Bitmap createOnePixBitmap() {
+ Bitmap a = Bitmap.createBitmap(1, 1, Bitmap.Config.ALPHA_8);
+ Canvas c = new Canvas(a);
+ Paint paint = new Paint();
+ paint.setColor(Color.WHITE);
+ c.drawPaint(paint);
+ return a;
+ }
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index c525cd4..a65ea9b 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -27,7 +27,6 @@
import android.os.Handler;
import android.os.UserHandle;
import android.support.annotation.Nullable;
-import android.support.v4.graphics.ColorUtils;
import android.util.Log;
import android.util.LongSparseArray;
@@ -112,7 +111,7 @@
* sizes (landscape vs portrait).
*/
private static class CacheDb extends SQLiteCacheHelper {
- private static final int DB_VERSION = 6;
+ private static final int DB_VERSION = 9;
private static final String TABLE_NAME = "shortcut_and_widget_previews";
private static final String COLUMN_COMPONENT = "componentName";
@@ -388,10 +387,10 @@
drawable.setBounds(x, 0, x + previewWidth, previewHeight);
drawable.draw(c);
} else {
- final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
- RectF boxRect = drawBoxWithShadow(c, p, previewWidth, previewHeight);
+ RectF boxRect = drawBoxWithShadow(c, previewWidth, previewHeight);
// Draw horizontal and vertical lines to represent individual columns.
+ final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(mContext.getResources()
.getDimension(R.dimen.widget_preview_cell_divider_width));
@@ -431,28 +430,19 @@
return preview;
}
- private RectF drawBoxWithShadow(Canvas c, Paint p, int width, int height) {
+ private RectF drawBoxWithShadow(Canvas c, int width, int height) {
Resources res = mContext.getResources();
- float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
- float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
- float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
- RectF bounds = new RectF(shadowBlur, shadowBlur,
- width - shadowBlur, height - shadowBlur - keyShadowDistance);
- p.setColor(Color.WHITE);
+ ShadowGenerator.Builder builder = new ShadowGenerator.Builder(Color.WHITE);
+ builder.shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
+ builder.radius = res.getDimension(R.dimen.widget_preview_corner_radius);
+ builder.keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
- // Key shadow
- p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
- ShadowGenerator.KEY_SHADOW_ALPHA << 24);
- c.drawRoundRect(bounds, corner, corner, p);
-
- // Ambient shadow
- p.setShadowLayer(shadowBlur, 0, 0,
- ColorUtils.setAlphaComponent(Color.BLACK, ShadowGenerator.AMBIENT_SHADOW_ALPHA));
- c.drawRoundRect(bounds, corner, corner, p);
-
- p.clearShadowLayer();
- return bounds;
+ builder.bounds.set(builder.shadowBlur, builder.shadowBlur,
+ width - builder.shadowBlur,
+ height - builder.shadowBlur - builder.keyShadowDistance);
+ builder.drawShadow(c);
+ return builder.bounds;
}
private Bitmap generateShortcutPreview(BaseActivity launcher, ShortcutConfigActivityInfo info,
@@ -478,16 +468,16 @@
c.setBitmap(preview);
c.drawColor(0, PorterDuff.Mode.CLEAR);
}
- Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- RectF boxRect = drawBoxWithShadow(c, p, size, size);
+ RectF boxRect = drawBoxWithShadow(c, size, size);
Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow(
- mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, Build.VERSION_CODES.O);
+ mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext, 0);
Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
boxRect.set(0, 0, iconSize, iconSize);
boxRect.offset(padding, padding);
- c.drawBitmap(icon, src, boxRect, p);
+ c.drawBitmap(icon, src, boxRect,
+ new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
c.setBitmap(null);
return preview;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 36f2880..56a5f9e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -43,6 +43,7 @@
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.ViewDebug;
@@ -50,9 +51,7 @@
import android.view.accessibility.AccessibilityManager;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import android.widget.TextView;
import android.widget.Toast;
-
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.Launcher.LauncherOverlay;
import com.android.launcher3.UninstallDropTarget.DropTargetSource;
@@ -64,7 +63,6 @@
import com.android.launcher3.badge.FolderBadgeInfo;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
@@ -72,22 +70,22 @@
import com.android.launcher3.dragndrop.SpringLoadedDragController;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.MultiStateAlphaController;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.VerticalFlingDetector;
import com.android.launcher3.util.WallpaperOffsetInterpolator;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
-
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -111,7 +109,7 @@
* {@link #isFinishedSwitchingState()} ()} to return true. */
private static final float FINISHED_SWITCHING_STATE_TRANSITION_PROGRESS = 0.5f;
- private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
+ private static final boolean ENFORCE_DRAG_EVENT_ORDER = false;
private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
private static final int FADE_EMPTY_SCREEN_DURATION = 150;
@@ -137,8 +135,8 @@
private ShortcutAndWidgetContainer mDragSourceInternal;
- @Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
- @Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>();
+ @Thunk final LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
+ @Thunk final ArrayList<Long> mScreenOrder = new ArrayList<>();
@Thunk Runnable mRemoveEmptyScreenRunnable;
@Thunk boolean mDeferRemoveExtraEmptyScreen = false;
@@ -174,7 +172,7 @@
*/
private CellLayout mDropToLayout = null;
- @Thunk Launcher mLauncher;
+ @Thunk final Launcher mLauncher;
@Thunk DragController mDragController;
// These are temporary variables to prevent having to allocate a new object just to
@@ -183,10 +181,10 @@
private final int[] mTempXY = new int[2];
@Thunk float[] mDragViewVisualCenter = new float[2];
- private float[] mTempTouchCoordinates = new float[2];
+ private final float[] mTempTouchCoordinates = new float[2];
private SpringLoadedDragController mSpringLoadedDragController;
- private float mOverviewModeShrinkFactor;
+ private final float mOverviewModeShrinkFactor;
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
@@ -226,21 +224,13 @@
/**
* These values correspond to {@link Direction#X} & {@link Direction#Y}
*/
- private float[] mPageAlpha = new float[] {1, 1};
+ 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 float[] mHotseatAlpha = new float[] {1, 1, 1};
-
- public static final int QSB_ALPHA_INDEX_STATE_CHANGE = 0;
- public static final int QSB_ALPHA_INDEX_Y_TRANSLATION = 1;
- public static final int QSB_ALPHA_INDEX_PAGE_SCROLL = 2;
- public static final int QSB_ALPHA_INDEX_OVERLAY_SCROLL = 3;
-
-
- MultiStateAlphaController mQsbAlphaController;
+ private final float[] mHotseatAlpha = new float[] {1, 1, 1};
@ViewDebug.ExportedProperty(category = "launcher")
private State mState = State.NORMAL;
@@ -252,7 +242,7 @@
private boolean mStripScreensOnPageStopMoving = false;
private DragPreviewProvider mOutlineProvider = null;
- private boolean mWorkspaceFadeInAdjacentScreens;
+ private final boolean mWorkspaceFadeInAdjacentScreens;
final WallpaperOffsetInterpolator mWallpaperOffset;
private boolean mUnlockWallpaperFromDefaultPageOnLayout;
@@ -265,7 +255,7 @@
public static final int REORDER_TIMEOUT = 350;
private final Alarm mFolderCreationAlarm = new Alarm();
private final Alarm mReorderAlarm = new Alarm();
- private FolderIcon.PreviewBackground mFolderCreateBg;
+ private PreviewBackground mFolderCreateBg;
private FolderIcon mDragOverFolderIcon = null;
private boolean mCreateUserFolderOnDrop = false;
private boolean mAddToExistingFolderOnDrop = false;
@@ -297,7 +287,7 @@
@Thunk int mLastReorderY = -1;
private SparseArray<Parcelable> mSavedStates;
- private final ArrayList<Integer> mRestoredPages = new ArrayList<Integer>();
+ private final ArrayList<Integer> mRestoredPages = new ArrayList<>();
private float mCurrentScale;
private float mTransitionProgress;
@@ -311,19 +301,16 @@
boolean mScrollInteractionBegan;
boolean mStartedSendingScrollEvents;
float mLastOverlayScroll = 0;
- // Total over scrollX in the overlay direction.
- private int mUnboundedScrollX;
+ boolean mOverlayShown = false;
+
private boolean mForceDrawAdjacentPages = false;
// Total over scrollX in the overlay direction.
private float mOverlayTranslation;
- private int mFirstPageScrollX;
- private boolean mIgnoreQsbScroll;
// Handles workspace state transitions
- private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
+ private final WorkspaceStateTransitionAnimation mStateTransitionAnimation;
private AccessibilityDelegate mPagesAccessibilityDelegate;
- private OnStateChangeListener mOnStateChangeListener;
/**
* Used to inflate the Workspace from XML.
@@ -378,10 +365,6 @@
}
}
- public void setOnStateChangeListener(OnStateChangeListener listener) {
- mOnStateChangeListener = listener;
- }
-
/**
* Estimates the size of an item using spans: hSpan, vSpan.
*
@@ -451,7 +434,7 @@
mLauncher.lockScreenOrientation();
mLauncher.onInteractionBegin();
// Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
- InstallShortcutReceiver.enableInstallQueue();
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
// Do not add a new page if it is a accessible drag which was not started by the workspace.
// We do not support accessibility drag from other sources and instead provide a direct
@@ -504,7 +487,8 @@
mLauncher.unlockScreenOrientation(false);
// Re-enable any Un/InstallShortcutReceiver and now process any queued items
- InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_DRAG_AND_DROP, getContext());
mOutlineProvider = null;
mDragInfo = null;
@@ -529,15 +513,12 @@
// Set the wallpaper dimensions when Launcher starts up
setWallpaperDimension();
-
- setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
}
@Override
public void initParentViews(View parent) {
super.initParentViews(parent);
mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
- mQsbAlphaController = new MultiStateAlphaController(mLauncher.getQsbContainer(), 4);
}
private int getDefaultPage() {
@@ -577,11 +558,6 @@
return mTouchState != TOUCH_STATE_REST;
}
- private int getEmbeddedQsbId() {
- return mLauncher.getDeviceProfile().isVerticalBarLayout()
- ? R.id.qsb_container : R.id.workspace_blocked_row;
- }
-
/**
* Initializes and binds the first page
* @param qsb an existing qsb to recycle or null.
@@ -623,41 +599,17 @@
if (qsb == null) {
// In transposed layout, we add the QSB in the Grid. As workspace does not touch the
// edges, we do not need a full width QSB.
- qsb = mLauncher.getLayoutInflater().inflate(
- mLauncher.getDeviceProfile().isVerticalBarLayout()
- ? R.layout.qsb_container : R.layout.qsb_blocker_view,
- firstPage, false);
+ qsb = LayoutInflater.from(getContext())
+ .inflate(R.layout.qsb_container,firstPage, false);
}
CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
lp.canReorder = false;
- if (!firstPage.addViewToCellLayout(qsb, 0, getEmbeddedQsbId(), lp, true)) {
+ if (!firstPage.addViewToCellLayout(qsb, 0, R.id.qsb_container, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // Update the QSB to match the cell height. This is treating the QSB essentially as a child
- // of workspace despite that it's not a true child.
- // Note that it relies on the strict ordering of measuring the workspace before the QSB
- // at the dragLayer level.
- // Only measure the QSB when the view is enabled
- if (FeatureFlags.QSB_ON_FIRST_SCREEN && getChildCount() > 0) {
- CellLayout firstPage = (CellLayout) getChildAt(0);
- int cellHeight = firstPage.getCellHeight();
-
- View qsbContainer = mLauncher.getQsbContainer();
- ViewGroup.LayoutParams lp = qsbContainer.getLayoutParams();
- if (cellHeight > 0 && lp.height != cellHeight) {
- lp.height = cellHeight;
- qsbContainer.setLayoutParams(lp);
- }
- }
- }
-
public void removeAllWorkspaceScreens() {
// Disable all layout transitions before removing all pages to ensure that we don't get the
// transition animations competing with us changing the scroll when we add pages or the
@@ -671,7 +623,7 @@
}
// Recycle the QSB widget
- View qsb = findViewById(getEmbeddedQsbId());
+ View qsb = findViewById(R.id.qsb_container);
if (qsb != null) {
((ViewGroup) qsb.getParent()).removeView(qsb);
}
@@ -709,7 +661,7 @@
// Inflate the cell layout, but do not add it automatically so that we can get the newly
// created CellLayout.
- CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
+ CellLayout newScreen = (CellLayout) LayoutInflater.from(getContext()).inflate(
R.layout.workspace_screen, this, false /* attachToRoot */);
newScreen.setOnLongClickListener(mLongClickListener);
newScreen.setOnClickListener(mLauncher);
@@ -727,7 +679,7 @@
public void createCustomContentContainer() {
CellLayout customScreen = (CellLayout)
- mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
+ LayoutInflater.from(getContext()).inflate(R.layout.workspace_screen, this, false);
customScreen.disableDragTarget();
customScreen.disableJailContent();
@@ -854,7 +806,7 @@
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
// Update the model if we have changed any screens
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
}
}
@@ -956,7 +908,6 @@
return -1;
}
- int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -968,7 +919,7 @@
mScreenOrder.add(newId);
// Update the model for the new screen
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
return newId;
}
@@ -1013,7 +964,7 @@
}
int currentPage = getNextPage();
- ArrayList<Long> removeScreens = new ArrayList<Long>();
+ ArrayList<Long> removeScreens = new ArrayList<>();
int total = mWorkspaceScreens.size();
for (int i = 0; i < total; i++) {
long id = mWorkspaceScreens.keyAt(i);
@@ -1057,7 +1008,7 @@
if (!removeScreens.isEmpty()) {
// Update the model if we have changed any screens
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
}
if (pageShift >= 0) {
@@ -1082,7 +1033,7 @@
/**
* Adds the specified child in the specified screen based on the {@param info}
- * See {@link #addInScreen}.
+ * See {@link #addInScreen(View, long, long, int, int, int, int)}.
*/
public void addInScreen(View child, ItemInfo info) {
addInScreen(child, info.container, info.screenId, info.cellX, info.cellY,
@@ -1371,18 +1322,10 @@
onOverlayScrollChanged(0);
}
- @Override
- protected int getUnboundedScrollX() {
- if (isScrollingOverlay()) {
- return mUnboundedScrollX;
- }
-
- return super.getUnboundedScrollX();
- }
private boolean isScrollingOverlay() {
return mLauncherOverlay != null &&
- ((mIsRtl && mUnboundedScrollX > mMaxScrollX) || (!mIsRtl && mUnboundedScrollX < 0));
+ ((mIsRtl && getUnboundedScrollX() > mMaxScrollX) || (!mIsRtl && getUnboundedScrollX() < 0));
}
@Override
@@ -1402,22 +1345,8 @@
}
@Override
- public void scrollTo(int x, int y) {
- mUnboundedScrollX = x;
- super.scrollTo(x, y);
- }
-
- private void onWorkspaceOverallScrollChanged() {
- if (!mIgnoreQsbScroll) {
- mLauncher.getQsbContainer().setTranslationX(
- mOverlayTranslation + mFirstPageScrollX - getScrollX());
- }
- }
-
- @Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
- onWorkspaceOverallScrollChanged();
// Update the page indicator progress.
boolean isTransitioning = mIsSwitchingState
@@ -1465,12 +1394,33 @@
}
}
+ @Override
+ protected boolean shouldFlingForVelocity(int velocityX) {
+ // When the overlay is moving, the fling or settle transition is controlled by the overlay.
+ return Float.compare(mOverlayTranslation, 0) == 0 &&
+ super.shouldFlingForVelocity(velocityX);
+ }
+
private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f);
/**
* The overlay scroll is being controlled locally, just update our overlay effect
*/
public void onOverlayScrollChanged(float scroll) {
+
+ if (Float.compare(scroll, 1f) == 0) {
+ if (!mOverlayShown) {
+ mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+ Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
+ }
+ mOverlayShown = true;
+ } else if (Float.compare(scroll, 0f) == 0) {
+ if (mOverlayShown) {
+ mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+ Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
+ }
+ mOverlayShown = false;
+ }
float offset = 0f;
float slip = 0f;
@@ -1491,9 +1441,6 @@
// device I've tried, translating the launcher causes things to get quite laggy.
setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
- onWorkspaceOverallScrollChanged();
-
- mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_OVERLAY_SCROLL);
}
/**
@@ -1503,9 +1450,6 @@
*/
public void setWorkspaceYTranslationAndAlpha(float translation, float alpha) {
setWorkspaceTranslationAndAlpha(Direction.Y, translation, alpha);
-
- mLauncher.getQsbContainer().setTranslationY(translation);
- mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_Y_TRANSLATION);
}
/**
@@ -1587,16 +1531,13 @@
}
@Override
- protected void getEdgeVerticalPosition(int[] pos) {
- View child = getChildAt(getPageCount() - 1);
- pos[0] = child.getTop();
- pos[1] = child.getBottom();
- }
-
- @Override
- protected void notifyPageSwitchListener() {
- super.notifyPageSwitchListener();
-
+ protected void notifyPageSwitchListener(int prevPage) {
+ super.notifyPageSwitchListener(prevPage);
+ if (prevPage != mCurrentPage) {
+ int swipeDirection = (prevPage < mCurrentPage) ? Action.Direction.RIGHT : Action.Direction.LEFT;
+ mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
+ swipeDirection, ContainerType.WORKSPACE, prevPage);
+ }
if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
mCustomContentShowing = true;
if (mCustomContentCallbacks != null) {
@@ -1701,10 +1642,6 @@
float scrollProgress = getScrollProgress(screenCenter, child, i);
float alpha = 1 - Math.abs(scrollProgress);
child.getShortcutsAndWidgets().setAlpha(alpha);
-
- if (isQsbContainerPage(i)) {
- mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_PAGE_SCROLL);
- }
}
}
}
@@ -1798,34 +1735,6 @@
mWallpaperOffset.jumpToFinal();
}
super.onLayout(changed, left, top, right, bottom);
- mFirstPageScrollX = getScrollForPage(0);
- onWorkspaceOverallScrollChanged();
-
- final LayoutTransition transition = getLayoutTransition();
- // If the transition is running defer updating max scroll, as some empty pages could
- // still be present, and a max scroll change could cause sudden jumps in scroll.
- if (transition != null && transition.isRunning()) {
- transition.addTransitionListener(new LayoutTransition.TransitionListener() {
-
- @Override
- public void startTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {
- mIgnoreQsbScroll = true;
- }
-
- @Override
- public void endTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {
- // Wait until all transitions are complete.
- if (!transition.isRunning()) {
- mIgnoreQsbScroll = false;
- transition.removeTransitionListener(this);
- mFirstPageScrollX = getScrollForPage(0);
- onWorkspaceOverallScrollChanged();
- }
- }
- });
- }
updatePageAlphaValues();
}
@@ -1978,14 +1887,21 @@
return;
}
+ ArrayList<Long> prevScreenOrder = (ArrayList<Long>) mScreenOrder.clone();
mScreenOrder.clear();
int count = getChildCount();
for (int i = 0; i < count; i++) {
CellLayout cl = ((CellLayout) getChildAt(i));
mScreenOrder.add(getIdForScreen(cl));
}
- mLauncher.getUserEventDispatcher().logOverviewReorder();
- mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
+
+ for (int i = 0; i < prevScreenOrder.size(); i++) {
+ if (mScreenOrder.get(i) != prevScreenOrder.get(i)) {
+ mLauncher.getUserEventDispatcher().logOverviewReorder();
+ break;
+ }
+ }
+ LauncherModel.updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
// Re-enable auto layout transitions for page deletion.
enableLayoutTransitions();
@@ -2064,10 +1980,6 @@
mLauncher.notifyWidgetProvidersChanged();
}
- if (mOnStateChangeListener != null) {
- mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
- }
-
onPrepareStateTransition(mState.hasMultipleVisiblePages);
StateTransitionListener listener = new StateTransitionListener();
@@ -2175,27 +2087,9 @@
}
}
- /**
- * Returns the drawable for the given text view.
- */
- public static Drawable getTextViewIcon(TextView tv) {
- final Drawable[] drawables = tv.getCompoundDrawables();
- for (int i = 0; i < drawables.length; i++) {
- if (drawables[i] != null) {
- return drawables[i];
- }
- }
- return null;
- }
-
public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
View child = cellInfo.cell;
- // Make sure the drag was started by a long press as opposed to a long click.
- if (!child.isInTouchMode()) {
- return;
- }
-
mDragInfo = cellInfo;
child.setVisibility(INVISIBLE);
@@ -2316,8 +2210,8 @@
mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
}
- int spanX = 1;
- int spanY = 1;
+ int spanX;
+ int spanY;
if (mDragInfo != null) {
final CellLayout.CellInfo dragCellInfo = mDragInfo;
spanX = dragCellInfo.spanX;
@@ -2472,7 +2366,7 @@
// In order to keep everything continuous, we hand off the currently rendered
// folder background to the newly created icon. This preserves animation state.
fi.setFolderBackground(mFolderCreateBg);
- mFolderCreateBg = new FolderIcon.PreviewBackground();
+ mFolderCreateBg = new PreviewBackground();
fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
postAnimationRunnable);
} else {
@@ -2620,7 +2514,7 @@
CellLayout parentCell = getParentCellLayoutForView(cell);
if (parentCell != null) {
parentCell.removeView(cell);
- } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new NullPointerException("mDragInfo.cell has null parent");
}
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2953,7 +2847,7 @@
ItemInfo item = d.dragInfo;
if (item == null) {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new NullPointerException("DragObject has null info");
}
return;
@@ -3149,22 +3043,19 @@
}
class FolderCreationAlarmListener implements OnAlarmListener {
- CellLayout layout;
- int cellX;
- int cellY;
+ final CellLayout layout;
+ final int cellX;
+ final int cellY;
- FolderIcon.PreviewBackground bg = new FolderIcon.PreviewBackground();
+ final PreviewBackground bg = new PreviewBackground();
public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
this.layout = layout;
this.cellX = cellX;
this.cellY = cellY;
- DeviceProfile grid = mLauncher.getDeviceProfile();
BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
-
- bg.setup(getResources().getDisplayMetrics(), grid, null,
- cell.getMeasuredWidth(), cell.getPaddingTop());
+ bg.setup(mLauncher, null, cell.getMeasuredWidth(), cell.getPaddingTop());
// The full preview background should appear behind the icon
bg.isClipping = false;
@@ -3179,10 +3070,10 @@
}
class ReorderAlarmListener implements OnAlarmListener {
- float[] dragViewCenter;
- int minSpanX, minSpanY, spanX, spanY;
- DragObject dragObject;
- View child;
+ final float[] dragViewCenter;
+ final int minSpanX, minSpanY, spanX, spanY;
+ final DragObject dragObject;
+ final View child;
public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
int spanY, DragObject dragObject, View child) {
@@ -3274,7 +3165,7 @@
boolean findNearestVacantCell = true;
if (pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
- mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
+ mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
@@ -3340,7 +3231,7 @@
animationStyle, finalView, true);
} else {
// This is for other drag/drop cases, like dragging from All Apps
- View view = null;
+ View view;
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -3364,7 +3255,7 @@
// First we find the cell nearest to point at which the item is
// dropped, without any consideration to whether there is an item there.
if (touchXY != null) {
- mTargetCell = findNearestArea((int) touchXY[0], (int) touchXY[1], spanX, spanY,
+ mTargetCell = findNearestArea(touchXY[0], touchXY[1], spanX, spanY,
cellLayout, mTargetCell);
float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
mDragViewVisualCenter[1], mTargetCell);
@@ -3610,13 +3501,13 @@
mDragInfo.container, mDragInfo.screenId);
if (cellLayout != null) {
cellLayout.onDropChild(mDragInfo.cell);
- } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
throw new RuntimeException("Invalid state: cellLayout == null in "
+ "Workspace#onDropCompleted. Please file a bug. ");
- };
+ }
}
if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful))
- && mDragInfo.cell != null) {
+ && mDragInfo != null && mDragInfo.cell != null) {
mDragInfo.cell.setVisibility(VISIBLE);
}
mDragInfo = null;
@@ -3636,7 +3527,7 @@
CellLayout parentCell = getParentCellLayoutForView(v);
if (parentCell != null) {
parentCell.removeView(v);
- } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
// When an app is uninstalled using the drop target, we wait until resume to remove
// the icon. We also remove all the corresponding items from the workspace at
// {@link Launcher#bindComponentsRemoved}. That call can come before or after
@@ -3765,7 +3656,7 @@
* Returns a list of all the CellLayouts in the workspace.
*/
ArrayList<CellLayout> getWorkspaceAndHotseatCellLayouts() {
- ArrayList<CellLayout> layouts = new ArrayList<CellLayout>();
+ ArrayList<CellLayout> layouts = new ArrayList<>();
int screenCount = getChildCount();
for (int screen = 0; screen < screenCount; screen++) {
layouts.add(((CellLayout) getChildAt(screen)));
@@ -3907,7 +3798,7 @@
* @param view view for the shortcut
* @return true if done, false to continue the map
*/
- public boolean evaluate(ItemInfo info, View view);
+ boolean evaluate(ItemInfo info, View view);
}
/**
@@ -3928,7 +3819,7 @@
ItemInfo info = (ItemInfo) item.getTag();
if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
FolderIcon folder = (FolderIcon) item;
- ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+ ArrayList<View> folderChildren = folder.getFolder().getItemsInRankOrder();
// map over all the children in the folder
final int childCount = folderChildren.size();
for (int childIdx = 0; childIdx < childCount; childIdx++) {
@@ -3949,7 +3840,7 @@
void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
int total = shortcuts.size();
- final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(total);
+ final HashSet<ShortcutInfo> updates = new HashSet<>(total);
final HashSet<Long> folderIds = new HashSet<>();
for (int i = 0; i < total; i++) {
@@ -3965,7 +3856,7 @@
updates.contains(info)) {
ShortcutInfo si = (ShortcutInfo) info;
BubbleTextView shortcut = (BubbleTextView) v;
- Drawable oldIcon = getTextViewIcon(shortcut);
+ Drawable oldIcon = shortcut.getIcon();
boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
&& ((PreloadIconDrawable) oldIcon).hasNotCompleted();
shortcut.applyFromShortcutInfo(si, si.isPromise() != oldPromiseState);
@@ -4189,8 +4080,8 @@
private boolean mRefreshPending;
- public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
- LauncherAppWidgetHost host) {
+ DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
+ LauncherAppWidgetHost host) {
mInfos = infos;
mHost = host;
mHandler = new Handler();
@@ -4227,20 +4118,6 @@
}
}
- public interface OnStateChangeListener {
-
- /**
- * Called when the workspace state is changing.
- * @param toState final state
- * @param targetAnim animation which will be played during the transition or null.
- */
- void prepareStateChange(State toState, AnimatorSet targetAnim);
- }
-
- public static final boolean isQsbContainerPage(int pageNo) {
- return pageNo == 0;
- }
-
private class StateTransitionListener extends AnimatorListenerAdapter
implements AnimatorUpdateListener {
@Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 482a2c9..32deaf2 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -355,27 +355,10 @@
cl.setBackgroundAlpha(finalBackgroundAlpha);
cl.setShortcutAndWidgetAlpha(finalAlpha);
}
-
- if (Workspace.isQsbContainerPage(i) &&
- states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
- if (animated) {
- Animator anim = mWorkspace.mQsbAlphaController
- .animateAlphaAtIndex(finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
- anim.setDuration(duration);
- anim.setInterpolator(mZoomInInterpolator);
- mStateAnimator.play(anim);
- } else {
- mWorkspace.mQsbAlphaController.setAlphaAtIndex(
- finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
- }
- }
}
final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
- Animator qsbAlphaAnimation = mWorkspace.mQsbAlphaController
- .animateAlphaAtIndex(finalQsbAlpha, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE);
-
if (animated) {
Animator scale = LauncherAnimUtils.ofPropertyValuesHolder(mWorkspace,
new PropertyListBuilder().scale(mNewScale)
@@ -393,7 +376,6 @@
// For animation optimization, we may need to provide the Launcher transition
// with a set of views on which to force build and manage layers in certain scenarios.
layerViews.addView(overviewPanel);
- layerViews.addView(mLauncher.getQsbContainer());
layerViews.addView(mLauncher.getHotseat());
layerViews.addView(mWorkspace.getPageIndicator());
@@ -407,11 +389,9 @@
overviewPanelAlpha.setDuration(duration);
hotseatAlpha.setDuration(duration);
- qsbAlphaAnimation.setDuration(duration);
mStateAnimator.play(overviewPanelAlpha);
mStateAnimator.play(hotseatAlpha);
- mStateAnimator.play(qsbAlphaAnimation);
mStateAnimator.addListener(new AnimatorListenerAdapter() {
boolean canceled = false;
@Override
@@ -439,7 +419,6 @@
AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
mWorkspace.getPageIndicator().setShouldAutoHide(!states.stateIsSpringLoaded);
- qsbAlphaAnimation.end();
mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end();
mWorkspace.updateCustomContentVisibility();
mWorkspace.setScaleX(mNewScale);
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
index d271f1d..9c23c19 100644
--- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -17,8 +17,8 @@
package com.android.launcher3.accessibility;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.R;
+import com.android.launcher3.folder.FolderPagedView;
/**
* Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD in a folder.
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 70e5781..6cd086b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -18,7 +18,6 @@
import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.FolderInfo;
@@ -27,7 +26,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.PendingAddItemInfo;
import com.android.launcher3.R;
@@ -37,6 +35,7 @@
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.util.Thunk;
@@ -370,7 +369,7 @@
Folder folder = Folder.getOpen(mLauncher);
if (folder != null) {
- if (!folder.getItemsInReadingOrder().contains(item)) {
+ if (!folder.getItemsInRankOrder().contains(item)) {
folder.close(true);
folder = null;
}
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 9a23aa8..e6f120f 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -27,9 +27,9 @@
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
import com.android.launcher3.dragndrop.DragLayer;
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index c71bc31..3830a93 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -23,9 +23,12 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
import android.view.Gravity;
+import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
/**
* This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
@@ -35,7 +38,7 @@
public class AllAppsBackgroundDrawable extends Drawable {
/**
- * A helper class to positon and orient a drawable to be drawn.
+ * A helper class to position and orient a drawable to be drawn.
*/
protected static class TransformedImageDrawable {
private Drawable mImage;
@@ -48,9 +51,9 @@
* @param gravity If one of the Gravity center values, the x and y offset will take the width
* and height of the image into account to center the image to the offset.
*/
- public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
+ public TransformedImageDrawable(Context context, int resourceId, float xPct, float yPct,
int gravity) {
- mImage = res.getDrawable(resourceId);
+ mImage = context.getDrawable(resourceId);
mXPercent = xPct;
mYPercent = yPct;
mGravity = gravity;
@@ -97,19 +100,24 @@
public AllAppsBackgroundDrawable(Context context) {
Resources res = context.getResources();
- mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
- 0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
- mIcons = new TransformedImageDrawable[4];
- mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
- 0.375f, 0, Gravity.CENTER_HORIZONTAL);
- mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
- 0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
- mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
- 0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
- mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
- 0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
mHeight = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_height);
+
+ context = new ContextThemeWrapper(context,
+ Themes.getAttrBoolean(context, R.attr.isMainColorDark)
+ ? R.style.AllAppsEmptySearchBackground_Dark
+ : R.style.AllAppsEmptySearchBackground);
+ mHand = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_hand,
+ 0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
+ mIcons = new TransformedImageDrawable[4];
+ mIcons[0] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_1,
+ 0.375f, 0, Gravity.CENTER_HORIZONTAL);
+ mIcons[1] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_2,
+ 0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
+ mIcons[2] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_3,
+ 0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
+ mIcons[3] = new TransformedImageDrawable(context, R.drawable.ic_all_apps_bg_icon_4,
+ 0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
}
/**
@@ -119,7 +127,8 @@
int finalAlphaI = (int) (finalAlpha * 255f);
if (getAlpha() != finalAlphaI) {
mBackgroundAnim = cancelAnimator(mBackgroundAnim);
- mBackgroundAnim = ObjectAnimator.ofInt(this, "alpha", finalAlphaI);
+ mBackgroundAnim = ObjectAnimator.ofInt(this, LauncherAnimUtils.DRAWABLE_ALPHA,
+ finalAlphaI);
mBackgroundAnim.setDuration(duration);
mBackgroundAnim.start();
}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 2c7d156..499eb45 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,15 +20,10 @@
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
-import android.text.Spannable;
-import android.text.SpannableString;
import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -42,25 +37,22 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
-import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.discovery.AppDiscoveryItem;
-import com.android.launcher3.discovery.AppDiscoveryUpdateState;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.folder.Folder;
-import com.android.launcher3.graphics.TintedDrawableSpan;
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.PackageUserKey;
-import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@@ -68,26 +60,24 @@
* The all apps view container.
*/
public class AllAppsContainerView extends BaseContainerView implements DragSource,
- View.OnLongClickListener, AllAppsSearchBarController.Callbacks, Insettable {
+ View.OnLongClickListener, Insettable {
private final Launcher mLauncher;
private final AlphabeticalAppsList mApps;
private final AllAppsGridAdapter mAdapter;
- private final RecyclerView.LayoutManager mLayoutManager;
+ private final LinearLayoutManager mLayoutManager;
private AllAppsRecyclerView mAppsRecyclerView;
- private AllAppsSearchBarController mSearchBarController;
-
+ private SearchUiManager mSearchUiManager;
private View mSearchContainer;
- private int mSearchContainerMinHeight;
- private ExtendedEditText mSearchInput;
- private HeaderElevationController mElevationController;
private SpannableStringBuilder mSearchQueryBuilder = null;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
+ private SpringAnimationHandler mSpringAnimationHandler;
+
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -102,11 +92,10 @@
mLauncher = Launcher.getLauncher(context);
mApps = new AlphabeticalAppsList(context);
mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
+ mSpringAnimationHandler = mAdapter.getSpringAnimationHandler();
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mSearchQueryBuilder = new SpannableStringBuilder();
- mSearchContainerMinHeight
- = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
Selection.setSelection(mSearchQueryBuilder, 0);
}
@@ -148,7 +137,7 @@
*/
public void addApps(List<AppInfo> apps) {
mApps.addApps(apps);
- mSearchBarController.refreshSearchResult();
+ mSearchUiManager.refreshSearchResult();
}
/**
@@ -156,7 +145,18 @@
*/
public void updateApps(List<AppInfo> apps) {
mApps.updateApps(apps);
- mSearchBarController.refreshSearchResult();
+ mSearchUiManager.refreshSearchResult();
+ }
+
+ public void updatePromiseAppProgress(PromiseAppInfo app) {
+ int childCount = mAppsRecyclerView.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = mAppsRecyclerView.getChildAt(i);
+ if (child instanceof BubbleTextView && child.getTag() == app) {
+ BubbleTextView bubbleTextView = (BubbleTextView) child;
+ bubbleTextView.applyProgressLevel(app.level);
+ }
+ }
}
/**
@@ -164,53 +164,26 @@
*/
public void removeApps(List<AppInfo> apps) {
mApps.removeApps(apps);
- mSearchBarController.refreshSearchResult();
- }
-
- public void setSearchBarVisible(boolean visible) {
- if (visible) {
- mSearchBarController.setVisibility(View.VISIBLE);
- } else {
- mSearchBarController.setVisibility(View.INVISIBLE);
- }
- }
-
- /**
- * Sets the search bar that shows above the a-z list.
- */
- public void setSearchBarController(AllAppsSearchBarController searchController) {
- if (mSearchBarController != null) {
- throw new RuntimeException("Expected search bar controller to only be set once");
- }
- mSearchBarController = searchController;
- mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
- mAdapter.setSearchController(mSearchBarController);
- }
-
- /**
- * Scrolls this list view to the top.
- */
- public void scrollToTop() {
- mAppsRecyclerView.scrollToTop();
+ mSearchUiManager.refreshSearchResult();
}
/**
* Returns whether the view itself will handle the touch event or not.
*/
public boolean shouldContainerScroll(MotionEvent ev) {
- int[] point = new int[2];
- point[0] = (int) ev.getX();
- point[1] = (int) ev.getY();
- Utilities.mapCoordInSelfToDescendant(mAppsRecyclerView, this, point);
-
// IF the MotionEvent is inside the search box, and the container keeps on receiving
// touch input, container should move down.
if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
return true;
}
+ int[] point = new int[2];
+ point[0] = (int) ev.getX();
+ point[1] = (int) ev.getY();
+ Utilities.mapCoordInSelfToDescendant(
+ mAppsRecyclerView.getScrollBar(), mLauncher.getDragLayer(), point);
// IF the MotionEvent is inside the thumb, container should not be pulled down.
- if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
+ if (mAppsRecyclerView.getScrollBar().shouldBlockIntercept(point[0], point[1])) {
return false;
}
@@ -223,22 +196,12 @@
}
/**
- * Focuses the search field and begins an app search.
- */
- public void startAppsSearch() {
- if (mSearchBarController != null) {
- mSearchBarController.focusSearchField();
- }
- }
-
- /**
* Resets the state of AllApps.
*/
public void reset() {
// Reset the search bar and base recycler view after transitioning home
- scrollToTop();
- mSearchBarController.reset();
- mAppsRecyclerView.reset();
+ mAppsRecyclerView.scrollToTop();
+ mSearchUiManager.reset();
}
@Override
@@ -256,28 +219,21 @@
}
});
- mSearchContainer = findViewById(R.id.search_container);
- mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
-
- // Update the hint to contain the icon.
- // Prefix the original hint with two spaces. The first space gets replaced by the icon
- // using span. The second space is used for a singe space character between the hint
- // and the icon.
- SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
- spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
- 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
- mSearchInput.setHint(spanned);
-
- mElevationController = new HeaderElevationController(mSearchContainer);
-
// Load the all apps recycler view
- mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
+ mAppsRecyclerView = findViewById(R.id.apps_list_view);
mAppsRecyclerView.setApps(mApps);
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
- mAppsRecyclerView.addOnScrollListener(mElevationController);
- mAppsRecyclerView.setElevationController(mElevationController);
+ if (FeatureFlags.LAUNCHER3_PHYSICS) {
+ mAppsRecyclerView.setSpringAnimationHandler(mSpringAnimationHandler);
+ mAppsRecyclerView.addOnScrollListener(new SpringMotionOnScrollListener());
+ }
+
+ mSearchContainer = findViewById(R.id.search_container);
+ mSearchUiManager = (SearchUiManager) mSearchContainer;
+ mSearchUiManager.initialize(mApps, mAppsRecyclerView);
+
FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
@@ -291,18 +247,21 @@
}
}
+ public SearchUiManager getSearchUiManager() {
+ return mSearchUiManager;
+ }
+
@Override
public View getTouchDelegateTargetView() {
return mAppsRecyclerView;
}
@Override
- public void onBoundsChanged(Rect newBounds) { }
-
- @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 (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
if (mNumAppsPerRow != grid.inv.numColumns ||
mNumPredictedAppsPerRow != grid.inv.numColumns) {
@@ -313,22 +272,11 @@
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
}
- if (!grid.isVerticalBarLayout()) {
- MarginLayoutParams searchContainerLp =
- (MarginLayoutParams) mSearchContainer.getLayoutParams();
-
- searchContainerLp.height = mLauncher.getDragLayer().getInsets().top
- + mSearchContainerMinHeight;
- mSearchContainer.setLayoutParams(searchContainerLp);
- }
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}
// --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
-
- // Update the number of items in the grid before we measure the view
- grid.updateAppsViewNumCols();
if (mNumAppsPerRow != grid.allAppsNumCols ||
mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
mNumAppsPerRow = grid.allAppsNumCols;
@@ -345,31 +293,13 @@
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
- // Determine if the key event was actual text, if so, focus the search bar and then dispatch
- // the key normally so that it can process this key event
- if (!mSearchBarController.isSearchFieldFocused() &&
- event.getAction() == KeyEvent.ACTION_DOWN) {
- final int unicodeChar = event.getUnicodeChar();
- final boolean isKeyNotWhitespace = unicodeChar > 0 &&
- !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
- if (isKeyNotWhitespace) {
- boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
- event.getKeyCode(), event);
- if (gotKey && mSearchQueryBuilder.length() > 0) {
- mSearchBarController.focusSearchField();
- }
- }
- }
-
+ mSearchUiManager.preDispatchKeyEvent(event);
return super.dispatchKeyEvent(event);
}
@Override
public boolean onLongClick(final View v) {
- // Return early if this is not initiated from a touch
- if (!v.isInTouchMode()) return false;
// When we have exited all apps or are in transition, disregard long clicks
-
if (!mLauncher.isAppsViewVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled or we are already dragging
@@ -428,47 +358,17 @@
}
@Override
- public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
- if (apps != null) {
- mApps.setOrderedFilter(apps);
- mAppsRecyclerView.onSearchResultsChanged();
- mAdapter.setLastSearchQuery(query);
- }
- }
-
- @Override
- public void onAppDiscoverySearchUpdate(@Nullable AppDiscoveryItem app,
- @NonNull AppDiscoveryUpdateState state) {
- if (!mLauncher.isDestroyed()) {
- mApps.onAppDiscoverySearchUpdate(app, state);
- mAppsRecyclerView.onSearchResultsChanged();
- }
- }
-
- @Override
- public void clearSearchResult() {
- if (mApps.setOrderedFilter(null)) {
- mAppsRecyclerView.onSearchResultsChanged();
- }
-
- // Clear the search query
- mSearchQueryBuilder.clear();
- mSearchQueryBuilder.clearSpans();
- Selection.setSelection(mSearchQueryBuilder, 0);
- }
-
- @Override
public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
targetParent.containerType = mAppsRecyclerView.getContainerType(v);
}
- public boolean shouldRestoreImeState() {
- return !TextUtils.isEmpty(mSearchInput.getText());
- }
-
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = mLauncher.getDeviceProfile();
+ mAppsRecyclerView.setPadding(
+ mAppsRecyclerView.getPaddingLeft(), mAppsRecyclerView.getPaddingTop(),
+ mAppsRecyclerView.getPaddingRight(), insets.bottom);
+
if (grid.isVerticalBarLayout()) {
ViewGroup.MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.leftMargin = insets.left;
@@ -480,7 +380,8 @@
ViewGroup.LayoutParams navBarBgLp = navBarBg.getLayoutParams();
navBarBgLp.height = insets.bottom;
navBarBg.setLayoutParams(navBarBgLp);
- navBarBg.setVisibility(View.VISIBLE);
+ navBarBg.setVisibility(FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
+ ? View.INVISIBLE : View.VISIBLE);
}
}
@@ -498,4 +399,40 @@
}
}
}
+
+ public SpringAnimationHandler getSpringAnimationHandler() {
+ return mSpringAnimationHandler;
+ }
+
+ public class SpringMotionOnScrollListener extends RecyclerView.OnScrollListener {
+
+ private int mScrollState = RecyclerView.SCROLL_STATE_IDLE;
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ if (mScrollState == RecyclerView.SCROLL_STATE_DRAGGING
+ || (dx == 0 && dy == 0)) {
+ if (mSpringAnimationHandler.isRunning()){
+ mSpringAnimationHandler.skipToEnd();
+ }
+ return;
+ }
+
+ int first = mLayoutManager.findFirstVisibleItemPosition();
+ int last = mLayoutManager.findLastVisibleItemPosition();
+
+ // We only show the spring animation when at the top or bottom, so we wait until the
+ // first or last row is visible to ensure that all animations run in sync.
+ boolean scrollUp = dy < 0;
+ if ((first == 0 && scrollUp) || (last == mAdapter.getItemCount() - 1 && dy > 0)) {
+ mSpringAnimationHandler.animateToFinalPosition(0, scrollUp ? 1 : -1);
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ mScrollState = newState;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index a1ff822..e08cb15 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -16,11 +16,7 @@
package com.android.launcher3.allapps;
import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import com.android.launcher3.BaseRecyclerViewFastScrollBar;
-import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.util.Thunk;
import java.util.HashSet;
@@ -210,7 +206,9 @@
for (RecyclerView.ViewHolder viewHolder : mTrackedFastScrollViews) {
int pos = viewHolder.getAdapterPosition();
boolean isActive = false;
- if (mCurrentFastScrollSection != null && pos > -1) {
+ if (mCurrentFastScrollSection != null
+ && pos > RecyclerView.NO_POSITION
+ && pos < mApps.getAdapterItems().size()) {
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
isActive = item != null &&
mCurrentFastScrollSection.equals(item.sectionName) &&
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 59cac8d..ba4fbe0 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -18,7 +18,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
-import android.graphics.Point;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
@@ -33,13 +34,17 @@
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
-import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItemView;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.List;
@@ -64,21 +69,20 @@
// 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 under the search field
- public static final int VIEW_TYPE_SEARCH_DIVIDER = 1 << 6;
// The divider that separates prediction icons from the app list
- public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 7;
- public static final int VIEW_TYPE_APPS_LOADING_DIVIDER = 1 << 8;
- public static final int VIEW_TYPE_DISCOVERY_ITEM = 1 << 9;
+ 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;
// Common view type masks
- public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_DIVIDER
- | VIEW_TYPE_SEARCH_MARKET_DIVIDER
+ 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 interface BindViewCallback {
@@ -89,6 +93,7 @@
* ViewHolder for each icon.
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
+
public ViewHolder(View v) {
super(v);
}
@@ -160,11 +165,6 @@
}
return extraRows;
}
-
- @Override
- public int getPaddingBottom() {
- return mLauncher.getDragLayer().getInsets().bottom;
- }
}
/**
@@ -199,7 +199,6 @@
private int mAppsPerRow;
private BindViewCallback mBindViewCallback;
- private AllAppsSearchBarController mSearchController;
private OnFocusChangeListener mIconFocusListener;
// The text to show when there are no search results and no market search handler.
@@ -207,6 +206,8 @@
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
+ private SpringAnimationHandler<ViewHolder> mSpringAnimationHandler;
+
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
iconClickListener, View.OnLongClickListener iconLongClickListener) {
Resources res = launcher.getResources();
@@ -219,6 +220,14 @@
mLayoutInflater = LayoutInflater.from(launcher);
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
+ if (FeatureFlags.LAUNCHER3_PHYSICS) {
+ mSpringAnimationHandler = new SpringAnimationHandler<>(
+ SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
+ }
+ }
+
+ public SpringAnimationHandler getSpringAnimationHandler() {
+ return mSpringAnimationHandler;
}
public static boolean isDividerViewType(int viewType) {
@@ -241,8 +250,8 @@
mGridLayoutMgr.setSpanCount(appsPerRow);
}
- public void setSearchController(AllAppsSearchBarController searchController) {
- mSearchController = searchController;
+ public int getNumAppsPerRow() {
+ return mAppsPerRow;
}
public void setIconFocusListener(OnFocusChangeListener focusListener) {
@@ -256,7 +265,7 @@
public void setLastSearchQuery(String query) {
Resources res = mLauncher.getResources();
mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
- mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
+ mMarketSearchIntent = PackageManagerHelper.getMarketSearchIntent(mLauncher, query);
}
/**
@@ -282,12 +291,11 @@
R.layout.all_apps_icon, parent, false);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
- icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
- .getLongPressTimeout());
+ icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
icon.setOnFocusChangeListener(mIconFocusListener);
- // Ensure the all apps icon height matches the workspace icons
- icon.getLayoutParams().height = getCellSize().y;
+ // 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
@@ -308,9 +316,6 @@
}
});
return new ViewHolder(searchMarketView);
- case VIEW_TYPE_SEARCH_DIVIDER:
- return new ViewHolder(mLayoutInflater.inflate(
- R.layout.all_apps_search_divider, parent, false));
case VIEW_TYPE_APPS_LOADING_DIVIDER:
View loadingDividerView = mLayoutInflater.inflate(
R.layout.all_apps_discovery_loading_divider, parent, false);
@@ -324,10 +329,6 @@
}
}
- private Point getCellSize() {
- return mLauncher.getDeviceProfile().getCellSize();
- }
-
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
@@ -336,7 +337,6 @@
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.applyFromApplicationInfo(info);
- icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
break;
case VIEW_TYPE_DISCOVERY_ITEM:
AppDiscoveryAppInfo appDiscoveryAppInfo = (AppDiscoveryAppInfo)
@@ -374,6 +374,22 @@
}
@Override
+ public void onViewAttachedToWindow(ViewHolder holder) {
+ int type = holder.getItemViewType();
+ if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
+ mSpringAnimationHandler.add(holder.itemView, holder);
+ }
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(ViewHolder holder) {
+ int type = holder.getItemViewType();
+ if (FeatureFlags.LAUNCHER3_PHYSICS && isViewType(type, VIEW_TYPE_MASK_HAS_SPRINGS)) {
+ mSpringAnimationHandler.remove(holder.itemView);
+ }
+ }
+
+ @Override
public boolean onFailedToRecycleView(ViewHolder holder) {
// Always recycle and we will reset the view when it is bound
return true;
@@ -389,4 +405,119 @@
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
+
+ /**
+ * Helper class to set the SpringAnimation values for an item in the adapter.
+ */
+ private class AllAppsSpringAnimationFactory
+ implements SpringAnimationHandler.AnimationFactory<ViewHolder> {
+ private static final float DEFAULT_MAX_VALUE_PX = 100;
+ private static final float DEFAULT_MIN_VALUE_PX = -DEFAULT_MAX_VALUE_PX;
+
+ // Damping ratio range is [0, 1]
+ private static final float SPRING_DAMPING_RATIO = 0.55f;
+
+ // Stiffness is a non-negative number.
+ private static final float MIN_SPRING_STIFFNESS = 580f;
+ private static final float MAX_SPRING_STIFFNESS = 900f;
+
+ // The amount by which each adjacent rows' stiffness will differ.
+ private static final float ROW_STIFFNESS_COEFFICIENT = 50f;
+
+ @Override
+ public SpringAnimation initialize(ViewHolder vh) {
+ return SpringAnimationHandler.forView(vh.itemView, DynamicAnimation.TRANSLATION_Y, 0);
+ }
+
+ /**
+ * @param spring A new or recycled SpringAnimation.
+ * @param vh The ViewHolder that {@param spring} is related to.
+ */
+ @Override
+ public void update(SpringAnimation spring, ViewHolder vh) {
+ int numPredictedApps = Math.min(mAppsPerRow, mApps.getPredictedApps().size());
+ int appPosition = getAppPosition(vh.getAdapterPosition(), numPredictedApps,
+ mAppsPerRow);
+
+ int col = appPosition % mAppsPerRow;
+ int row = appPosition / mAppsPerRow;
+
+ int numTotalRows = mApps.getNumAppRows() - 1; // zero-based count
+ if (row > (numTotalRows / 2)) {
+ // Mirror the rows so that the top row acts the same as the bottom row.
+ row = Math.abs(numTotalRows - row);
+ }
+
+ // We manipulate the stiffness, min, and max values based on the items distance to the
+ // first row and the items distance to the center column to create the ^-shaped motion
+ // effect.
+ float rowFactor = (1 + row) * 0.5f;
+ float colFactor = getColumnFactor(col, mAppsPerRow);
+
+ float minValue = DEFAULT_MIN_VALUE_PX * (rowFactor + colFactor);
+ float maxValue = DEFAULT_MAX_VALUE_PX * (rowFactor + colFactor);
+
+ float stiffness = Utilities.boundToRange(
+ MAX_SPRING_STIFFNESS - (row * ROW_STIFFNESS_COEFFICIENT),
+ MIN_SPRING_STIFFNESS,
+ MAX_SPRING_STIFFNESS);
+
+ spring.setMinValue(minValue)
+ .setMaxValue(maxValue)
+ .getSpring()
+ .setStiffness(stiffness)
+ .setDampingRatio(SPRING_DAMPING_RATIO);
+ }
+
+ /**
+ * @return The app position is the position of the app in the Adapter if we ignored all
+ * other view types.
+ *
+ * The first app is at position 0, and the first app each following row is at a
+ * position that is a multiple of {@param appsPerRow}.
+ *
+ * ie. If there are 5 apps per row, and there are two rows of apps:
+ * 0 1 2 3 4
+ * 5 6 7 8 9
+ */
+ private int getAppPosition(int position, int numPredictedApps, int appsPerRow) {
+ if (position < numPredictedApps) {
+ // Predicted apps are first in the adapter.
+ return position;
+ }
+
+ // There is at most 1 divider view between the predicted apps and the alphabetical apps.
+ int numDividerViews = numPredictedApps == 0 ? 0 : 1;
+
+ // This offset takes into consideration an incomplete row of predicted apps.
+ int numPredictedAppsOffset = appsPerRow - numPredictedApps;
+ return position + numPredictedAppsOffset - numDividerViews;
+ }
+
+ /**
+ * Increase the column factor as the distance increases between the column and the center
+ * column(s).
+ */
+ private float getColumnFactor(int col, int numCols) {
+ float centerColumn = numCols / 2;
+ int distanceToCenter = (int) Math.abs(col - centerColumn);
+
+ boolean evenNumberOfColumns = numCols % 2 == 0;
+ if (evenNumberOfColumns && col < centerColumn) {
+ distanceToCenter -= 1;
+ }
+
+ float factor = 0;
+ while (distanceToCenter > 0) {
+ if (distanceToCenter == 1) {
+ factor += 0.2f;
+ } else {
+ factor += 0.1f;
+ }
+ --distanceToCenter;
+ }
+
+ return factor;
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 64e2fcb..34421bd 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,12 +15,14 @@
*/
package com.android.launcher3.allapps;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
+import android.util.Property;
import android.util.SparseIntArray;
import android.view.MotionEvent;
import android.view.View;
@@ -28,8 +30,8 @@
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.DrawableFactory;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -53,7 +55,23 @@
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
- private HeaderElevationController mElevationController;
+ private SpringAnimationHandler mSpringAnimationHandler;
+ private OverScrollHelper mOverScrollHelper;
+ private VerticalPullDetector mPullDetector;
+
+ private float mContentTranslationY = 0;
+ public static final Property<AllAppsRecyclerView, Float> CONTENT_TRANS_Y =
+ new Property<AllAppsRecyclerView, Float>(Float.class, "appsRecyclerViewContentTransY") {
+ @Override
+ public Float get(AllAppsRecyclerView allAppsRecyclerView) {
+ return allAppsRecyclerView.getContentTranslationY();
+ }
+
+ @Override
+ public void set(AllAppsRecyclerView allAppsRecyclerView, Float y) {
+ allAppsRecyclerView.setContentTranslationY(y);
+ }
+ };
public AllAppsRecyclerView(Context context) {
this(context, null);
@@ -72,9 +90,33 @@
super(context, attrs, defStyleAttr);
Resources res = getResources();
addOnItemTouchListener(this);
- mScrollbar.setDetachThumbOnFastScroll();
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
+
+ mOverScrollHelper = new OverScrollHelper();
+ mPullDetector = new VerticalPullDetector(getContext());
+ mPullDetector.setListener(mOverScrollHelper);
+ mPullDetector.setDetectableScrollConditions(VerticalPullDetector.DIRECTION_UP
+ | VerticalPullDetector.DIRECTION_DOWN, true);
+ }
+
+ public void setSpringAnimationHandler(SpringAnimationHandler springAnimationHandler) {
+ mSpringAnimationHandler = springAnimationHandler;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent ev) {
+ mPullDetector.onTouchEvent(ev);
+ return super.onInterceptTouchEvent(rv, ev) || mOverScrollHelper.isInOverScroll();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent e) {
+ mPullDetector.onTouchEvent(e);
+ if (FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null) {
+ mSpringAnimationHandler.addMovement(e);
+ }
+ return super.onTouchEvent(e);
}
/**
@@ -85,8 +127,8 @@
mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
}
- public void setElevationController(HeaderElevationController elevationController) {
- mElevationController = elevationController;
+ public AlphabeticalAppsList getApps() {
+ return mApps;
}
/**
@@ -98,7 +140,6 @@
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_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
@@ -125,8 +166,6 @@
AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER,
AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER);
putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
- AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER);
- putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET);
putSameHeightFor(adapter, widthMeasureSpec, heightMeasureSpec,
AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH);
@@ -152,17 +191,16 @@
*/
public void scrollToTop() {
// Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
- if (mScrollbar.isThumbDetached()) {
+ if (mScrollbar != null) {
mScrollbar.reattachThumbToScroll();
}
scrollToPosition(0);
- if (mElevationController != null) {
- mElevationController.reset();
- }
}
@Override
public void onDraw(Canvas c) {
+ c.translate(0, mContentTranslationY);
+
// Draw the background
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.draw(c);
@@ -171,6 +209,19 @@
super.onDraw(c);
}
+ public float getContentTranslationY() {
+ return mContentTranslationY;
+ }
+
+ /**
+ * Use this method instead of calling {@link #setTranslationY(float)}} directly to avoid drawing
+ * on top of other Views.
+ */
+ public void setContentTranslationY(float y) {
+ mContentTranslationY = y;
+ invalidate();
+ }
+
@Override
protected boolean verifyDrawable(Drawable who) {
return who == mEmptySearchBackground || super.verifyDrawable(who);
@@ -349,7 +400,7 @@
}
@Override
- protected boolean supportsFastScrolling() {
+ public boolean supportsFastScrolling() {
// Only allow fast scrolling when the user is not searching, since the results are not
// grouped in a meaningful order
return !mApps.hasFilter();
@@ -369,7 +420,8 @@
if (position == NO_POSITION) {
return -1;
}
- return getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
+ return getPaddingTop() +
+ getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
}
public int getCurrentScrollY(int position, int offset) {
@@ -399,14 +451,7 @@
}
mCachedScrollPositions.put(position, y);
}
-
- return getPaddingTop() + y - offset;
- }
-
- @Override
- protected int getScrollbarTrackHeight() {
- return super.getScrollbarTrackHeight()
- - Launcher.getLauncher(getContext()).getDragLayer().getInsets().bottom;
+ return y - offset;
}
/**
@@ -415,9 +460,8 @@
*/
@Override
protected int getAvailableScrollHeight() {
- int paddedHeight = getCurrentScrollY(mApps.getAdapterItems().size(), 0);
- int totalHeight = paddedHeight + getPaddingBottom();
- return totalHeight - getScrollbarTrackHeight();
+ return getPaddingTop() + getCurrentScrollY(mApps.getAdapterItems().size(), 0)
+ - getHeight() + getPaddingBottom();
}
/**
@@ -436,4 +480,86 @@
y + mEmptySearchBackground.getIntrinsicHeight());
}
+ private class OverScrollHelper implements VerticalPullDetector.Listener {
+
+ private static final float MAX_RELEASE_VELOCITY = 5000; // px / s
+ private static final float MAX_OVERSCROLL_PERCENTAGE = 0.07f;
+
+ private boolean mIsInOverScroll;
+
+ @Override
+ public void onDragStart(boolean start) {
+ }
+
+ @Override
+ public boolean onDrag(float displacement, float velocity) {
+ // We are in overscroll iff we are trying to drag further down when we're already at
+ // the bottom of All Apps.
+ mIsInOverScroll = !canScrollVertically(1) && displacement < 0;
+
+ if (mIsInOverScroll) {
+ displacement = getDampedOverScroll(displacement);
+ setContentTranslationY(displacement);
+ }
+ return mIsInOverScroll;
+ }
+
+ @Override
+ public void onDragEnd(float velocity, boolean fling) {
+ float y = getContentTranslationY();
+ if (mIsInOverScroll && Float.compare(y, 0) != 0) {
+ if (FeatureFlags.LAUNCHER3_PHYSICS) {
+ // We calculate our own velocity to give the springs the desired effect.
+ velocity = y / getDampedOverScroll(getHeight()) * MAX_RELEASE_VELOCITY;
+ // We want to negate the velocity because we are moving to 0 from -1 due to the
+ // downward motion. (y-axis -1 is above 0).
+ mSpringAnimationHandler.animateToPositionWithVelocity(0, -1, -velocity);
+ }
+
+ ObjectAnimator.ofFloat(AllAppsRecyclerView.this,
+ AllAppsRecyclerView.CONTENT_TRANS_Y, 0)
+ .setDuration(100)
+ .start();
+ }
+ mIsInOverScroll = false;
+ }
+
+ public boolean isInOverScroll() {
+ return mIsInOverScroll;
+ }
+
+ private float getDampedOverScroll(float y) {
+ return dampedOverScroll(y, getHeight()) * MAX_OVERSCROLL_PERCENTAGE;
+ }
+
+ /**
+ * This curve determines how the effect of scrolling over the limits of the page diminishes
+ * as the user pulls further and further from the bounds
+ *
+ * @param f The percentage of how much the user has overscrolled.
+ * @return A transformed percentage based on the influence curve.
+ */
+ private float overScrollInfluenceCurve(float f) {
+ f -= 1.0f;
+ return f * f * f + 1.0f;
+ }
+
+ /**
+ * @param amount The original amount overscrolled.
+ * @param max The maximum amount that the View can overscroll.
+ * @return The dampened overscroll amount.
+ */
+ private float dampedOverScroll(float amount, float max) {
+ float f = amount / max;
+ if (Float.compare(f, 0) == 0) return 0;
+ f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+ // Clamp this factor, f, to -1 < f < 1
+ if (Math.abs(f) >= 1) {
+ f /= Math.abs(f);
+ }
+
+ return Math.round(f * max);
+ }
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 6587ad7..517dc94 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -20,7 +20,6 @@
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.android.launcher3.BubbleTextView;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 30ed180..0859e06 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -22,8 +22,13 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.SpringAnimationHandler;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.GradientView;
+import com.android.launcher3.graphics.ScrimView;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TouchController;
@@ -38,12 +43,13 @@
* closer to top or closer to the page indicator.
*/
public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener,
- View.OnLayoutChangeListener {
+ SearchUiManager.OnScrollRangeChangeListener {
private static final String TAG = "AllAppsTrans";
private static final boolean DBG = false;
- private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f);
+ private final Interpolator mWorkspaceAccelnterpolator = new AccelerateInterpolator(2f);
+ private final Interpolator mHotseatAccelInterpolator = new AccelerateInterpolator(1.5f);
private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
private final VerticalPullDetector.ScrollInterpolator mScrollInterpolator
@@ -65,6 +71,7 @@
private final Launcher mLauncher;
private final VerticalPullDetector mDetector;
private final ArgbEvaluator mEvaluator;
+ private final boolean mIsDarkTheme;
// 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
@@ -87,10 +94,15 @@
private AnimatorSet mCurrentAnimation;
private boolean mNoIntercept;
+ private boolean mTouchEventStartedOnHotseat;
// Used in discovery bounce animation to provide the transition without workspace changing.
private boolean mIsTranslateWithoutWorkspace = false;
private AnimatorSet mDiscoBounceAnimation;
+ private GradientView mGradientView;
+ private ScrimView mScrimView;
+
+ private SpringAnimationHandler mSpringAnimationHandler;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
@@ -101,12 +113,14 @@
mEvaluator = new ArgbEvaluator();
mAllAppsBackgroundColor = Themes.getAttrColor(l, android.R.attr.colorPrimary);
+ mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
}
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = false;
+ mTouchEventStartedOnHotseat = mLauncher.getDragLayer().isEventOverHotseat(ev);
if (!mLauncher.isAllAppsVisible() && mLauncher.getWorkspace().workspaceInModalState()) {
mNoIntercept = true;
} else if (mLauncher.isAllAppsVisible() &&
@@ -153,6 +167,9 @@
@Override
public boolean onControllerTouchEvent(MotionEvent ev) {
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.addMovement(ev);
+ }
return mDetector.onTouchEvent(ev);
}
@@ -171,6 +188,9 @@
mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
mShiftStart = mAppsView.getTranslationY();
preparePull(start);
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.skipToEnd();
+ }
}
@Override
@@ -193,6 +213,9 @@
return; // early termination.
}
+ final int containerType = mTouchEventStartedOnHotseat
+ ? ContainerType.HOTSEAT : ContainerType.WORKSPACE;
+
if (fling) {
if (velocity < 0) {
calculateDuration(velocity, mAppsView.getTranslationY());
@@ -201,11 +224,13 @@
mLauncher.getUserEventDispatcher().logActionOnContainer(
Action.Touch.FLING,
Action.Direction.UP,
- ContainerType.HOTSEAT);
+ containerType);
}
- mLauncher.showAppsView(true /* animated */,
- false /* updatePredictedApps */,
- false /* focusSearchBar */);
+ mLauncher.showAppsView(true /* animated */, false /* updatePredictedApps */);
+ if (hasSpringAnimationHandler()) {
+ // The icons are moving upwards, so we go to 0 from 1. (y-axis 1 is below 0.)
+ mSpringAnimationHandler.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
+ }
} else {
calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
mLauncher.showWorkspace(true);
@@ -221,11 +246,9 @@
mLauncher.getUserEventDispatcher().logActionOnContainer(
Action.Touch.SWIPE,
Action.Direction.UP,
- ContainerType.HOTSEAT);
+ containerType);
}
- mLauncher.showAppsView(true, /* animated */
- false /* updatePredictedApps */,
- false /* focusSearchBar */);
+ mLauncher.showAppsView(true, /* animated */ false /* updatePredictedApps */);
}
}
}
@@ -247,20 +270,45 @@
if (!mLauncher.isAllAppsVisible()) {
mLauncher.tryAndUpdatePredictedApps();
mAppsView.setVisibility(View.VISIBLE);
- mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
+ if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
+ }
}
}
}
private void updateLightStatusBar(float shift) {
// Do not modify status bar on landscape as all apps is not full bleed.
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ if (!FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS
+ && mLauncher.getDeviceProfile().isVerticalBarLayout()) {
return;
}
- // Use a light status bar (dark icons) if all apps is behind at least half of the status
- // bar. If the status bar is already light due to wallpaper extraction, keep it that way.
- boolean forceLight = shift <= mStatusBarHeight / 2;
- mLauncher.activateLightSystemBars(forceLight, true /* statusBar */, true /* navBar */);
+
+ // Use a light system UI (dark icons) if all apps is behind at least half of the status bar.
+ boolean forceChange = shift <= mStatusBarHeight / 2;
+ if (forceChange) {
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_ALL_APPS, !mIsDarkTheme);
+ } else {
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_ALL_APPS, 0);
+ }
+ }
+
+ private void updateAllAppsBg(float progress) {
+ // gradient
+ if (mGradientView == null) {
+ mGradientView = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
+ mGradientView.setVisibility(View.VISIBLE);
+ }
+ mGradientView.setProgress(progress);
+
+ // scrim
+ if (mScrimView == null) {
+ mScrimView = (ScrimView) mLauncher.findViewById(R.id.scrim_bg);
+ mScrimView.setVisibility(View.VISIBLE);
+ }
+ mScrimView.setProgress(progress);
}
/**
@@ -273,31 +321,37 @@
float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f);
float alpha = 1 - workspaceHotseatAlpha;
- float interpolation = mAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
+ float workspaceAlpha = mWorkspaceAccelnterpolator.getInterpolation(workspaceHotseatAlpha);
+ float hotseatAlpha = mHotseatAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
int color = (Integer) mEvaluator.evaluate(mDecelInterpolator.getInterpolation(alpha),
mHotseatBackgroundColor, mAllAppsBackgroundColor);
int bgAlpha = Color.alpha((int) mEvaluator.evaluate(alpha,
mHotseatBackgroundColor, mAllAppsBackgroundColor));
- mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
+ if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ updateAllAppsBg(alpha);
+ } else {
+ mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
+ }
+
mAppsView.getContentView().setAlpha(alpha);
mAppsView.setTranslationY(shiftCurrent);
if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y, -mShiftRange + shiftCurrent,
- interpolation);
+ hotseatAlpha);
} else {
mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y,
PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent),
- interpolation);
+ hotseatAlpha);
}
if (mIsTranslateWithoutWorkspace) {
return;
}
mWorkspace.setWorkspaceYTranslationAndAlpha(
- PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), interpolation);
+ PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), workspaceAlpha);
if (!mDetector.isDraggingState()) {
mContainerVelocity = mDetector.computeVelocity(shiftCurrent - shiftPrevious,
@@ -451,6 +505,9 @@
public void finishPullUp() {
mHotseat.setVisibility(View.INVISIBLE);
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.reset();
+ }
setProgress(0f);
}
@@ -459,6 +516,9 @@
mHotseat.setBackgroundTransparent(false /* transparent */);
mHotseat.setVisibility(View.VISIBLE);
mAppsView.reset();
+ if (hasSpringAnimationHandler()) {
+ mSpringAnimationHandler.reset();
+ }
setProgress(1f);
}
@@ -486,21 +546,20 @@
mAppsView = appsView;
mHotseat = hotseat;
mWorkspace = workspace;
- mHotseat.addOnLayoutChangeListener(this);
mHotseat.bringToFront();
mCaretController = new AllAppsCaretController(
mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
+ mAppsView.getSearchUiManager().addOnScrollRangeChangeListener(this);
+ mSpringAnimationHandler = mAppsView.getSpringAnimationHandler();
+ }
+
+ private boolean hasSpringAnimationHandler() {
+ return FeatureFlags.LAUNCHER3_PHYSICS && mSpringAnimationHandler != null;
}
@Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- mShiftRange = top;
- } else {
- mShiftRange = bottom;
- }
+ public void onScrollRangeChanged(int scrollRange) {
+ mShiftRange = scrollRange;
setProgress(mProgress);
}
-
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index f5cf7ef..608e898 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -24,7 +24,7 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.discovery.AppDiscoveryAppInfo;
import com.android.launcher3.discovery.AppDiscoveryItem;
import com.android.launcher3.discovery.AppDiscoveryUpdateState;
@@ -138,13 +138,6 @@
return item;
}
- public static AdapterItem asSearchDivider(int pos) {
- AdapterItem item = new AdapterItem();
- item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER;
- item.position = pos;
- return item;
- }
-
public static AdapterItem asMarketDivider(int pos) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
@@ -226,6 +219,13 @@
}
/**
+ * Returns the predicted apps.
+ */
+ public List<AppInfo> getPredictedApps() {
+ return mPredictedApps;
+ }
+
+ /**
* Returns fast scroller sections of all the current filtered applications.
*/
public List<FastScrollSectionInfo> getFastScrollerSections() {
@@ -429,9 +429,6 @@
}
}
- // Add the search divider
- mAdapterItems.add(AdapterItem.asSearchDivider(position++));
-
// Process the predicted app components
mPredictedApps.clear();
if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
@@ -440,7 +437,7 @@
if (info != null) {
mPredictedApps.add(info);
} else {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
Log.e(TAG, "Predicted app not found: " + ck);
}
}
@@ -606,6 +603,10 @@
return result;
}
+ public AppInfo findApp(ComponentKey key) {
+ return mComponentToAppMap.get(key);
+ }
+
/**
* Returns the cached section name for the given title, recomputing and updating the cache if
* the title has no cached section name.
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
deleted file mode 100644
index 57747e3..0000000
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.allapps;
-
-/**
- * The default search controller.
- */
-public class DefaultAppSearchController extends AllAppsSearchBarController {
-
- public DefaultAppSearchAlgorithm onInitializeSearch() {
- return new DefaultAppSearchAlgorithm(mApps.getApps());
- }
-}
diff --git a/src/com/android/launcher3/allapps/HeaderElevationController.java b/src/com/android/launcher3/allapps/HeaderElevationController.java
deleted file mode 100644
index b167fed..0000000
--- a/src/com/android/launcher3/allapps/HeaderElevationController.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.android.launcher3.allapps;
-
-import android.content.res.Resources;
-import android.graphics.Outline;
-import android.support.v7.widget.RecyclerView;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-
-/**
- * Helper class for controlling the header elevation in response to RecyclerView scroll.
- */
-public class HeaderElevationController extends RecyclerView.OnScrollListener {
-
- private final View mHeader;
- private final float mMaxElevation;
- private final float mScrollToElevation;
-
- private int mCurrentY = 0;
-
- public HeaderElevationController(View header) {
- mHeader = header;
- final Resources res = mHeader.getContext().getResources();
- mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
- mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
-
- // We need to provide a custom outline so the shadow only appears on the bottom edge.
- // The top, left and right edges are all extended out, and the shadow is clipped
- // by the parent.
- final ViewOutlineProvider vop = new ViewOutlineProvider() {
- @Override
- public void getOutline(View view, Outline outline) {
- final View parent = (View) mHeader.getParent();
-
- final int left = parent.getLeft(); // Use the parent to account for offsets
- final int top = view.getTop();
- final int right = left + view.getWidth();
- final int bottom = view.getBottom();
-
- final int offset = Utilities.pxFromDp(mMaxElevation, res.getDisplayMetrics());
- outline.setRect(left - offset, top - offset, right + offset, bottom);
- }
- };
- mHeader.setOutlineProvider(vop);
- }
-
- public void reset() {
- mCurrentY = 0;
- onScroll(mCurrentY);
- }
-
- @Override
- public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
- onScroll(mCurrentY);
- }
-
- private void onScroll(int scrollY) {
- float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
- float newElevation = mMaxElevation * elevationPct;
- if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
- mHeader.setElevation(newElevation);
- }
- }
-
-}
diff --git a/src/com/android/launcher3/allapps/LandscapeFastScroller.java b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
new file mode 100644
index 0000000..cdde657
--- /dev/null
+++ b/src/com/android/launcher3/allapps/LandscapeFastScroller.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
new file mode 100644
index 0000000..c0d7850
--- /dev/null
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.KeyEvent;
+
+/**
+ * Interface for controlling the Apps search UI.
+ */
+public interface SearchUiManager {
+
+ /**
+ * Initializes the search manager.
+ */
+ void initialize(AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView);
+
+ /**
+ * 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();
+
+ /**
+ * Called before dispatching a key event, in case the search manager wants to initialize
+ * some UI beforehand.
+ */
+ void preDispatchKeyEvent(KeyEvent keyEvent);
+
+ void addOnScrollRangeChangeListener(OnScrollRangeChangeListener listener);
+
+ /**
+ * Callback for listening to changes in the vertical scroll range when opening all-apps.
+ */
+ interface OnScrollRangeChangeListener {
+
+ void onScrollRangeChanged(int scrollRange);
+ }
+}
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
index 7800c01..13c4f63 100644
--- a/src/com/android/launcher3/allapps/VerticalPullDetector.java
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -17,7 +17,7 @@
private static final boolean DBG = false;
private static final String TAG = "VerticalPullDetector";
- private float mTouchSlop;
+ private final float mTouchSlop;
private int mScrollConditions;
public static final int DIRECTION_UP = 1 << 0;
@@ -47,8 +47,6 @@
SETTLING // onDragEnd
}
- ;
-
//------------------- ScrollState transition diagram -----------------------------------
//
// IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
@@ -110,7 +108,7 @@
private boolean mIgnoreSlopWhenSettling;
/* Client of this gesture detector can register a callback. */
- Listener mListener;
+ private Listener mListener;
public void setListener(Listener l) {
mListener = l;
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
similarity index 79%
rename from src/com/android/launcher3/allapps/AllAppsSearchBarController.java
rename to src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index c7ba3ab..63aa7be 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -13,12 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.Editable;
@@ -37,22 +34,22 @@
import com.android.launcher3.discovery.AppDiscoveryItem;
import com.android.launcher3.discovery.AppDiscoveryUpdateState;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
/**
* An interface to a search box that AllApps can command.
*/
-public abstract class AllAppsSearchBarController
+public class AllAppsSearchBarController
implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
protected Launcher mLauncher;
- protected AlphabeticalAppsList mApps;
protected Callbacks mCb;
protected ExtendedEditText mInput;
protected String mQuery;
- protected DefaultAppSearchAlgorithm mSearchAlgorithm;
+ protected SearchAlgorithm mSearchAlgorithm;
protected InputMethodManager mInputMethodManager;
public void setVisibility(int visibility) {
@@ -62,9 +59,8 @@
* Sets the references to the apps model and the search result callback.
*/
public final void initialize(
- AlphabeticalAppsList apps, ExtendedEditText input,
+ SearchAlgorithm searchAlgorithm, ExtendedEditText input,
Launcher launcher, Callbacks cb) {
- mApps = apps;
mCb = cb;
mLauncher = launcher;
@@ -76,22 +72,9 @@
mInputMethodManager = (InputMethodManager)
mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- mSearchAlgorithm = onInitializeSearch();
-
- onInitialized();
+ mSearchAlgorithm = searchAlgorithm;
}
- /**
- * You can override this method to perform custom initialization.
- */
- protected void onInitialized() {
- }
-
- /**
- * To be implemented by subclasses. This method will get called when the controller is set.
- */
- protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
-
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// Do nothing
@@ -114,7 +97,7 @@
}
}
- protected void refreshSearchResult() {
+ public void refreshSearchResult() {
if (TextUtils.isEmpty(mQuery)) {
return;
}
@@ -135,7 +118,8 @@
if (query.isEmpty()) {
return false;
}
- return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
+ return mLauncher.startActivitySafely(v,
+ PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null);
}
@Override
@@ -186,29 +170,11 @@
}
/**
- * Creates a new market search intent.
- */
- public Intent createMarketSearchIntent(String query) {
- Uri marketSearchUri = Uri.parse("market://search")
- .buildUpon()
- .appendQueryParameter("c", "apps")
- .appendQueryParameter("q", query)
- .build();
- return new Intent(Intent.ACTION_VIEW).setData(marketSearchUri);
- }
-
- /**
* Callback for getting search results.
*/
public interface Callbacks {
/**
- * Called when the bounds of the search bar has changed.
- */
- @Deprecated
- void onBoundsChanged(Rect newBounds);
-
- /**
* Called when the search is complete.
*
* @param apps sorted list of matching components or null if in case of failure.
@@ -220,7 +186,6 @@
*/
void clearSearchResult();
-
/**
* Called when the app discovery is providing an update of search, which can either be
* START for starting a new discovery,
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
new file mode 100644
index 0000000..5cb12d5
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps.search;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.method.TextKeyListener;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsRecyclerView;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+import com.android.launcher3.allapps.SearchUiManager;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.discovery.AppDiscoveryItem;
+import com.android.launcher3.discovery.AppDiscoveryUpdateState;
+import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.util.ComponentKey;
+import java.util.ArrayList;
+
+/**
+ * Layout to contain the All-apps search UI.
+ */
+public class AppsSearchContainerLayout extends FrameLayout
+ implements SearchUiManager, AllAppsSearchBarController.Callbacks {
+
+ private final Launcher mLauncher;
+ private final int mMinHeight;
+ private final int mSearchBoxHeight;
+ private final AllAppsSearchBarController mSearchBarController;
+ private final SpannableStringBuilder mSearchQueryBuilder;
+
+ private ExtendedEditText mSearchInput;
+ private AlphabeticalAppsList mApps;
+ private AllAppsRecyclerView mAppsRecyclerView;
+ private AllAppsGridAdapter mAdapter;
+ private View mDivider;
+ private HeaderElevationController mElevationController;
+
+ public AppsSearchContainerLayout(Context context) {
+ this(context, null);
+ }
+
+ public AppsSearchContainerLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public AppsSearchContainerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mLauncher = Launcher.getLauncher(context);
+ mMinHeight = getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_height);
+ mSearchBoxHeight = getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height);
+ mSearchBarController = new AllAppsSearchBarController();
+
+ mSearchQueryBuilder = new SpannableStringBuilder();
+ Selection.setSelection(mSearchQueryBuilder, 0);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mSearchInput = findViewById(R.id.search_box_input);
+ mDivider = findViewById(R.id.search_divider);
+ mElevationController = new HeaderElevationController(mDivider);
+
+ // Update the hint to contain the icon.
+ // Prefix the original hint with two spaces. The first space gets replaced by the icon
+ // using span. The second space is used for a singe space character between the hint
+ // and the icon.
+ SpannableString spanned = new SpannableString(" " + mSearchInput.getHint());
+ spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
+ 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+ mSearchInput.setHint(spanned);
+
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ if (!dp.isVerticalBarLayout()) {
+ LayoutParams lp = (LayoutParams) mDivider.getLayoutParams();
+ lp.leftMargin = lp.rightMargin = dp.edgeMarginPx;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+ !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+ getLayoutParams().height = mLauncher.getDragLayer().getInsets().top + mMinHeight;
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+
+ @Override
+ public void initialize(
+ AlphabeticalAppsList appsList, AllAppsRecyclerView recyclerView) {
+ mApps = appsList;
+ mAppsRecyclerView = recyclerView;
+ mAppsRecyclerView.addOnScrollListener(mElevationController);
+ mAdapter = (AllAppsGridAdapter) mAppsRecyclerView.getAdapter();
+ mSearchBarController.initialize(
+ new DefaultAppSearchAlgorithm(appsList.getApps()), mSearchInput, mLauncher, this);
+ }
+
+ @Override
+ public void refreshSearchResult() {
+ mSearchBarController.refreshSearchResult();
+ }
+
+ @Override
+ public void reset() {
+ mElevationController.reset();
+ mSearchBarController.reset();
+ }
+
+ @Override
+ public void preDispatchKeyEvent(KeyEvent event) {
+ // Determine if the key event was actual text, if so, focus the search bar and then dispatch
+ // the key normally so that it can process this key event
+ if (!mSearchBarController.isSearchFieldFocused() &&
+ event.getAction() == KeyEvent.ACTION_DOWN) {
+ final int unicodeChar = event.getUnicodeChar();
+ final boolean isKeyNotWhitespace = unicodeChar > 0 &&
+ !Character.isWhitespace(unicodeChar) && !Character.isSpaceChar(unicodeChar);
+ if (isKeyNotWhitespace) {
+ boolean gotKey = TextKeyListener.getInstance().onKeyDown(this, mSearchQueryBuilder,
+ event.getKeyCode(), event);
+ if (gotKey && mSearchQueryBuilder.length() > 0) {
+ mSearchBarController.focusSearchField();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
+ if (apps != null) {
+ mApps.setOrderedFilter(apps);
+ notifyResultChanged();
+ mAdapter.setLastSearchQuery(query);
+ }
+ }
+
+ @Override
+ public void clearSearchResult() {
+ if (mApps.setOrderedFilter(null)) {
+ notifyResultChanged();
+ }
+
+ // Clear the search query
+ 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();
+ }
+ }
+
+ private void notifyResultChanged() {
+ mElevationController.reset();
+ mAppsRecyclerView.onSearchResultsChanged();
+ }
+
+ @Override
+ public void addOnScrollRangeChangeListener(final OnScrollRangeChangeListener listener) {
+ mLauncher.getHotseat().addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ DeviceProfile dp = mLauncher.getDeviceProfile();
+ if (!dp.isVerticalBarLayout()) {
+ Rect insets = mLauncher.getDragLayer().getInsets();
+ int hotseatBottom = bottom - dp.hotseatBarBottomPaddingPx - insets.bottom;
+ int searchTopMargin = insets.top + (mMinHeight - mSearchBoxHeight);
+ listener.onScrollRangeChanged(hotseatBottom - searchTopMargin);
+ } else {
+ listener.onScrollRangeChanged(bottom);
+ }
+ }
+ });
+ }
+}
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
similarity index 92%
rename from src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
rename to src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
index 06cf9aa..8a0fd46 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithm.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.os.Handler;
@@ -26,7 +26,7 @@
/**
* The default search implementation.
*/
-public class DefaultAppSearchAlgorithm {
+public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
private final List<AppInfo> mApps;
protected final Handler mResultHandler;
@@ -36,12 +36,14 @@
mResultHandler = new Handler();
}
+ @Override
public void cancel(boolean interruptActiveRequests) {
if (interruptActiveRequests) {
mResultHandler.removeCallbacksAndMessages(null);
}
}
+ @Override
public void doSearch(final String query,
final AllAppsSearchBarController.Callbacks callback) {
final ArrayList<ComponentKey> result = getTitleMatchResult(query);
@@ -54,7 +56,7 @@
});
}
- protected ArrayList<ComponentKey> getTitleMatchResult(String query) {
+ private ArrayList<ComponentKey> getTitleMatchResult(String query) {
// Do an intersection of the words in the query and each title, and filter out all the
// apps that don't match all of the words in the query.
final String queryTextLower = query.toLowerCase();
@@ -67,7 +69,7 @@
return result;
}
- protected boolean matches(AppInfo info, String query) {
+ public static boolean matches(AppInfo info, String query) {
int queryLength = query.length();
String title = info.title.toString();
@@ -103,7 +105,7 @@
* 3) Any capital character after a digit or small character
* 4) Any capital character before a small character
*/
- protected boolean isBreak(int thisType, int prevType, int nextType) {
+ private static boolean isBreak(int thisType, int prevType, int nextType) {
switch (thisType) {
case Character.UPPERCASE_LETTER:
if (nextType == Character.UPPERCASE_LETTER) {
diff --git a/src/com/android/launcher3/allapps/search/HeaderElevationController.java b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
new file mode 100644
index 0000000..7cd32b2
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/HeaderElevationController.java
@@ -0,0 +1,81 @@
+package com.android.launcher3.allapps.search;
+
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.R;
+
+/**
+ * Helper class for controlling the header elevation in response to RecyclerView scroll.
+ */
+public class HeaderElevationController extends RecyclerView.OnScrollListener {
+
+ private final View mHeader;
+ private final View mHeaderChild;
+ private final float mMaxElevation;
+ private final float mScrollToElevation;
+
+ private int mCurrentY = 0;
+
+ public HeaderElevationController(View header) {
+ mHeader = header;
+ final Resources res = mHeader.getContext().getResources();
+ mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
+ mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+
+ // We need to provide a custom outline so the shadow only appears on the bottom edge.
+ // The top, left and right edges are all extended out to match parent's edge, so that
+ // the shadow is clipped by the parent.
+ final ViewOutlineProvider vop = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ // Set the left and top to be at the parents edge. Since the coordinates are
+ // relative to this view,
+ // (x = -view.getLeft()) for this view => (x = 0) for parent
+ final int left = -view.getLeft();
+ final int top = -view.getTop();
+
+ // Since the view is centered align, the spacing on left and right are same.
+ // Add same spacing on the right to reach parent's edge.
+ final int right = view.getWidth() - left;
+ final int bottom = view.getHeight();
+ final int offset = (int) mMaxElevation;
+ outline.setRect(left - offset, top - offset, right + offset, bottom);
+ }
+ };
+ mHeader.setOutlineProvider(vop);
+ mHeaderChild = ((ViewGroup) mHeader).getChildAt(0);
+ }
+
+ public void reset() {
+ mCurrentY = 0;
+ onScroll(mCurrentY);
+ }
+
+ @Override
+ public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
+ onScroll(mCurrentY);
+ }
+
+ private void onScroll(int scrollY) {
+ float elevationPct = Math.min(scrollY, mScrollToElevation) / mScrollToElevation;
+ float newElevation = mMaxElevation * elevationPct;
+ if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
+ mHeader.setElevation(newElevation);
+
+ // To simulate a scrolling effect for the header, we translate the header down, and
+ // its content up by the same amount, so that it gets clipped by the parent, making it
+ // look like the content was scrolled out of the view.
+ int shift = Math.min(mHeader.getHeight(), scrollY);
+ mHeader.setTranslationY(-shift);
+ mHeaderChild.setTranslationY(shift);
+ }
+ }
+
+}
diff --git a/src/com/android/launcher3/allapps/search/SearchAlgorithm.java b/src/com/android/launcher3/allapps/search/SearchAlgorithm.java
new file mode 100644
index 0000000..c409b1c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/search/SearchAlgorithm.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps.search;
+
+/**
+ * An interface for handling search.
+ */
+public interface SearchAlgorithm {
+
+ /**
+ * Performs search and sends the result to the callback.
+ */
+ void doSearch(String query, AllAppsSearchBarController.Callbacks callback);
+
+ /**
+ * Cancels any active request.
+ */
+ void cancel(boolean interruptActiveRequests);
+}
diff --git a/src/com/android/launcher3/anim/AnimationLayerSet.java b/src/com/android/launcher3/anim/AnimationLayerSet.java
index 14bcd17..f0b3458 100644
--- a/src/com/android/launcher3/anim/AnimationLayerSet.java
+++ b/src/com/android/launcher3/anim/AnimationLayerSet.java
@@ -18,9 +18,8 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.util.ArrayMap;
import android.view.View;
-
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -29,14 +28,14 @@
*/
public class AnimationLayerSet extends AnimatorListenerAdapter {
- private final HashMap<View, Integer> mViewsToLayerTypeMap;
+ private final ArrayMap<View, Integer> mViewsToLayerTypeMap;
public AnimationLayerSet() {
- mViewsToLayerTypeMap = new HashMap<>();
+ mViewsToLayerTypeMap = new ArrayMap<>();
}
public AnimationLayerSet(View v) {
- mViewsToLayerTypeMap = new HashMap<>(1);
+ mViewsToLayerTypeMap = new ArrayMap<>(1);
addView(v);
}
diff --git a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
similarity index 97%
rename from src/com/android/launcher3/util/CircleRevealOutlineProvider.java
rename to src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
index 9fe5147..9fb6b49 100644
--- a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
+++ b/src/com/android/launcher3/anim/CircleRevealOutlineProvider.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
diff --git a/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java b/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
deleted file mode 100644
index be1e2d6..0000000
--- a/src/com/android/launcher3/anim/PillHeightRevealOutlineProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.anim;
-
-import android.graphics.Rect;
-
-import com.android.launcher3.util.PillRevealOutlineProvider;
-
-/**
- * Extension of {@link PillRevealOutlineProvider} which only changes the height of the pill.
- * For now, we assume the height is added/removed from the bottom.
- */
-public class PillHeightRevealOutlineProvider extends PillRevealOutlineProvider {
-
- private final int mNewHeight;
-
- public PillHeightRevealOutlineProvider(Rect pillRect, float radius, int newHeight) {
- super(0, 0, pillRect, radius);
- mOutline.set(pillRect);
- mNewHeight = newHeight;
- }
-
- @Override
- public void setProgress(float progress) {
- mOutline.top = 0;
- int heightDifference = mPillRect.height() - mNewHeight;
- mOutline.bottom = (int) (mPillRect.bottom - heightDifference * (1 - progress));
- }
-}
diff --git a/src/com/android/launcher3/util/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
similarity index 96%
rename from src/com/android/launcher3/util/RevealOutlineAnimation.java
rename to src/com/android/launcher3/anim/RevealOutlineAnimation.java
index 4560477..51d00d9 100644
--- a/src/com/android/launcher3/util/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -1,4 +1,4 @@
-package com.android.launcher3.util;
+package com.android.launcher3.anim;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -83,4 +83,8 @@
public void getOutline(View v, Outline outline) {
outline.setRoundRect(mOutline, mOutlineRadius);
}
+
+ public float getRadius() {
+ return mOutlineRadius;
+ }
}
diff --git a/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
new file mode 100644
index 0000000..d01b26c
--- /dev/null
+++ b/src/com/android/launcher3/anim/RoundedRectRevealOutlineProvider.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.graphics.Rect;
+
+import com.android.launcher3.popup.PopupContainerWithArrow;
+
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS;
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS;
+
+/**
+ * A {@link RevealOutlineAnimation} that provides an outline that interpolates between two radii
+ * and two {@link Rect}s.
+ *
+ * An example usage of this provider is an outline that starts out as a circle and ends
+ * as a rounded rectangle.
+ */
+public class RoundedRectRevealOutlineProvider extends RevealOutlineAnimation {
+ private final float mStartRadius;
+ private final float mEndRadius;
+
+ private final Rect mStartRect;
+ private final Rect mEndRect;
+
+ private final @PopupContainerWithArrow.RoundedCornerFlags int mRoundedCorners;
+
+ public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect,
+ Rect endRect) {
+ this(startRadius, endRadius, startRect, endRect,
+ ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
+ }
+
+ public RoundedRectRevealOutlineProvider(float startRadius, float endRadius, Rect startRect,
+ Rect endRect, int roundedCorners) {
+ mStartRadius = startRadius;
+ mEndRadius = endRadius;
+ mStartRect = startRect;
+ mEndRect = endRect;
+ mRoundedCorners = roundedCorners;
+ }
+
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return false;
+ }
+
+ @Override
+ public void setProgress(float progress) {
+ mOutlineRadius = (1 - progress) * mStartRadius + progress * mEndRadius;
+
+ mOutline.left = (int) ((1 - progress) * mStartRect.left + progress * mEndRect.left);
+ mOutline.top = (int) ((1 - progress) * mStartRect.top + progress * mEndRect.top);
+ if ((mRoundedCorners & ROUNDED_TOP_CORNERS) == 0) {
+ mOutline.top -= mOutlineRadius;
+ }
+ mOutline.right = (int) ((1 - progress) * mStartRect.right + progress * mEndRect.right);
+ mOutline.bottom = (int) ((1 - progress) * mStartRect.bottom + progress * mEndRect.bottom);
+ if ((mRoundedCorners & ROUNDED_BOTTOM_CORNERS) == 0) {
+ mOutline.bottom += mOutlineRadius;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/anim/SpringAnimationHandler.java b/src/com/android/launcher3/anim/SpringAnimationHandler.java
new file mode 100644
index 0000000..3e58adc
--- /dev/null
+++ b/src/com/android/launcher3/anim/SpringAnimationHandler.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.anim;
+
+import android.support.animation.FloatPropertyCompat;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
+import android.support.annotation.IntDef;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+
+import com.android.launcher3.R;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+
+/**
+ * Handler class that manages springs for a set of views that should all move based on the same
+ * {@link MotionEvent}s.
+ *
+ * Supports setting either X or Y velocity on the list of springs added to this handler.
+ */
+public class SpringAnimationHandler<T> {
+
+ private static final String TAG = "SpringAnimationHandler";
+ private static final boolean DEBUG = false;
+
+ private static final float VELOCITY_DAMPING_FACTOR = 0.175f;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({Y_DIRECTION, X_DIRECTION})
+ public @interface Direction {}
+ public static final int Y_DIRECTION = 0;
+ public static final int X_DIRECTION = 1;
+ private int mVelocityDirection;
+
+ private VelocityTracker mVelocityTracker;
+ private float mCurrentVelocity = 0;
+ private boolean mShouldComputeVelocity = false;
+
+ private AnimationFactory<T> mAnimationFactory;
+
+ private ArrayList<SpringAnimation> mAnimations = new ArrayList<>();
+
+ /**
+ * @param direction Either {@link #X_DIRECTION} or {@link #Y_DIRECTION}.
+ * Determines which direction we use to calculate and set the velocity.
+ * @param factory The AnimationFactory is responsible for initializing and updating the
+ * SpringAnimations added to this class.
+ */
+ public SpringAnimationHandler(@Direction int direction, AnimationFactory<T> factory) {
+ mVelocityDirection = direction;
+ mAnimationFactory = factory;
+ }
+
+ /**
+ * Adds a new or recycled animation to the list of springs handled by this class.
+ *
+ * @param view The view the spring is attached to.
+ * @param object Used to initialize and update the spring.
+ */
+ public void add(View view, T object) {
+ SpringAnimation spring = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+ if (spring == null) {
+ spring = mAnimationFactory.initialize(object);
+ view.setTag(R.id.spring_animation_tag, spring);
+ }
+ mAnimationFactory.update(spring, object);
+ spring.setStartVelocity(mCurrentVelocity);
+ mAnimations.add(spring);
+ }
+
+ /**
+ * Stops and removes the spring attached to {@param view}.
+ */
+ public void remove(View view) {
+ SpringAnimation animation = (SpringAnimation) view.getTag(R.id.spring_animation_tag);
+ if (animation.canSkipToEnd()) {
+ animation.skipToEnd();
+ }
+ mAnimations.remove(animation);
+ }
+
+ public void addMovement(MotionEvent event) {
+ int action = event.getActionMasked();
+ if (DEBUG) Log.d(TAG, "addMovement#action=" + action);
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL:
+ case MotionEvent.ACTION_DOWN:
+ reset();
+ break;
+ }
+
+ getVelocityTracker().addMovement(event);
+ mShouldComputeVelocity = true;
+ }
+
+ public void animateToFinalPosition(float position, int startValue) {
+ animateToFinalPosition(position, startValue, mShouldComputeVelocity);
+ }
+
+ /**
+ * @param position The final animation position.
+ * @param startValue < 0 if scrolling from start to end; > 0 if scrolling from end to start
+ * The magnitude of the number changes how the spring will move.
+ * @param setVelocity If true, we set the velocity to {@link #mCurrentVelocity} before
+ * starting the animation.
+ */
+ private void animateToFinalPosition(float position, int startValue, boolean setVelocity) {
+ if (DEBUG) {
+ Log.d(TAG, "animateToFinalPosition#position=" + position + ", startValue=" + startValue);
+ }
+
+ if (mShouldComputeVelocity) {
+ mCurrentVelocity = computeVelocity();
+ }
+
+ int size = mAnimations.size();
+ for (int i = 0; i < size; ++i) {
+ mAnimations.get(i).setStartValue(startValue);
+ if (setVelocity) {
+ mAnimations.get(i).setStartVelocity(mCurrentVelocity);
+ }
+ mAnimations.get(i).animateToFinalPosition(position);
+ }
+
+ reset();
+ }
+
+ /**
+ * Similar to {@link #animateToFinalPosition(float, int)}, but used in cases where we want to
+ * manually set the velocity.
+ */
+ public void animateToPositionWithVelocity(float position, int startValue, float velocity) {
+ if (DEBUG) {
+ Log.d(TAG, "animateToPosition#pos=" + position + ", start=" + startValue
+ + ", velocity=" + velocity);
+ }
+
+ mCurrentVelocity = velocity;
+ mShouldComputeVelocity = false;
+ animateToFinalPosition(position, startValue, true);
+ }
+
+
+ public boolean isRunning() {
+ // All the animations run at the same time so we can just check the first one.
+ return !mAnimations.isEmpty() && mAnimations.get(0).isRunning();
+ }
+
+ public void skipToEnd() {
+ if (DEBUG) Log.d(TAG, "setStartVelocity#skipToEnd");
+ if (DEBUG) Log.v(TAG, "setStartVelocity#skipToEnd", new Exception());
+
+ int size = mAnimations.size();
+ for (int i = 0; i < size; ++i) {
+ if (mAnimations.get(i).canSkipToEnd()) {
+ mAnimations.get(i).skipToEnd();
+ }
+ }
+ }
+
+ public void reset() {
+ if (mVelocityTracker != null) {
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+ mCurrentVelocity = 0;
+ mShouldComputeVelocity = false;
+ }
+
+
+ private float computeVelocity() {
+ getVelocityTracker().computeCurrentVelocity(1000 /* millis */);
+
+ float velocity = isVerticalDirection()
+ ? getVelocityTracker().getYVelocity()
+ : getVelocityTracker().getXVelocity();
+ velocity *= VELOCITY_DAMPING_FACTOR;
+
+ if (DEBUG) Log.d(TAG, "computeVelocity=" + velocity);
+ return velocity;
+ }
+
+ private boolean isVerticalDirection() {
+ return mVelocityDirection == Y_DIRECTION;
+ }
+
+ private VelocityTracker getVelocityTracker() {
+ if (mVelocityTracker == null) {
+ mVelocityTracker = VelocityTracker.obtain();
+ }
+ return mVelocityTracker;
+ }
+
+ /**
+ * This interface is used to initialize and update the SpringAnimations added to the
+ * {@link SpringAnimationHandler}.
+ *
+ * @param <T> The object that each SpringAnimation is attached to.
+ */
+ public interface AnimationFactory<T> {
+
+ /**
+ * Initializes a new Spring for {@param object}.
+ */
+ SpringAnimation initialize(T object);
+
+ /**
+ * Updates the value of {@param spring} based on {@param object}.
+ */
+ void update(SpringAnimation spring, T object);
+ }
+
+ /**
+ * Helper method to create a new SpringAnimation for {@param view}.
+ */
+ public static SpringAnimation forView(View view, FloatPropertyCompat property, float finalPos) {
+ SpringAnimation spring = new SpringAnimation(view, property, finalPos);
+ spring.setSpring(new SpringForce(finalPos));
+ return spring;
+ }
+
+}
diff --git a/src/com/android/launcher3/badge/BadgeRenderer.java b/src/com/android/launcher3/badge/BadgeRenderer.java
index ba1977a..c2cc215 100644
--- a/src/com/android/launcher3/badge/BadgeRenderer.java
+++ b/src/com/android/launcher3/badge/BadgeRenderer.java
@@ -107,7 +107,8 @@
// Lazily load the background with shadow.
Bitmap backgroundWithShadow = mBackgroundsWithShadow.get(numChars);
if (backgroundWithShadow == null) {
- backgroundWithShadow = ShadowGenerator.createPillWithShadow(Color.WHITE, width, mSize);
+ backgroundWithShadow = new ShadowGenerator.Builder(Color.WHITE)
+ .setupBlurForSize(mSize).createPill(width, mSize);
mBackgroundsWithShadow.put(numChars, backgroundWithShadow);
}
canvas.save(Canvas.MATRIX_SAVE_FLAG);
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index e997a99..75a2a5d 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -25,16 +25,9 @@
import android.os.Bundle;
import android.os.UserHandle;
import android.support.annotation.Nullable;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
-import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.util.LooperExecuter;
import com.android.launcher3.util.PackageUserKey;
-
import java.util.List;
public abstract class LauncherAppsCompat {
@@ -55,7 +48,7 @@
}
private static LauncherAppsCompat sInstance;
- private static Object sInstanceLock = new Object();
+ private static final Object sInstanceLock = new Object();
public static LauncherAppsCompat getInstance(Context context) {
synchronized (sInstanceLock) {
@@ -88,62 +81,6 @@
public abstract List<ShortcutConfigActivityInfo> getCustomShortcutActivityList(
@Nullable PackageUserKey packageUser);
- /**
- * request.accept() will initiate the following flow:
- * -> go-to-system-process for actual processing (a)
- * -> callback-to-launcher on UI thread (b)
- * -> post callback on the worker thread (c)
- * -> Update model and unpin (in system) any shortcut not in out model. (d)
- *
- * Note that (b) will take at-least one frame as it involves posting callback from binder
- * thread to UI thread.
- * If (d) happens before we add this shortcut to our model, we will end up unpinning
- * the shortcut in the system.
- * Here its the caller's responsibility to add the newly created ShortcutInfo immediately
- * to the model (which may involves a single post-to-worker-thread). That will guarantee
- * that (d) happens after model is updated.
- */
- @Nullable
- public static ShortcutInfo createShortcutInfoFromPinItemRequest(
- Context context, final PinItemRequestCompat request, final long acceptDelay) {
- if (request != null &&
- request.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT &&
- request.isValid()) {
-
- if (acceptDelay <= 0) {
- if (!request.accept()) {
- return null;
- }
- } else {
- // Block the worker thread until the accept() is called.
- new LooperExecuter(LauncherModel.getWorkerLooper()).execute(new Runnable() {
- @Override
- public void run() {
- try {
- Thread.sleep(acceptDelay);
- } catch (InterruptedException e) {
- // Ignore
- }
- if (request.isValid()) {
- request.accept();
- }
- }
- });
- }
-
- 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 */);
- LauncherAppState.getInstance(context).getModel()
- .updateAndBindShortcutInfo(info, compat);
- return info;
- } else {
- return null;
- }
- }
-
public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
showAppDetailsForProfile(component, user, null, null);
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 647c315..cc3e5a7 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -29,23 +29,22 @@
import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
-
+import android.util.ArrayMap;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVL;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.util.PackageUserKey;
-
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public class LauncherAppsCompatVL extends LauncherAppsCompat {
protected final LauncherApps mLauncherApps;
protected final Context mContext;
- private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks = new HashMap<>();
+ private final ArrayMap<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks =
+ new ArrayMap<>();
LauncherAppsCompatVL(Context context) {
mContext = context;
@@ -131,43 +130,52 @@
}
private static class WrappedCallback extends LauncherApps.Callback {
- private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
+ private final LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
public WrappedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
mCallback = callback;
}
+ @Override
public void onPackageRemoved(String packageName, UserHandle user) {
mCallback.onPackageRemoved(packageName, user);
}
+ @Override
public void onPackageAdded(String packageName, UserHandle user) {
mCallback.onPackageAdded(packageName, user);
}
+ @Override
public void onPackageChanged(String packageName, UserHandle user) {
mCallback.onPackageChanged(packageName, user);
}
+ @Override
public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
mCallback.onPackagesAvailable(packageNames, user, replacing);
}
+ @Override
public void onPackagesUnavailable(String[] packageNames, UserHandle user,
boolean replacing) {
mCallback.onPackagesUnavailable(packageNames, user, replacing);
}
+ @Override
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
mCallback.onPackagesSuspended(packageNames, user);
}
+ @Override
public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
mCallback.onPackagesUnsuspended(packageNames, user);
}
- public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
- UserHandle user) {
+ @Override
+ public void onShortcutsChanged(@NonNull String packageName,
+ @NonNull List<ShortcutInfo> shortcuts,
+ @NonNull UserHandle user) {
List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
for (ShortcutInfo shortcutInfo : shortcuts) {
shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
index d145539..3214b46 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVO.java
@@ -18,20 +18,27 @@
import android.annotation.TargetApi;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.Parcelable;
import android.os.Process;
import android.os.UserHandle;
import android.support.annotation.Nullable;
-import android.util.Log;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.compat.ShortcutConfigActivityInfo.ShortcutConfigActivityInfoVO;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.PackageUserKey;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@@ -45,11 +52,6 @@
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, UserHandle user) {
try {
- // TODO: Temporary workaround until the API signature is updated
- if (false) {
- throw new PackageManager.NameNotFoundException();
- }
-
ApplicationInfo info = mLauncherApps.getApplicationInfo(packageName, flags, user);
return (info.flags & ApplicationInfo.FLAG_INSTALLED) == 0 || !info.enabled
? null : info;
@@ -64,34 +66,89 @@
List<ShortcutConfigActivityInfo> result = new ArrayList<>();
UserHandle myUser = Process.myUserHandle();
- try {
- Method m = LauncherApps.class.getDeclaredMethod("getShortcutConfigActivityList",
- String.class, UserHandle.class);
- final List<UserHandle> users;
- final String packageName;
- if (packageUser == null) {
- users = UserManagerCompat.getInstance(mContext).getUserProfiles();
- packageName = null;
- } else {
- users = new ArrayList<>(1);
- users.add(packageUser.mUser);
- packageName = packageUser.mPackageName;
- }
- for (UserHandle user : users) {
- boolean ignoreTargetSdk = myUser.equals(user);
- List<LauncherActivityInfo> activities =
- (List<LauncherActivityInfo>) m.invoke(mLauncherApps, packageName, user);
- for (LauncherActivityInfo activityInfo : activities) {
- if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion >=
- Build.VERSION_CODES.O) {
- result.add(new ShortcutConfigActivityInfoVO(activityInfo));
- }
+ final List<UserHandle> users;
+ final String packageName;
+ if (packageUser == null) {
+ users = UserManagerCompat.getInstance(mContext).getUserProfiles();
+ packageName = null;
+ } else {
+ users = new ArrayList<>(1);
+ users.add(packageUser.mUser);
+ packageName = packageUser.mPackageName;
+ }
+ for (UserHandle user : users) {
+ boolean ignoreTargetSdk = myUser.equals(user);
+ List<LauncherActivityInfo> activities =
+ mLauncherApps.getShortcutConfigActivityList(packageName, user);
+ for (LauncherActivityInfo activityInfo : activities) {
+ if (ignoreTargetSdk || activityInfo.getApplicationInfo().targetSdkVersion >=
+ Build.VERSION_CODES.O) {
+ result.add(new ShortcutConfigActivityInfoVO(activityInfo));
}
}
- } catch (Exception e) {
- Log.e("LauncherAppsCompatVO", "Error calling new API", e);
}
return result;
}
+
+ /**
+ * request.accept() will initiate the following flow:
+ * -> go-to-system-process for actual processing (a)
+ * -> callback-to-launcher on UI thread (b)
+ * -> post callback on the worker thread (c)
+ * -> Update model and unpin (in system) any shortcut not in out model. (d)
+ *
+ * Note that (b) will take at-least one frame as it involves posting callback from binder
+ * thread to UI thread.
+ * If (d) happens before we add this shortcut to our model, we will end up unpinning
+ * the shortcut in the system.
+ * Here its the caller's responsibility to add the newly created ShortcutInfo immediately
+ * to the model (which may involves a single post-to-worker-thread). That will guarantee
+ * that (d) happens after model is updated.
+ */
+ @Nullable
+ public static ShortcutInfo createShortcutInfoFromPinItemRequest(
+ Context context, final PinItemRequest request, final long acceptDelay) {
+ if (request != null &&
+ request.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT &&
+ request.isValid()) {
+
+ if (acceptDelay <= 0) {
+ if (!request.accept()) {
+ return null;
+ }
+ } else {
+ // Block the worker thread until the accept() is called.
+ new LooperExecutor(LauncherModel.getWorkerLooper()).execute(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(acceptDelay);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ if (request.isValid()) {
+ request.accept();
+ }
+ }
+ });
+ }
+
+ 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 */);
+ LauncherAppState.getInstance(context).getModel()
+ .updateAndBindShortcutInfo(info, compat);
+ return info;
+ } else {
+ return null;
+ }
+ }
+
+ public static PinItemRequest getPinItemRequest(Intent intent) {
+ Parcelable extra = intent.getParcelableExtra(LauncherApps.EXTRA_PIN_ITEM_REQUEST);
+ return extra instanceof PinItemRequest ? (PinItemRequest) extra : null;
+ }
}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index c7fe0ce..112cca5 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -16,9 +16,13 @@
package com.android.launcher3.compat;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageInstaller;
+import android.support.annotation.NonNull;
import java.util.HashMap;
+import java.util.List;
public abstract class PackageInstallerCompat {
@@ -46,19 +50,34 @@
public abstract void onStop();
public static final class PackageInstallInfo {
+ public final ComponentName componentName;
public final String packageName;
+ public final int state;
+ public final int progress;
- public int state;
- public int progress;
-
- public PackageInstallInfo(String packageName) {
- this.packageName = packageName;
+ private PackageInstallInfo(@NonNull PackageInstaller.SessionInfo info) {
+ this.state = STATUS_INSTALLING;
+ this.packageName = info.getAppPackageName();
+ this.componentName = new ComponentName(packageName, "");
+ this.progress = (int) (info.getProgress() * 100f);
}
public PackageInstallInfo(String packageName, int state, int progress) {
- this.packageName = packageName;
this.state = state;
+ this.packageName = packageName;
+ this.componentName = new ComponentName(packageName, "");
this.progress = progress;
}
+
+ public static PackageInstallInfo fromInstallingState(PackageInstaller.SessionInfo info) {
+ return new PackageInstallInfo(info);
+ }
+
+ public static PackageInstallInfo fromState(int state, String packageName) {
+ return new PackageInstallInfo(packageName, state, 0 /* progress */);
+ }
+
}
+
+ public abstract List<PackageInstaller.SessionInfo> getAllVerifiedSessions();
}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index b87582f..1ffd3da 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -17,34 +17,44 @@
package com.android.launcher3.compat;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionCallback;
import android.content.pm.PackageInstaller.SessionInfo;
import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
+import android.text.TextUtils;
import android.util.SparseArray;
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Thunk;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
public class PackageInstallerCompatVL extends PackageInstallerCompat {
+ private static final boolean DEBUG = false;
+
@Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
@Thunk final PackageInstaller mInstaller;
private final IconCache mCache;
private final Handler mWorker;
+ private final Context mAppContext;
+ private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
PackageInstallerCompatVL(Context context) {
+ mAppContext = context.getApplicationContext();
mInstaller = context.getPackageManager().getPackageInstaller();
mCache = LauncherAppState.getInstance(context).getIconCache();
mWorker = new Handler(LauncherModel.getWorkerLooper());
-
mInstaller.registerSessionCallback(mCallback, mWorker);
}
@@ -52,7 +62,7 @@
public HashMap<String, Integer> updateAndGetActiveSessionCache() {
HashMap<String, Integer> activePackages = new HashMap<>();
UserHandle user = Process.myUserHandle();
- for (SessionInfo info : mInstaller.getAllSessions()) {
+ for (SessionInfo info : getAllVerifiedSessions()) {
addSessionInfoToCache(info, user);
if (info.getAppPackageName() != null) {
activePackages.put(info.getAppPackageName(), (int) (info.getProgress() * 100));
@@ -86,7 +96,14 @@
@Override
public void onCreated(int sessionId) {
- pushSessionDisplayToLauncher(sessionId);
+ SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app != null) {
+ app.getModel().onInstallSessionCreated(
+ PackageInstallInfo.fromInstallingState(sessionInfo));
+ }
+ }
}
@Override
@@ -97,18 +114,17 @@
mActiveSessions.remove(sessionId);
if (packageName != null) {
- sendUpdate(new PackageInstallInfo(packageName,
- success ? STATUS_INSTALLED : STATUS_FAILED, 0));
+ sendUpdate(PackageInstallInfo.fromState(
+ success ? STATUS_INSTALLED : STATUS_FAILED,
+ packageName));
}
}
@Override
public void onProgressChanged(int sessionId, float progress) {
- SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
if (session != null && session.getAppPackageName() != null) {
- sendUpdate(new PackageInstallInfo(session.getAppPackageName(),
- STATUS_INSTALLING,
- (int) (session.getProgress() * 100)));
+ sendUpdate(PackageInstallInfo.fromInstallingState(session));
}
}
@@ -120,16 +136,48 @@
pushSessionDisplayToLauncher(sessionId);
}
- private void pushSessionDisplayToLauncher(int sessionId) {
- SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
+ SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
if (session != null && session.getAppPackageName() != null) {
+ mActiveSessions.put(sessionId, session.getAppPackageName());
addSessionInfoToCache(session, Process.myUserHandle());
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
-
if (app != null) {
app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
}
+ return session;
}
+ return null;
}
};
+
+ private PackageInstaller.SessionInfo verify(PackageInstaller.SessionInfo sessionInfo) {
+ if (sessionInfo == null
+ || sessionInfo.getInstallerPackageName() == null
+ || TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
+ return null;
+ }
+ String pkg = sessionInfo.getInstallerPackageName();
+ synchronized (mSessionVerifiedMap) {
+ if (!mSessionVerifiedMap.containsKey(pkg)) {
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
+ boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
+ ApplicationInfo.FLAG_SYSTEM, Process.myUserHandle()) != null;
+ mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
+ }
+ }
+ return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
+ }
+
+ @Override
+ public List<SessionInfo> getAllVerifiedSessions() {
+ List<SessionInfo> list = new ArrayList<>(mInstaller.getAllSessions());
+ Iterator<SessionInfo> it = list.iterator();
+ while (it.hasNext()) {
+ if (verify(it.next()) == null) {
+ it.remove();
+ }
+ }
+ return list;
+ }
}
diff --git a/src/com/android/launcher3/compat/PinItemRequestCompat.java b/src/com/android/launcher3/compat/PinItemRequestCompat.java
deleted file mode 100644
index 1308cba..0000000
--- a/src/com/android/launcher3/compat/PinItemRequestCompat.java
+++ /dev/null
@@ -1,126 +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.compat;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.launcher3.Utilities;
-
-/**
- * A wrapper around platform implementation of PinItemRequestCompat until the
- * updated SDK is available.
- */
-public class PinItemRequestCompat implements Parcelable {
-
- public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
-
- public static final int REQUEST_TYPE_SHORTCUT = 1;
- public static final int REQUEST_TYPE_APPWIDGET = 2;
-
- private final Parcelable mObject;
-
- private PinItemRequestCompat(Parcelable object) {
- mObject = object;
- }
-
- public int getRequestType() {
- return (Integer) invokeMethod("getRequestType");
- }
-
- public ShortcutInfo getShortcutInfo() {
- return (ShortcutInfo) invokeMethod("getShortcutInfo");
- }
-
- public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
- try {
- return (AppWidgetProviderInfo) mObject.getClass()
- .getDeclaredMethod("getAppWidgetProviderInfo", Context.class)
- .invoke(mObject, context);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public boolean isValid() {
- return (Boolean) invokeMethod("isValid");
- }
-
- public boolean accept() {
- return (Boolean) invokeMethod("accept");
- }
-
- public boolean accept(Bundle options) {
- try {
- return (Boolean) mObject.getClass().getDeclaredMethod("accept", Bundle.class)
- .invoke(mObject, options);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- public Bundle getExtras() {
- try {
- return (Bundle) mObject.getClass().getDeclaredMethod("getExtras").invoke(mObject);
- } catch (Exception e) {
- return null;
- }
- }
-
- private Object invokeMethod(String methodName) {
- try {
- return mObject.getClass().getDeclaredMethod(methodName).invoke(mObject);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeParcelable(mObject, i);
- }
-
- public static final Parcelable.Creator<PinItemRequestCompat> CREATOR =
- new Parcelable.Creator<PinItemRequestCompat>() {
- public PinItemRequestCompat createFromParcel(Parcel source) {
- Parcelable object = source.readParcelable(null);
- return new PinItemRequestCompat(object);
- }
-
- public PinItemRequestCompat[] newArray(int size) {
- return new PinItemRequestCompat[size];
- }
- };
-
- public static PinItemRequestCompat getPinItemRequest(Intent intent) {
- if (!Utilities.isAtLeastO()) {
- return null;
- }
- Parcelable extra = intent.getParcelableExtra(EXTRA_PIN_ITEM_REQUEST);
- return extra == null ? null : new PinItemRequestCompat(extra);
- }
-}
diff --git a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
index 4a55e8c..31c0087 100644
--- a/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
+++ b/src/com/android/launcher3/compat/ShortcutConfigActivityInfo.java
@@ -37,8 +37,6 @@
import com.android.launcher3.R;
import com.android.launcher3.ShortcutInfo;
-import java.lang.reflect.Method;
-
/**
* Wrapper class for representing a shortcut configure activity.
*/
@@ -127,7 +125,7 @@
}
@TargetApi(26)
- static class ShortcutConfigActivityInfoVO extends ShortcutConfigActivityInfo {
+ public static class ShortcutConfigActivityInfoVO extends ShortcutConfigActivityInfo {
private final LauncherActivityInfo mInfo;
@@ -151,15 +149,13 @@
if (getUser().equals(Process.myUserHandle())) {
return super.startConfigActivity(activity, requestCode);
}
+ IntentSender is = activity.getSystemService(LauncherApps.class)
+ .getShortcutConfigActivityIntent(mInfo);
try {
- Method m = LauncherApps.class.getDeclaredMethod(
- "getShortcutConfigActivityIntent", LauncherActivityInfo.class);
- IntentSender is = (IntentSender) m.invoke(
- activity.getSystemService(LauncherApps.class), mInfo);
activity.startIntentSenderForResult(is, requestCode, null, 0, 0, 0);
return true;
- } catch (Exception e) {
- Log.e(TAG, "Error calling new API", e);
+ } catch (IntentSender.SendIntentException e) {
+ Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
return false;
}
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index 45525f5..bb42573 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -21,13 +21,11 @@
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
-
-import com.android.launcher3.Utilities;
+import android.util.ArrayMap;
import com.android.launcher3.util.LongArrayMap;
-
+import com.android.launcher3.util.ManagedProfileHeuristic;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
public class UserManagerCompatVL extends UserManagerCompat {
@@ -40,7 +38,7 @@
protected LongArrayMap<UserHandle> mUsers;
// Create a separate reverse map as LongArrayMap.indexOfValue checks if objects are same
// and not {@link Object#equals}
- protected HashMap<UserHandle, Long> mUserToSerialMap;
+ protected ArrayMap<UserHandle, Long> mUserToSerialMap;
UserManagerCompatVL(Context context) {
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -88,7 +86,7 @@
public void enableAndResetCache() {
synchronized (this) {
mUsers = new LongArrayMap<>();
- mUserToSerialMap = new HashMap<>();
+ mUserToSerialMap = new ArrayMap<>();
List<UserHandle> users = mUserManager.getUserProfiles();
if (users != null) {
for (UserHandle user : users) {
@@ -122,7 +120,7 @@
@Override
public long getUserCreationTime(UserHandle user) {
- SharedPreferences prefs = Utilities.getPrefs(mContext);
+ SharedPreferences prefs = ManagedProfileHeuristic.prefs(mContext);
String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
if (!prefs.contains(key)) {
prefs.edit().putLong(key, System.currentTimeMillis()).apply();
diff --git a/src/com/android/launcher3/compat/WallpaperColorsCompat.java b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
new file mode 100644
index 0000000..58d2a80
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperColorsCompat.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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;
+
+/**
+ * A compatibility layer around platform implementation of WallpaperColors
+ */
+public class WallpaperColorsCompat {
+
+ public static final int HINT_SUPPORTS_DARK_TEXT = 0x1;
+
+ private final int mPrimaryColor;
+ private final int mSecondaryColor;
+ private final int mTertiaryColor;
+ private final int mColorHints;
+
+ public WallpaperColorsCompat(int primaryColor, int secondaryColor, int tertiaryColor,
+ int colorHints) {
+ mPrimaryColor = primaryColor;
+ mSecondaryColor = secondaryColor;
+ mTertiaryColor = tertiaryColor;
+ mColorHints = colorHints;
+ }
+
+ public int getPrimaryColor() {
+ return mPrimaryColor;
+ }
+
+ public int getSecondaryColor() {
+ return mSecondaryColor;
+ }
+
+ public int getTertiaryColor() {
+ return mTertiaryColor;
+ }
+
+ public int getColorHints() {
+ return mColorHints;
+ }
+
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompat.java b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
new file mode 100644
index 0000000..cbcabdf
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompat.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.support.annotation.Nullable;
+
+import com.android.launcher3.Utilities;
+
+public abstract class WallpaperManagerCompat {
+
+ private static final Object sInstanceLock = new Object();
+ private static WallpaperManagerCompat sInstance;
+
+ public static WallpaperManagerCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ context = context.getApplicationContext();
+
+ if (Utilities.isAtLeastO()) {
+ try {
+ sInstance = new WallpaperManagerCompatVOMR1(context);
+ } catch (Exception e) {
+ // The wallpaper APIs do not yet exist
+ }
+ }
+ if (sInstance == null) {
+ sInstance = new WallpaperManagerCompatVL(context);
+ }
+ }
+ return sInstance;
+ }
+ }
+
+
+ public abstract @Nullable WallpaperColorsCompat getWallpaperColors(int which);
+
+ public abstract void addOnColorsChangedListener(OnColorsChangedListenerCompat listener);
+
+ /**
+ * Interface definition for a callback to be invoked when colors change on a wallpaper.
+ */
+ public interface OnColorsChangedListenerCompat {
+
+ void onColorsChanged(WallpaperColorsCompat colors, int which);
+ }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
new file mode 100644
index 0000000..8e572ee
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVL.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.Nullable;
+import android.support.v7.graphics.Palette;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.launcher3.Utilities;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
+public class WallpaperManagerCompatVL extends WallpaperManagerCompat {
+
+ private static final String TAG = "WMCompatVL";
+
+ private static final String VERSION_PREFIX = "1,";
+ private static final String KEY_COLORS = "wallpaper_parsed_colors";
+ private static final String ACTION_EXTRACTION_COMPLETE =
+ "com.android.launcher3.compat.WallpaperManagerCompatVL.EXTRACTION_COMPLETE";
+
+ private final ArrayList<OnColorsChangedListenerCompat> mListeners = new ArrayList<>();
+
+ private final Context mContext;
+ private WallpaperColorsCompat mColorsCompat;
+
+ WallpaperManagerCompatVL(Context context) {
+ mContext = context;
+
+ String colors = getDevicePrefs(mContext).getString(KEY_COLORS, "");
+ int wallpaperId = -1;
+ if (colors.startsWith(VERSION_PREFIX)) {
+ Pair<Integer, WallpaperColorsCompat> storedValue = parseValue(colors);
+ wallpaperId = storedValue.first;
+ mColorsCompat = storedValue.second;
+ }
+
+ if (wallpaperId == -1 || wallpaperId != getWallpaperId(context)) {
+ reloadColors();
+ }
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ reloadColors();
+ }
+ }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+
+ // Register a receiver for results
+ String permission = null;
+ // Find a permission which only we can use.
+ try {
+ for (PermissionInfo info : context.getPackageManager().getPackageInfo(
+ context.getPackageName(),
+ PackageManager.GET_PERMISSIONS).permissions) {
+ if ((info.protectionLevel & PermissionInfo.PROTECTION_SIGNATURE) != 0) {
+ permission = info.name;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Something went wrong. ignore
+ Log.d(TAG, "Unable to get permission info", e);
+ }
+ mContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ handleResult(intent.getStringExtra(KEY_COLORS));
+ }
+ }, new IntentFilter(ACTION_EXTRACTION_COMPLETE), permission, new Handler());
+ }
+
+ @Nullable
+ @Override
+ public WallpaperColorsCompat getWallpaperColors(int which) {
+ return which == FLAG_SYSTEM ? mColorsCompat : null;
+ }
+
+ @Override
+ public void addOnColorsChangedListener(OnColorsChangedListenerCompat listener) {
+ mListeners.add(listener);
+ }
+
+ private void reloadColors() {
+ JobInfo job = new JobInfo.Builder(Utilities.WALLPAPER_COMPAT_JOB_ID,
+ new ComponentName(mContext, ColorExtractionService.class))
+ .setMinimumLatency(0).build();
+ ((JobScheduler) mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(job);
+ }
+
+ private void handleResult(String result) {
+ getDevicePrefs(mContext).edit().putString(KEY_COLORS, result).apply();
+ mColorsCompat = parseValue(result).second;
+ for (OnColorsChangedListenerCompat listener : mListeners) {
+ listener.onColorsChanged(mColorsCompat, FLAG_SYSTEM);
+ }
+ }
+
+ private static final int getWallpaperId(Context context) {
+ if (!Utilities.ATLEAST_NOUGAT) {
+ return -1;
+ }
+ return context.getSystemService(WallpaperManager.class).getWallpaperId(FLAG_SYSTEM);
+ }
+
+ /**
+ * Parses the stored value and returns the wallpaper id and wallpaper colors.
+ */
+ private static Pair<Integer, WallpaperColorsCompat> parseValue(String value) {
+ String[] parts = value.split(",");
+ Integer wallpaperId = Integer.parseInt(parts[1]);
+ if (parts.length == 2) {
+ // There is no wallpaper color info present, eg when live wallpaper has no preview.
+ return Pair.create(wallpaperId, null);
+ }
+
+ int primary = parts.length > 2 ? Integer.parseInt(parts[2]) : 0;
+ int secondary = parts.length > 3 ? Integer.parseInt(parts[3]) : 0;
+ int tertiary = parts.length > 4 ? Integer.parseInt(parts[4]) : 0;
+
+ return Pair.create(wallpaperId, new WallpaperColorsCompat(primary, secondary, tertiary,
+ 0 /* hints */));
+ }
+
+ /**
+ * Intent service to handle color extraction
+ */
+ public static class ColorExtractionService extends JobService implements Runnable {
+ private static final int MAX_WALLPAPER_EXTRACTION_AREA = 112 * 112;
+
+ private HandlerThread mWorkerThread;
+ private Handler mWorkerHandler;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mWorkerThread = new HandlerThread("ColorExtractionService");
+ mWorkerThread.start();
+ mWorkerHandler = new Handler(mWorkerThread.getLooper());
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mWorkerThread.quit();
+ }
+
+ @Override
+ public boolean onStartJob(final JobParameters jobParameters) {
+ mWorkerHandler.post(this);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ mWorkerHandler.removeCallbacksAndMessages(null);
+ return true;
+ }
+
+ /**
+ * Extracts the wallpaper colors and sends the result back through the receiver.
+ */
+ @Override
+ public void run() {
+ int wallpaperId = getWallpaperId(this);
+
+ Bitmap bitmap = null;
+ Drawable drawable = null;
+
+ WallpaperManager wm = WallpaperManager.getInstance(this);
+ WallpaperInfo info = wm.getWallpaperInfo();
+ if (info != null) {
+ // For live wallpaper, extract colors from thumbnail
+ drawable = info.loadThumbnail(getPackageManager());
+ } else {
+ if (Utilities.ATLEAST_NOUGAT) {
+ try (ParcelFileDescriptor fd = wm.getWallpaperFile(FLAG_SYSTEM)) {
+ BitmapRegionDecoder decoder = BitmapRegionDecoder
+ .newInstance(fd.getFileDescriptor(), false);
+
+ int requestedArea = decoder.getWidth() * decoder.getHeight();
+ BitmapFactory.Options options = new BitmapFactory.Options();
+
+ if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ double areaRatio =
+ (double) requestedArea / MAX_WALLPAPER_EXTRACTION_AREA;
+ double nearestPowOf2 =
+ Math.floor(Math.log(areaRatio) / (2 * Math.log(2)));
+ options.inSampleSize = (int) Math.pow(2, nearestPowOf2);
+ }
+ Rect region = new Rect(0, 0, decoder.getWidth(), decoder.getHeight());
+ bitmap = decoder.decodeRegion(region, options);
+ decoder.recycle();
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
+ }
+ }
+ if (bitmap == null) {
+ drawable = wm.getDrawable();
+ }
+ }
+
+ if (drawable != null) {
+ // Calculate how big the bitmap needs to be.
+ // This avoids unnecessary processing and allocation inside Palette.
+ final int requestedArea = drawable.getIntrinsicWidth() *
+ drawable.getIntrinsicHeight();
+ double scale = 1;
+ if (requestedArea > MAX_WALLPAPER_EXTRACTION_AREA) {
+ scale = Math.sqrt(MAX_WALLPAPER_EXTRACTION_AREA / (double) requestedArea);
+ }
+ bitmap = Bitmap.createBitmap((int) (drawable.getIntrinsicWidth() * scale),
+ (int) (drawable.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888);
+ final Canvas bmpCanvas = new Canvas(bitmap);
+ drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ drawable.draw(bmpCanvas);
+ }
+
+ String value = VERSION_PREFIX + wallpaperId;
+
+ if (bitmap != null) {
+ Palette palette = Palette.from(bitmap).generate();
+ bitmap.recycle();
+
+ StringBuilder builder = new StringBuilder(value);
+ List<Pair<Integer,Integer>> colorsToOccurrences = new ArrayList<>();
+ for (Palette.Swatch swatch : palette.getSwatches()) {
+ colorsToOccurrences.add(new Pair(swatch.getRgb(), swatch.getPopulation()));
+ }
+
+ Collections.sort(colorsToOccurrences, new Comparator<Pair<Integer, Integer>>() {
+ @Override
+ public int compare(Pair<Integer, Integer> a, Pair<Integer, Integer> b) {
+ return b.second - a.second;
+ }
+ });
+
+ for (int i=0; i < Math.min(3, colorsToOccurrences.size()); i++) {
+ builder.append(',').append(colorsToOccurrences.get(i).first);
+ }
+
+ value = builder.toString();
+ }
+
+ // Send the result
+ sendBroadcast(new Intent(ACTION_EXTRACTION_COMPLETE)
+ .setPackage(getPackageName())
+ .putExtra(KEY_COLORS, value));
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
new file mode 100644
index 0000000..6233fab
--- /dev/null
+++ b/src/com/android/launcher3/compat/WallpaperManagerCompatVOMR1.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.annotation.TargetApi;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+@TargetApi(Build.VERSION_CODES.O)
+public class WallpaperManagerCompatVOMR1 extends WallpaperManagerCompat {
+
+ private static final String TAG = "WMCompatVOMR1";
+
+ private final WallpaperManager mWm;
+
+ private final Class mOCLClass;
+ private final Method mAddOCLMethod;
+
+ private final Method mWCGetMethod;
+ private final Method mWCGetPrimaryColorMethod;
+ private final Method mWCGetSecondaryColorMethod;
+ private final Method mWCGetTertiaryColorMethod;
+ private final Method mWCColorHintsMethod;
+
+ WallpaperManagerCompatVOMR1(Context context) throws Exception {
+ mWm = context.getSystemService(WallpaperManager.class);
+
+ mOCLClass = Class.forName("android.app.WallpaperManager$OnColorsChangedListener");
+ mAddOCLMethod = WallpaperManager.class.getDeclaredMethod(
+ "addOnColorsChangedListener", mOCLClass);
+
+ mWCGetMethod = WallpaperManager.class.getDeclaredMethod("getWallpaperColors", int.class);
+ Class wallpaperColorsClass = mWCGetMethod.getReturnType();
+ mWCGetPrimaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getPrimaryColor");
+ mWCGetSecondaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getSecondaryColor");
+ mWCGetTertiaryColorMethod = wallpaperColorsClass.getDeclaredMethod("getTertiaryColor");
+ mWCColorHintsMethod = wallpaperColorsClass.getDeclaredMethod("getColorHints");
+ }
+
+ @Nullable
+ @Override
+ public WallpaperColorsCompat getWallpaperColors(int which) {
+ try {
+ return convertColorsObject(mWCGetMethod.invoke(mWm, which));
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling wallpaper API", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void addOnColorsChangedListener(final OnColorsChangedListenerCompat listener) {
+ Object onChangeListener = Proxy.newProxyInstance(
+ WallpaperManager.class.getClassLoader(),
+ new Class[]{mOCLClass},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object o, Method method, Object[] objects)
+ throws Throwable {
+ String methodName = method.getName();
+ if ("onColorsChanged".equals(methodName)) {
+ listener.onColorsChanged(
+ convertColorsObject(objects[0]), (Integer) objects[1]);
+ } else if ("toString".equals(methodName)) {
+ return listener.toString();
+ }
+ return null;
+ }
+ });
+ try {
+ mAddOCLMethod.invoke(mWm, onChangeListener);
+ } catch (Exception e) {
+ Log.e(TAG, "Error calling wallpaper API", e);
+ }
+ }
+
+ private WallpaperColorsCompat convertColorsObject(Object colors) throws Exception {
+ if (colors == null) {
+ return null;
+ }
+ Color primary = (Color) mWCGetPrimaryColorMethod.invoke(colors);
+ Color secondary = (Color) mWCGetSecondaryColorMethod.invoke(colors);
+ Color tertiary = (Color) mWCGetTertiaryColorMethod.invoke(colors);
+ int primaryVal = primary != null ? primary.toArgb() : 0;
+ int secondaryVal = secondary != null ? secondary.toArgb() : 0;
+ int tertiaryVal = tertiary != null ? tertiary.toArgb() : 0;
+ int colorHints = (Integer) mWCColorHintsMethod.invoke(colors);
+ return new WallpaperColorsCompat(primaryVal, secondaryVal, tertiaryVal, colorHints);
+ }
+}
diff --git a/src/com/android/launcher3/discovery/AppDiscoveryItem.java b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
index 09c91ac..2e48b25 100644
--- a/src/com/android/launcher3/discovery/AppDiscoveryItem.java
+++ b/src/com/android/launcher3/discovery/AppDiscoveryItem.java
@@ -16,7 +16,6 @@
package com.android.launcher3.discovery;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 09592a8..01893bd 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -28,6 +28,7 @@
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Intent;
+import android.content.pm.LauncherApps.PinItemRequest;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Point;
@@ -50,7 +51,7 @@
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -60,7 +61,7 @@
import com.android.launcher3.widget.WidgetHostViewLoader;
import com.android.launcher3.widget.WidgetImageView;
-@TargetApi(Build.VERSION_CODES.N_MR1)
+@TargetApi(Build.VERSION_CODES.O)
public class AddItemActivity extends BaseActivity implements OnLongClickListener, OnTouchListener {
private static final int SHADOW_SIZE = 10;
@@ -70,7 +71,7 @@
private final PointF mLastTouchPos = new PointF();
- private PinItemRequestCompat mRequest;
+ private PinItemRequest mRequest;
private LauncherAppState mApp;
private InvariantDeviceProfile mIdp;
@@ -83,11 +84,13 @@
private int mPendingBindWidgetId;
private Bundle mWidgetOptions;
+ private boolean mFinishOnPause = false;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mRequest = PinItemRequestCompat.getPinItemRequest(getIntent());
+ mRequest = LauncherAppsCompatVO.getPinItemRequest(getIntent());
if (mRequest == null) {
finish();
return;
@@ -101,9 +104,9 @@
mDeviceProfile = mIdp.getDeviceProfile(getApplicationContext());
setContentView(R.layout.add_item_confirmation_activity);
- mWidgetCell = (LivePreviewWidgetCell) findViewById(R.id.widget_cell);
+ mWidgetCell = findViewById(R.id.widget_cell);
- if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+ if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
setupShortcut();
} else {
if (!setupWidget()) {
@@ -162,6 +165,7 @@
startActivity(homeIntent,
ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out).toBundle());
+ mFinishOnPause = true;
// Start a system drag and drop. We use a transparent bitmap as preview for system drag
// as the preview is handled internally by launcher.
@@ -181,6 +185,14 @@
return false;
}
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mFinishOnPause) {
+ finish();
+ }
+ }
+
private void setupShortcut() {
PinShortcutRequestActivityInfo shortcutInfo =
new PinShortcutRequestActivityInfo(mRequest, this);
@@ -226,7 +238,7 @@
* Called when place-automatically button is clicked.
*/
public void onPlaceAutomaticallyClick(View v) {
- if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+ if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
InstallShortcutReceiver.queueShortcut(
new ShortcutInfoCompat(mRequest.getShortcutInfo()), this);
logCommand(Action.Command.CONFIRM);
diff --git a/src/com/android/launcher3/dragndrop/BaseItemDragListener.java b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
new file mode 100644
index 0000000..d0f2629
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/BaseItemDragListener.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.widget.PendingItemDragHelper;
+
+import java.util.UUID;
+
+/**
+ * {@link DragSource} for handling drop from a different window.
+ */
+public abstract class BaseItemDragListener implements
+ View.OnDragListener, DragSource, DragOptions.PreDragCondition {
+
+ private static final String TAG = "BaseItemDragListener";
+
+ private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
+ public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
+
+ // Position of preview relative to the touch location
+ private final Rect mPreviewRect;
+
+ private final int mPreviewBitmapWidth;
+ private final int mPreviewViewWidth;
+
+ // Randomly generated id used to verify the drag event.
+ private final String mId;
+
+ protected Launcher mLauncher;
+ private DragController mDragController;
+ private long mDragStartTime;
+
+ public BaseItemDragListener(Rect previewRect, int previewBitmapWidth, int previewViewWidth) {
+ mPreviewRect = previewRect;
+ mPreviewBitmapWidth = previewBitmapWidth;
+ mPreviewViewWidth = previewViewWidth;
+ mId = UUID.randomUUID().toString();
+ }
+
+ protected BaseItemDragListener(Parcel parcel) {
+ mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
+ mPreviewBitmapWidth = parcel.readInt();
+ mPreviewViewWidth = parcel.readInt();
+ mId = parcel.readString();
+ }
+
+ protected void writeToParcel(Parcel parcel, int i) {
+ mPreviewRect.writeToParcel(parcel, i);
+ parcel.writeInt(mPreviewBitmapWidth);
+ parcel.writeInt(mPreviewViewWidth);
+ parcel.writeString(mId);
+ }
+
+ public String getMimeType() {
+ return MIME_TYPE_PREFIX + mId;
+ }
+
+ public void setLauncher(Launcher launcher) {
+ mLauncher = launcher;
+ mDragController = launcher.getDragController();
+ }
+
+ @Override
+ public boolean onDrag(View view, DragEvent event) {
+ if (mLauncher == null || mDragController == null) {
+ postCleanup();
+ return false;
+ }
+ if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+ if (onDragStart(event)) {
+ return true;
+ } else {
+ postCleanup();
+ return false;
+ }
+ }
+ return mDragController.onDragEvent(mDragStartTime, event);
+ }
+
+ protected boolean onDragStart(DragEvent event) {
+ ClipDescription desc = event.getClipDescription();
+ if (desc == null || !desc.hasMimeType(getMimeType())) {
+ Log.e(TAG, "Someone started a dragAndDrop before us.");
+ return false;
+ }
+
+ Point downPos = new Point((int) event.getX(), (int) event.getY());
+ DragOptions options = new DragOptions();
+ options.systemDndStartPoint = downPos;
+ options.preDragCondition = this;
+
+ // We use drag event position as the screenPos for the preview image. Since mPreviewRect
+ // already includes the view position relative to the drag event on the source window,
+ // and the absolute position (position relative to the screen) of drag event is same
+ // across windows, using drag position here give a good estimate for relative position
+ // to source window.
+ createDragHelper().startDrag(new Rect(mPreviewRect),
+ mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
+ mDragStartTime = SystemClock.uptimeMillis();
+ return true;
+ }
+
+ protected abstract PendingItemDragHelper createDragHelper();
+
+ @Override
+ public boolean shouldStartDrag(double distanceDragged) {
+ // Stay in pre-drag mode, if workspace is locked.
+ return !mLauncher.isWorkspaceLocked();
+ }
+
+ @Override
+ public void onPreDragStart(DropTarget.DragObject dragObject) {
+ // The predrag starts when the workspace is not yet loaded. In some cases we set
+ // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
+ // dragView from being visible. Instead just skip the fade-in animation here.
+ mLauncher.getDragLayer().setAlpha(1);
+
+ dragObject.dragView.setColor(
+ mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
+ }
+
+ @Override
+ public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
+ if (dragStarted) {
+ dragObject.dragView.setColor(0);
+ }
+ }
+
+ @Override
+ public boolean supportsAppInfoDropTarget() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsDeleteDropTarget() {
+ return false;
+ }
+
+ @Override
+ public float getIntrinsicIconScaleFactor() {
+ return 1f;
+ }
+
+ @Override
+ public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
+ boolean success) {
+ if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
+ !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
+ // Exit spring loaded mode if we have not successfully dropped or have not handled the
+ // drop in Workspace
+ mLauncher.exitSpringLoadedDragModeDelayed(true,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ }
+
+ if (!success) {
+ d.deferDragViewCleanupPostAnimation = false;
+ }
+ postCleanup();
+ }
+
+ private void postCleanup() {
+ if (mLauncher != null) {
+ // Remove any drag params from the launcher intent since the drag operation is complete.
+ Intent newIntent = new Intent(mLauncher.getIntent());
+ newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
+ mLauncher.setIntent(newIntent);
+ }
+
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ removeListener();
+ }
+ });
+ }
+
+ public void removeListener() {
+ if (mLauncher != null) {
+ mLauncher.getDragLayer().setOnDragListener(null);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 7410ae6..b852714 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -165,7 +165,7 @@
? res.getDimensionPixelSize(R.dimen.pre_drag_view_scale) : 0f;
final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
registrationY, initialDragViewScale, scaleDps);
-
+ dragView.setItemInfo(dragInfo);
mDragObject.dragComplete = false;
if (mOptions.isAccessibleDrag) {
// For an accessible drag, we assume the view is being dragged from the center.
@@ -210,13 +210,13 @@
}
private void callOnDragStart() {
- for (DragListener listener : new ArrayList<>(mListeners)) {
- listener.onDragStart(mDragObject, mOptions);
- }
if (mOptions.preDragCondition != null) {
mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
}
mIsInPreDrag = false;
+ for (DragListener listener : new ArrayList<>(mListeners)) {
+ listener.onDragStart(mDragObject, mOptions);
+ }
}
/**
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 65c0f29..d8a3024 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.view.DragEvent;
import android.view.MotionEvent;
-
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.Utilities;
@@ -97,16 +96,11 @@
*/
class SystemDragDriver extends DragDriver {
- private final DragObject mDragObject;
- private final Context mContext;
-
float mLastX = 0;
float mLastY = 0;
- public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
+ SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
super(dragController);
- mDragObject = dragObject;
- mContext = context;
}
@Override
@@ -162,10 +156,10 @@
* Class for driving an internal (i.e. not using framework) drag/drop operation.
*/
class InternalDragDriver extends DragDriver {
- public InternalDragDriver(DragController dragController) {
+ InternalDragDriver(DragController dragController) {
super(dragController);
}
@Override
public boolean onDragEvent (DragEvent event) { return false; }
-};
+}
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 7178c5e..be5f01a 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -27,6 +27,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Region;
+import android.support.v4.graphics.ColorUtils;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -54,10 +55,12 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.widget.WidgetsBottomSheet;
@@ -72,9 +75,6 @@
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
- // Scrim color without any alpha component.
- private static final int SCRIM_COLOR = Color.BLACK & 0x00FFFFFF;
-
private final int[] mTmpXY = new int[2];
@Thunk DragController mDragController;
@@ -107,6 +107,7 @@
// Related to adjacent page hints
private final Rect mScrollChildPosition = new Rect();
private final ViewGroupFocusHelper mFocusIndicatorHelper;
+ private final WallpaperColorInfo mWallpaperColorInfo;
// Related to pinch-to-go-to-overview gesture.
private PinchToOverviewListener mPinchListener = null;
@@ -130,6 +131,7 @@
mIsRtl = Utilities.isRtl(getResources());
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
+ mWallpaperColorInfo = WallpaperColorInfo.getInstance(getContext());
}
public void setup(Launcher launcher, DragController dragController,
@@ -451,7 +453,8 @@
@Override
public void setInsets(Rect insets) {
super.setInsets(insets);
- setBackgroundResource(insets.top == 0 ? 0 : R.drawable.workspace_bg);
+ setBackground(insets.top == 0 ? null
+ : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
}
@Override
@@ -876,7 +879,10 @@
getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
}
- canvas.drawColor((alpha << 24) | SCRIM_COLOR);
+ // for super light wallpaper it needs to be darken for contrast to workspace
+ // for dark wallpapers the text is white so darkening works as well
+ int color = ColorUtils.compositeColors(0x66000000, mWallpaperColorInfo.getMainColor());
+ canvas.drawColor(ColorUtils.setAlphaComponent(color, alpha));
canvas.restore();
}
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
index 230fa2d..9433aad 100644
--- a/src/com/android/launcher3/dragndrop/DragOptions.java
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -17,8 +17,6 @@
package com.android.launcher3.dragndrop;
import android.graphics.Point;
-import android.support.annotation.CallSuper;
-import android.view.View;
import com.android.launcher3.DropTarget;
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 7806c98..022b3b8 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -16,31 +16,67 @@
package com.android.launcher3.dragndrop;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.FloatArrayEvaluator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.pm.LauncherActivityInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.animation.DynamicAnimation;
+import android.support.animation.SpringAnimation;
+import android.support.animation.SpringForce;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.ShortcutConfigActivityInfo;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.IconNormalizer;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.Arrays;
+import java.util.List;
-public class DragView extends View {
+public class DragView extends FrameLayout {
public static final int COLOR_CHANGE_DURATION = 120;
public static final int VIEW_ZOOM_DURATION = 150;
@@ -57,6 +93,7 @@
private Point mDragVisualizeOffset = null;
private Rect mDragRegion = null;
+ private final Launcher mLauncher;
private final DragLayer mDragLayer;
@Thunk final DragController mDragController;
private boolean mHasDrawn = false;
@@ -76,6 +113,18 @@
private int mAnimatedShiftX;
private int mAnimatedShiftY;
+ // Below variable only needed IF FeatureFlags.LAUNCHER3_SPRING_ICONS is {@code true}
+ private SpringAnimation mSpringX, mSpringY;
+ private ImageView mFgImageView, mBgImageView;
+ private Path mScaledMaskPath;
+ private Drawable mBadge;
+
+ // Following three values are fine tuned with motion ux designer
+ private final static int STIFFNESS = 4000;
+ private final static float DAMPENING_RATIO = 1f;
+ private final static int PARALLAX_MAX_IN_DP = 8;
+ private final int mDelta;
+
/**
* Construct the drag view.
* <p>
@@ -89,6 +138,7 @@
public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
final float initialScale, final float finalScaleDps) {
super(launcher);
+ mLauncher = launcher;
mDragLayer = launcher.getDragLayer();
mDragController = launcher.getDragController();
@@ -142,8 +192,210 @@
mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
mBlurSizeOutline = getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
-
setElevation(getResources().getDimension(R.dimen.drag_elevation));
+ setWillNotDraw(false);
+ mDelta = (int)(getResources().getDisplayMetrics().density * PARALLAX_MAX_IN_DP);
+ }
+
+ /**
+ * Initialize {@code #mIconDrawable} only if the icon type is app icon (not shortcut or folder).
+ */
+ @TargetApi(Build.VERSION_CODES.O)
+ public void setItemInfo(final ItemInfo info) {
+ if (!(FeatureFlags.LAUNCHER3_SPRING_ICONS && Utilities.isAtLeastO())) {
+ return;
+ }
+ if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
+ info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ return;
+ }
+ // Load the adaptive icon on a background thread and add the view in ui thread.
+ final Looper workerLooper = LauncherModel.getWorkerLooper();
+ new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ LauncherAppState appState = LauncherAppState.getInstance(mLauncher);
+ Object[] outObj = new Object[1];
+ Drawable dr = getFullDrawable(info, appState, outObj);
+
+ if (dr instanceof AdaptiveIconDrawable) {
+ int w = mBitmap.getWidth();
+ int h = mBitmap.getHeight();
+ AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) dr;
+ adaptiveIcon.setBounds(0, 0, w, h);
+ float blurSizeOutline = mLauncher.getResources()
+ .getDimension(R.dimen.blur_size_medium_outline);
+ float normalizationScale = IconNormalizer.getInstance(mLauncher)
+ .getScale(adaptiveIcon, null, null, null) * ((w - blurSizeOutline) / w);
+
+ final Path mask = getMaskPath(adaptiveIcon, normalizationScale);
+ mFgImageView = setupImageView(adaptiveIcon.getForeground(), normalizationScale);
+ mBgImageView = setupImageView(adaptiveIcon.getBackground(), normalizationScale);
+ mSpringX = setupSpringAnimation(-w/4, w/4, DynamicAnimation.TRANSLATION_X);
+ mSpringY = setupSpringAnimation(-h/4, h/4, DynamicAnimation.TRANSLATION_Y);
+
+ mBadge = getBadge(info, appState, outObj[0]);
+ int blurMargin = (int) blurSizeOutline / 2;
+ mBadge.setBounds(blurMargin, blurMargin, w - blurMargin, h - blurMargin);
+
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ // Assign the variable on the UI thread to avoid race conditions.
+ mScaledMaskPath = mask;
+ addView(mBgImageView);
+ addView(mFgImageView);
+ setWillNotDraw(true);
+
+ if (info.isDisabled()) {
+ FastBitmapDrawable d = new FastBitmapDrawable(null);
+ d.setIsDisabled(true);
+ ColorFilter cf = d.getColorFilter();
+ mBgImageView.setColorFilter(cf);
+ mFgImageView.setColorFilter(cf);
+ mBadge.setColorFilter(cf);
+ }
+ }
+ });
+ }
+ }});
+ }
+
+ /**
+ * Returns the full drawable for {@param info}.
+ * @param outObj this is set to the internal data associated with {@param info},
+ * eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
+ */
+ private Drawable getFullDrawable(ItemInfo info, LauncherAppState appState, Object[] outObj) {
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(mLauncher)
+ .resolveActivity(info.getIntent(), info.user);
+ outObj[0] = activityInfo;
+ return (activityInfo != null) ? appState.getIconCache()
+ .getFullResIcon(activityInfo, false) : null;
+ } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (info instanceof PendingAddShortcutInfo) {
+ ShortcutConfigActivityInfo activityInfo =
+ ((PendingAddShortcutInfo) info).activityInfo;
+ outObj[0] = activityInfo;
+ return activityInfo.getFullResIcon(appState.getIconCache());
+ }
+ ShortcutKey key = ShortcutKey.fromItemInfo(info);
+ DeepShortcutManager sm = DeepShortcutManager.getInstance(mLauncher);
+ List<ShortcutInfoCompat> si = sm.queryForFullDetails(
+ key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
+ if (si.isEmpty()) {
+ return null;
+ } else {
+ outObj[0] = si.get(0);
+ return sm.getShortcutIconDrawable(si.get(0),
+ appState.getInvariantDeviceProfile().fillResIconDpi);
+ }
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * For apps icons and shortcut icons that have badges, this method creates a drawable that can
+ * later on be rendered on top of the layers for the badges. For app icons, work profile badges
+ * can only be applied. For deep shortcuts, when dragged from the pop up container, there's no
+ * badge. When dragged from workspace or folder, it may contain app AND/OR work profile badge
+ **/
+
+ @TargetApi(Build.VERSION_CODES.O)
+ private Drawable getBadge(ItemInfo info, LauncherAppState appState, Object obj) {
+ int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
+ if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ if (info.id == ItemInfo.NO_ID || !(obj instanceof ShortcutInfoCompat)) {
+ // The item is not yet added on home screen.
+ return new FixedSizeEmptyDrawable(iconSize);
+ }
+ ShortcutInfoCompat si = (ShortcutInfoCompat) obj;
+ Bitmap badge = LauncherIcons.getShortcutInfoBadge(si, appState.getIconCache());
+
+ float badgeSize = mLauncher.getResources().getDimension(R.dimen.profile_badge_size);
+ float insetFraction = (iconSize - badgeSize) / iconSize;
+ return new InsetDrawable(new FastBitmapDrawable(badge),
+ insetFraction, insetFraction, 0, 0);
+ } else {
+ return mLauncher.getPackageManager()
+ .getUserBadgedIcon(new FixedSizeEmptyDrawable(iconSize), info.user);
+ }
+ }
+
+ private ImageView setupImageView(Drawable drawable, float normalizationScale) {
+ FrameLayout.LayoutParams params = new LayoutParams(MATCH_PARENT, MATCH_PARENT);
+ ImageView imageViewOut = new ImageView(getContext());
+ imageViewOut.setLayoutParams(params);
+ imageViewOut.setScaleType(ImageView.ScaleType.FIT_XY);
+ imageViewOut.setScaleX(normalizationScale);
+ imageViewOut.setScaleY(normalizationScale);
+ imageViewOut.setImageDrawable(drawable);
+ return imageViewOut;
+ }
+
+ private SpringAnimation setupSpringAnimation(int minValue, int maxValue,
+ DynamicAnimation.ViewProperty property) {
+ SpringAnimation s = new SpringAnimation(mFgImageView, property, 0);
+ s.setMinValue(minValue).setMaxValue(maxValue);
+ s.setSpring(new SpringForce(0)
+ .setDampingRatio(DAMPENING_RATIO)
+ .setStiffness(STIFFNESS));
+ return s;
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ private Path getMaskPath(AdaptiveIconDrawable dr, float normalizationScale) {
+ Matrix m = new Matrix();
+ // Shrink very tiny bit so that the clip path is smaller than the original bitmap
+ // that has anti aliased edges and shadows.
+ float s = normalizationScale * .97f;
+ m.setScale(s, s, dr.getBounds().centerX(), dr.getBounds().centerY());
+ Path p = new Path();
+ dr.getIconMask().transform(m, p);
+ return p;
+ }
+
+ private void applySpring(int x, int y) {
+ if (mSpringX == null || mSpringY == null) {
+ return;
+ }
+ mSpringX.animateToFinalPosition(Utilities.boundToRange(x, -mDelta, mDelta));
+ mSpringY.animateToFinalPosition(Utilities.boundToRange(y, -mDelta, mDelta));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ int w = right - left;
+ int h = bottom - top;
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).layout(-w / 4, -h / 4, w + w / 4, h + h / 4);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int w = mBitmap.getWidth();
+ int h = mBitmap.getHeight();
+ setMeasuredDimension(w, h);
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).measure(w, h);
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ if (mScaledMaskPath != null) {
+ int cnt = canvas.save();
+ canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
+ canvas.clipPath(mScaledMaskPath);
+ super.dispatchDraw(canvas);
+ canvas.restoreToCount(cnt);
+ mBadge.draw(canvas);
+ } else {
+ super.dispatchDraw(canvas);
+ }
}
/** Sets the scale of the view over the normal workspace icon size. */
@@ -187,11 +439,6 @@
return mDragRegion;
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
- }
-
// Draws drag shadow for system DND.
@SuppressLint("WrongCall")
public void drawDragShadow(Canvas canvas) {
@@ -343,6 +590,9 @@
* @param touchY the y coordinate the user touched in DragLayer coordinates
*/
public void move(int touchX, int touchY) {
+ if (touchX > 0 && touchY > 0 && mLastTouchX > 0 && mLastTouchY > 0) {
+ applySpring(mLastTouchX - touchX, mLastTouchY - touchY);
+ }
mLastTouchX = touchX;
mLastTouchY = touchY;
applyTranslation();
@@ -391,4 +641,24 @@
public float getInitialScale() {
return mInitialScale;
}
+
+ private static class FixedSizeEmptyDrawable extends ColorDrawable {
+
+ private final int mSize;
+
+ public FixedSizeEmptyDrawable(int size) {
+ super(Color.TRANSPARENT);
+ mSize = size;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mSize;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mSize;
+ }
+ }
}
diff --git a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
index 36a0292..e36f607 100644
--- a/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
+++ b/src/com/android/launcher3/dragndrop/LivePreviewWidgetCell.java
@@ -4,7 +4,6 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.RemoteViews;
diff --git a/src/com/android/launcher3/dragndrop/PinItemDragListener.java b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
index df0c47c..c8d3890 100644
--- a/src/com/android/launcher3/dragndrop/PinItemDragListener.java
+++ b/src/com/android/launcher3/dragndrop/PinItemDragListener.java
@@ -16,87 +16,51 @@
package com.android.launcher3.dragndrop;
+import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
-import android.content.ClipDescription;
import android.content.Intent;
-import android.graphics.Point;
+import android.content.pm.LauncherApps.PinItemRequest;
import android.graphics.Rect;
+import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemClock;
-import android.util.Log;
import android.view.DragEvent;
import android.view.View;
import android.widget.RemoteViews;
-import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DragSource;
-import com.android.launcher3.DropTarget;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.PendingAddItemInfo;
-import com.android.launcher3.R;
-import com.android.launcher3.compat.PinItemRequestCompat;
-import com.android.launcher3.folder.Folder;
+import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.PendingItemDragHelper;
import com.android.launcher3.widget.WidgetAddFlowHandler;
-import java.util.UUID;
-
/**
* {@link DragSource} for handling drop from a different window. This object is initialized
* in the source window and is passed on to the Launcher activity as an Intent extra.
*/
-public class PinItemDragListener
- implements Parcelable, View.OnDragListener, DragSource, DragOptions.PreDragCondition {
+@TargetApi(Build.VERSION_CODES.O)
+public class PinItemDragListener extends BaseItemDragListener implements Parcelable {
- private static final String TAG = "PinItemDragListener";
-
- private static final String MIME_TYPE_PREFIX = "com.android.launcher3.drag_and_drop/";
public static final String EXTRA_PIN_ITEM_DRAG_LISTENER = "pin_item_drag_listener";
- private final PinItemRequestCompat mRequest;
+ private final PinItemRequest mRequest;
- // Position of preview relative to the touch location
- private final Rect mPreviewRect;
-
- private final int mPreviewBitmapWidth;
- private final int mPreviewViewWidth;
-
- // Randomly generated id used to verify the drag event.
- private final String mId;
-
- private Launcher mLauncher;
- private DragController mDragController;
- private long mDragStartTime;
-
- public PinItemDragListener(PinItemRequestCompat request, Rect previewRect,
+ public PinItemDragListener(PinItemRequest request, Rect previewRect,
int previewBitmapWidth, int previewViewWidth) {
+ super(previewRect, previewBitmapWidth, previewViewWidth);
mRequest = request;
- mPreviewRect = previewRect;
- mPreviewBitmapWidth = previewBitmapWidth;
- mPreviewViewWidth = previewViewWidth;
- mId = UUID.randomUUID().toString();
}
private PinItemDragListener(Parcel parcel) {
- mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
- mPreviewRect = Rect.CREATOR.createFromParcel(parcel);
- mPreviewBitmapWidth = parcel.readInt();
- mPreviewViewWidth = parcel.readInt();
- mId = parcel.readString();
- }
-
- public String getMimeType() {
- return MIME_TYPE_PREFIX + mId;
+ super(parcel);
+ mRequest = PinItemRequest.CREATOR.createFromParcel(parcel);
}
@Override
@@ -106,47 +70,22 @@
@Override
public void writeToParcel(Parcel parcel, int i) {
+ super.writeToParcel(parcel, i);
mRequest.writeToParcel(parcel, i);
- mPreviewRect.writeToParcel(parcel, i);
- parcel.writeInt(mPreviewBitmapWidth);
- parcel.writeInt(mPreviewViewWidth);
- parcel.writeString(mId);
- }
-
- public void setLauncher(Launcher launcher) {
- mLauncher = launcher;
- mDragController = launcher.getDragController();
}
@Override
- public boolean onDrag(View view, DragEvent event) {
- if (mLauncher == null || mDragController == null) {
- postCleanup();
- return false;
- }
- if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
- if (onDragStart(event)) {
- return true;
- } else {
- postCleanup();
- return false;
- }
- }
- return mDragController.onDragEvent(mDragStartTime, event);
- }
-
- private boolean onDragStart(DragEvent event) {
+ protected boolean onDragStart(DragEvent event) {
if (!mRequest.isValid()) {
return false;
}
- ClipDescription desc = event.getClipDescription();
- if (desc == null || !desc.hasMimeType(getMimeType())) {
- Log.e(TAG, "Someone started a dragAndDrop before us.");
- return false;
- }
+ return super.onDragStart(event);
+ }
+ @Override
+ protected PendingItemDragHelper createDragHelper() {
final PendingAddItemInfo item;
- if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_SHORTCUT) {
+ if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_SHORTCUT) {
item = new PendingAddShortcutInfo(
new PinShortcutRequestActivityInfo(mRequest, mLauncher));
} else {
@@ -166,112 +105,20 @@
View view = new View(mLauncher);
view.setTag(item);
- Point downPos = new Point((int) event.getX(), (int) event.getY());
- DragOptions options = new DragOptions();
- options.systemDndStartPoint = downPos;
- options.preDragCondition = this;
-
- // We use drag event position as the screenPos for the preview image. Since mPreviewRect
- // already includes the view position relative to the drag event on the source window,
- // and the absolute position (position relative to the screen) of drag event is same
- // across windows, using drag position here give a good estimate for relative position
- // to source window.
PendingItemDragHelper dragHelper = new PendingItemDragHelper(view);
- if (mRequest.getRequestType() == PinItemRequestCompat.REQUEST_TYPE_APPWIDGET) {
+ if (mRequest.getRequestType() == PinItemRequest.REQUEST_TYPE_APPWIDGET) {
dragHelper.setPreview(getPreview(mRequest));
}
-
- dragHelper.startDrag(new Rect(mPreviewRect),
- mPreviewBitmapWidth, mPreviewViewWidth, downPos, this, options);
- mDragStartTime = SystemClock.uptimeMillis();
- return true;
- }
-
- @Override
- public boolean shouldStartDrag(double distanceDragged) {
- // Stay in pre-drag mode, if workspace is locked.
- return !mLauncher.isWorkspaceLocked();
- }
-
- @Override
- public void onPreDragStart(DropTarget.DragObject dragObject) {
- // The predrag starts when the workspace is not yet loaded. In some cases we set
- // the dragLayer alpha to 0 to have a nice fade-in animation. But that will prevent the
- // dragView from being visible. Instead just skip the fade-in animation here.
- mLauncher.getDragLayer().setAlpha(1);
-
- dragObject.dragView.setColor(
- mLauncher.getResources().getColor(R.color.delete_target_hover_tint));
- }
-
- @Override
- public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
- if (dragStarted) {
- dragObject.dragView.setColor(0);
- }
- }
-
- @Override
- public boolean supportsAppInfoDropTarget() {
- return false;
- }
-
- @Override
- public boolean supportsDeleteDropTarget() {
- return false;
- }
-
- @Override
- public float getIntrinsicIconScaleFactor() {
- return 1f;
- }
-
- @Override
- public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
- boolean success) {
- if (isFlingToDelete || !success || (target != mLauncher.getWorkspace() &&
- !(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
- // Exit spring loaded mode if we have not successfully dropped or have not handled the
- // drop in Workspace
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
-
- if (!success) {
- d.deferDragViewCleanupPostAnimation = false;
- }
- postCleanup();
+ return dragHelper;
}
@Override
public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
LauncherLogProto.Target targetParent) {
- targetParent.containerType = ContainerType.PINITEM;
+ targetParent.containerType = LauncherLogProto.ContainerType.PINITEM;
}
- private void postCleanup() {
- if (mLauncher != null) {
- // Remove any drag params from the launcher intent since the drag operation is complete.
- Intent newIntent = new Intent(mLauncher.getIntent());
- newIntent.removeExtra(EXTRA_PIN_ITEM_DRAG_LISTENER);
- mLauncher.setIntent(newIntent);
- }
-
- new Handler(Looper.getMainLooper()).post(new Runnable() {
- @Override
- public void run() {
- removeListener();
- }
- });
- }
-
- public void removeListener() {
- if (mLauncher != null) {
- mLauncher.getDragLayer().setOnDragListener(null);
- }
- }
-
- public static RemoteViews getPreview(PinItemRequestCompat request) {
+ public static RemoteViews getPreview(PinItemRequest request) {
Bundle extras = request.getExtras();
if (extras != null &&
extras.get(AppWidgetManager.EXTRA_APPWIDGET_PREVIEW) instanceof RemoteViews) {
@@ -281,6 +128,9 @@
}
public static boolean handleDragRequest(Launcher launcher, Intent intent) {
+ if (!Utilities.isAtLeastO()) {
+ return false;
+ }
if (intent == null || !Intent.ACTION_MAIN.equals(intent.getAction())) {
return false;
}
diff --git a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
index bb5ac5b..52abbc7 100644
--- a/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
+++ b/src/com/android/launcher3/dragndrop/PinShortcutRequestActivityInfo.java
@@ -21,6 +21,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.PinItemRequest;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -30,26 +31,25 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.PinItemRequestCompat;
+import com.android.launcher3.compat.LauncherAppsCompatVO;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
/**
* Extension of ShortcutConfigActivityInfo to be used in the confirmation prompt for pin item
* request.
*/
-@TargetApi(Build.VERSION_CODES.N_MR1)
+@TargetApi(Build.VERSION_CODES.O)
class PinShortcutRequestActivityInfo extends ShortcutConfigActivityInfo {
// Class name used in the target component, such that it will never represent an
// actual existing class.
private static final String DUMMY_COMPONENT_CLASS = "pinned-shortcut";
- private final PinItemRequestCompat mRequest;
+ private final PinItemRequest mRequest;
private final ShortcutInfo mInfo;
private final Context mContext;
- public PinShortcutRequestActivityInfo(PinItemRequestCompat request, Context context) {
+ public PinShortcutRequestActivityInfo(PinItemRequest request, Context context) {
super(new ComponentName(request.getShortcutInfo().getPackage(), DUMMY_COMPONENT_CLASS),
request.getShortcutInfo().getUserHandle());
mRequest = request;
@@ -80,7 +80,7 @@
Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT +
mContext.getResources().getInteger(R.integer.config_overlayTransitionTime) / 2;
// Delay the actual accept() call until the drop animation is complete.
- return LauncherAppsCompat.createShortcutInfoFromPinItemRequest(
+ return LauncherAppsCompatVO.createShortcutInfoFromPinItemRequest(
mContext, mRequest, duration);
}
diff --git a/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java b/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
index b6da6ad..9f617e4 100644
--- a/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
+++ b/src/com/android/launcher3/dragndrop/PinWidgetFlowHandler.java
@@ -16,15 +16,17 @@
package com.android.launcher3.dragndrop;
+import android.annotation.TargetApi;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.pm.LauncherApps.PinItemRequest;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
-import com.android.launcher3.compat.PinItemRequestCompat;
import com.android.launcher3.widget.WidgetAddFlowHandler;
/**
@@ -33,18 +35,19 @@
* No config activity is shown even if it is defined in widget config. And a callback is sent when
* the widget is bound.
*/
+@TargetApi(Build.VERSION_CODES.O)
public class PinWidgetFlowHandler extends WidgetAddFlowHandler implements Parcelable {
- private final PinItemRequestCompat mRequest;
+ private final PinItemRequest mRequest;
- public PinWidgetFlowHandler(AppWidgetProviderInfo providerInfo, PinItemRequestCompat request) {
+ public PinWidgetFlowHandler(AppWidgetProviderInfo providerInfo, PinItemRequest request) {
super(providerInfo);
mRequest = request;
}
protected PinWidgetFlowHandler(Parcel parcel) {
super(parcel);
- mRequest = PinItemRequestCompat.CREATOR.createFromParcel(parcel);
+ mRequest = PinItemRequest.CREATOR.createFromParcel(parcel);
}
@Override
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
new file mode 100644
index 0000000..de614f0
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionAlgorithm.java
@@ -0,0 +1,775 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dynamicui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Range;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.WallpaperColorsCompat;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Implementation of tonal color extraction
+ **/
+public class ColorExtractionAlgorithm {
+
+ public static ColorExtractionAlgorithm newInstance(Context context) {
+ return Utilities.getOverrideObject(ColorExtractionAlgorithm.class,
+ context.getApplicationContext(), R.string.color_extraction_impl_class);
+ }
+
+ private static final String TAG = "Tonal";
+
+ // Used for tonal palette fitting
+ private static final float FIT_WEIGHT_H = 1.0f;
+ private static final float FIT_WEIGHT_S = 1.0f;
+ private static final float FIT_WEIGHT_L = 10.0f;
+
+ // Temporary variable to avoid allocations
+ private float[] mTmpHSL = new float[3];
+
+ public @Nullable Pair<Integer, Integer> extractInto(WallpaperColorsCompat inWallpaperColors) {
+ if (inWallpaperColors == null) {
+ return null;
+ }
+
+ final List<Integer> mainColors = getMainColors(inWallpaperColors);
+ final int mainColorsSize = mainColors.size();
+
+ if (mainColorsSize == 0) {
+ return null;
+ }
+ // Tonal is not really a sort, it takes a color from the extracted
+ // palette and finds a best fit amongst a collection of pre-defined
+ // palettes. The best fit is tweaked to be closer to the source color
+ // and replaces the original palette
+
+ // Get the most preeminent, non-blacklisted color.
+ Integer bestColor = 0;
+ final float[] hsl = new float[3];
+ for (int i = 0; i < mainColorsSize; i++) {
+ final int colorValue = mainColors.get(i);
+ ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue),
+ Color.blue(colorValue), hsl);
+
+ // Stop when we find a color that meets our criteria
+ if (!isBlacklisted(hsl)) {
+ bestColor = colorValue;
+ break;
+ }
+ }
+
+ // Fail if not found
+ if (bestColor == null) {
+ return null;
+ }
+
+ int colorValue = bestColor;
+ ColorUtils.RGBToHSL(Color.red(colorValue), Color.green(colorValue), Color.blue(colorValue),
+ hsl);
+
+ // The Android HSL definition requires the hue to go from 0 to 360 but
+ // the Material Tonal Palette defines hues from 0 to 1.
+ hsl[0] /= 360f;
+
+ // Find the palette that contains the closest color
+ TonalPalette palette = findTonalPalette(hsl[0], hsl[1]);
+ if (palette == null) {
+ Log.w(TAG, "Could not find a tonal palette!");
+ return null;
+ }
+
+ // Figure out what's the main color index in the optimal palette
+ int fitIndex = bestFit(palette, hsl[0], hsl[1], hsl[2]);
+ if (fitIndex == -1) {
+ Log.w(TAG, "Could not find best fit!");
+ return null;
+ }
+
+ // Generate the 10 colors palette by offsetting each one of them
+ float[] h = fit(palette.h, hsl[0], fitIndex,
+ Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
+ float[] s = fit(palette.s, hsl[1], fitIndex, 0.0f, 1.0f);
+ float[] l = fit(palette.l, hsl[2], fitIndex, 0.0f, 1.0f);
+
+ final int textInversionIndex = h.length - 3;
+
+ int primaryIndex;
+ int secondaryIndex;
+
+ // Dark colors:
+ // Stops at 4th color, only lighter if dark text is supported
+ if (fitIndex < 2) {
+ primaryIndex = 0;
+ } else if (fitIndex < textInversionIndex) {
+ primaryIndex = Math.min(fitIndex, 3);
+ } else {
+ primaryIndex = h.length - 1;
+ }
+ secondaryIndex = primaryIndex + (primaryIndex >= 2 ? -2 : 2);
+
+ int mainColor = getColorInt(primaryIndex, h, s, l);
+ int secondaryColor = getColorInt(secondaryIndex, h, s, l);
+
+ return new Pair<>(mainColor, secondaryColor);
+ }
+
+ private int getColorInt(int fitIndex, float[] h, float[] s, float[] l) {
+ mTmpHSL[0] = fract(h[fitIndex]) * 360.0f;
+ mTmpHSL[1] = s[fitIndex];
+ mTmpHSL[2] = l[fitIndex];
+ return ColorUtils.HSLToColor(mTmpHSL);
+ }
+
+ /**
+ * Checks if a given color exists in the blacklist
+ * @param hsl float array with 3 components (H 0..360, S 0..1 and L 0..1)
+ * @return true if color should be avoided
+ */
+ private boolean isBlacklisted(float[] hsl) {
+ for (ColorRange badRange: BLACKLISTED_COLORS) {
+ if (badRange.containsColor(hsl[0], hsl[1], hsl[2])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Offsets all colors by a delta, clamping values that go beyond what's
+ * supported on the color space.
+ * @param data what you want to fit
+ * @param v how big should be the offset
+ * @param index which index to calculate the delta against
+ * @param min minimum accepted value (clamp)
+ * @param max maximum accepted value (clamp)
+ * @return new shifted palette
+ */
+ private static float[] fit(float[] data, float v, int index, float min, float max) {
+ float[] fitData = new float[data.length];
+ float delta = v - data[index];
+
+ for (int i = 0; i < data.length; i++) {
+ fitData[i] = Utilities.boundToRange(data[i] + delta, min, max);
+ }
+
+ return fitData;
+ }
+
+ /**
+ * Finds the closest color in a palette, given another HSL color
+ *
+ * @param palette where to search
+ * @param h hue
+ * @param s saturation
+ * @param l lightness
+ * @return closest index or -1 if palette is empty.
+ */
+ private static int bestFit(@NonNull TonalPalette palette, float h, float s, float l) {
+ int minErrorIndex = -1;
+ float minError = Float.POSITIVE_INFINITY;
+
+ for (int i = 0; i < palette.h.length; i++) {
+ float error =
+ FIT_WEIGHT_H * Math.abs(h - palette.h[i])
+ + FIT_WEIGHT_S * Math.abs(s - palette.s[i])
+ + FIT_WEIGHT_L * Math.abs(l - palette.l[i]);
+ if (error < minError) {
+ minError = error;
+ minErrorIndex = i;
+ }
+ }
+
+ return minErrorIndex;
+ }
+
+ @Nullable
+ private static TonalPalette findTonalPalette(float h, float s) {
+ // Fallback to a grey palette if the color is too desaturated.
+ // This avoids hue shifts.
+ if (s < 0.05f) {
+ return GREY_PALETTE;
+ }
+
+ TonalPalette best = null;
+ float error = Float.POSITIVE_INFINITY;
+
+ for (int i = 0; i < TONAL_PALETTES.length; i++) {
+ final TonalPalette candidate = TONAL_PALETTES[i];
+
+ if (h >= candidate.minHue && h <= candidate.maxHue) {
+ best = candidate;
+ break;
+ }
+
+ if (candidate.maxHue > 1.0f && h >= 0.0f && h <= fract(candidate.maxHue)) {
+ best = candidate;
+ break;
+ }
+
+ if (candidate.minHue < 0.0f && h >= fract(candidate.minHue) && h <= 1.0f) {
+ best = candidate;
+ break;
+ }
+
+ if (h <= candidate.minHue && candidate.minHue - h < error) {
+ best = candidate;
+ error = candidate.minHue - h;
+ } else if (h >= candidate.maxHue && h - candidate.maxHue < error) {
+ best = candidate;
+ error = h - candidate.maxHue;
+ } else if (candidate.maxHue > 1.0f && h >= fract(candidate.maxHue)
+ && h - fract(candidate.maxHue) < error) {
+ best = candidate;
+ error = h - fract(candidate.maxHue);
+ } else if (candidate.minHue < 0.0f && h <= fract(candidate.minHue)
+ && fract(candidate.minHue) - h < error) {
+ best = candidate;
+ error = fract(candidate.minHue) - h;
+ }
+ }
+
+ return best;
+ }
+
+ private static float fract(float v) {
+ return v - (float) Math.floor(v);
+ }
+
+ static class TonalPalette {
+ final float[] h;
+ final float[] s;
+ final float[] l;
+ final float minHue;
+ final float maxHue;
+
+ TonalPalette(float[] h, float[] s, float[] l) {
+ if (h.length != s.length || s.length != l.length) {
+ throw new IllegalArgumentException("All arrays should have the same size. h: "
+ + Arrays.toString(h) + " s: " + Arrays.toString(s) + " l: "
+ + Arrays.toString(l));
+ }
+
+ this.h = h;
+ this.s = s;
+ this.l = l;
+
+ float minHue = Float.POSITIVE_INFINITY;
+ float maxHue = Float.NEGATIVE_INFINITY;
+
+ for (float v : h) {
+ minHue = Math.min(v, minHue);
+ maxHue = Math.max(v, maxHue);
+ }
+
+ this.minHue = minHue;
+ this.maxHue = maxHue;
+ }
+ }
+
+ // Data definition of Material Design tonal palettes
+ // When the sort type is set to TONAL, these palettes are used to find
+ // a best fit. Each palette is defined as 22 HSL colors
+ private static final TonalPalette[] TONAL_PALETTES = {
+ new TonalPalette(
+ new float[] {1f, 1f, 0.991f, 0.991f, 0.9833333333333333f, 0f, 0f, 0f,
+ 0.01134380453752181f, 0.015625000000000003f, 0.024193548387096798f,
+ 0.027397260273972573f, 0.017543859649122865f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8434782608695652f, 1f, 1f, 1f, 1f,
+ 1f},
+ new float[] {0.04f, 0.09f, 0.14f, 0.2f, 0.27450980392156865f,
+ 0.34901960784313724f, 0.4235294117647059f, 0.5490196078431373f,
+ 0.6254901960784314f, 0.6862745098039216f, 0.7568627450980392f,
+ 0.8568627450980393f, 0.9254901960784314f}
+ ),
+ new TonalPalette(
+ new float[] {0.638f, 0.638f, 0.6385767790262171f, 0.6301169590643275f,
+ 0.6223958333333334f, 0.6151079136690647f, 0.6065400843881856f,
+ 0.5986964618249534f, 0.5910746812386157f, 0.5833333333333334f,
+ 0.5748031496062993f, 0.5582010582010583f},
+ new float[] {1f, 1f, 1f, 1f, 0.9014084507042253f, 0.8128654970760234f,
+ 0.7979797979797981f, 0.7816593886462883f, 0.778723404255319f, 1f, 1f,
+ 1f},
+ new float[] {0.05f, 0.12f, 0.17450980392156862f, 0.2235294117647059f,
+ 0.2784313725490196f, 0.3352941176470588f, 0.388235294117647f,
+ 0.44901960784313727f, 0.5392156862745098f, 0.6509803921568628f,
+ 0.7509803921568627f, 0.8764705882352941f}
+ ),
+ new TonalPalette(
+ new float[] {0.563f, 0.569f, 0.5666f, 0.5669934640522876f, 0.5748031496062993f,
+ 0.5595238095238095f, 0.5473118279569893f, 0.5393258426966292f,
+ 0.5315955766192734f, 0.524031007751938f, 0.5154711673699016f,
+ 0.508080808080808f, 0.5f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0.8847736625514403f, 1f, 1f,
+ 1f},
+ new float[] {0.07f, 0.12f, 0.16f, 0.2f, 0.24901960784313726f,
+ 0.27450980392156865f, 0.30392156862745096f, 0.34901960784313724f,
+ 0.4137254901960784f, 0.47647058823529415f, 0.5352941176470588f,
+ 0.6764705882352942f, 0.8f}
+ ),
+ new TonalPalette(
+ new float[] {0.508f, 0.511f, 0.508f, 0.508f, 0.5082304526748972f,
+ 0.5069444444444444f, 0.5f, 0.5f, 0.5f, 0.48724954462659376f,
+ 0.4800347222222222f, 0.4755134281200632f, 0.4724409448818897f,
+ 0.4671052631578947f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 0.8888888888888887f, 0.9242424242424242f, 1f,
+ 1f, 0.8133333333333332f, 0.7868852459016393f, 1f, 1f, 1f},
+ new float[] {0.04f, 0.06f, 0.08f, 0.12f, 0.1588235294117647f,
+ 0.21176470588235297f, 0.25882352941176473f, 0.3f, 0.34901960784313724f,
+ 0.44117647058823534f, 0.5215686274509804f, 0.5862745098039216f,
+ 0.7509803921568627f, 0.8509803921568627f}
+ ),
+ new TonalPalette(
+ new float[] {0.333f, 0.333f, 0.333f, 0.3333333333333333f, 0.3333333333333333f,
+ 0.34006734006734f, 0.34006734006734f, 0.34006734006734f,
+ 0.34259259259259256f, 0.3475783475783476f, 0.34767025089605735f,
+ 0.3467741935483871f, 0.3703703703703704f},
+ new float[] {0.70f, 0.72f, 0.69f, 0.6703296703296703f, 0.728813559322034f,
+ 0.5657142857142856f, 0.5076923076923077f, 0.3944223107569721f,
+ 0.6206896551724138f, 0.8931297709923666f, 1f, 1f, 1f},
+ new float[] {0.05f, 0.08f, 0.14f, 0.1784313725490196f, 0.23137254901960785f,
+ 0.3431372549019608f, 0.38235294117647056f, 0.49215686274509807f,
+ 0.6588235294117647f, 0.7431372549019608f, 0.8176470588235294f,
+ 0.8784313725490196f, 0.9294117647058824f}
+ ),
+ new TonalPalette(
+ new float[] {0.161f, 0.163f, 0.163f, 0.162280701754386f, 0.15032679738562088f,
+ 0.15879265091863518f, 0.16236559139784948f, 0.17443868739205526f,
+ 0.17824074074074076f, 0.18674698795180725f, 0.18692449355432778f,
+ 0.1946778711484594f, 0.18604651162790695f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[] {0.05f, 0.08f, 0.11f, 0.14901960784313725f, 0.2f,
+ 0.24901960784313726f, 0.30392156862745096f, 0.3784313725490196f,
+ 0.4235294117647059f, 0.48823529411764705f, 0.6450980392156863f,
+ 0.7666666666666666f, 0.8313725490196078f}
+ ),
+ new TonalPalette(
+ new float[] {0.108f, 0.105f, 0.105f, 0.105f, 0.10619469026548674f,
+ 0.11924686192468618f, 0.13046448087431692f, 0.14248366013071895f,
+ 0.1506024096385542f, 0.16220238095238093f, 0.16666666666666666f,
+ 0.16666666666666666f, 0.162280701754386f, 0.15686274509803924f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[] {0.17f, 0.22f, 0.28f, 0.35f, 0.44313725490196076f,
+ 0.46862745098039216f, 0.47843137254901963f, 0.5f, 0.5117647058823529f,
+ 0.5607843137254902f, 0.6509803921568628f, 0.7509803921568627f,
+ 0.8509803921568627f, 0.9f}
+ ),
+ new TonalPalette(
+ new float[] {0.036f, 0.036f, 0.036f, 0.036f, 0.03561253561253561f,
+ 0.05098039215686275f, 0.07516339869281045f, 0.09477124183006536f,
+ 0.1150326797385621f, 0.134640522875817f, 0.14640522875816991f,
+ 0.1582397003745319f, 0.15773809523809523f, 0.15359477124183002f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[] {0.19f, 0.26f, 0.34f, 0.39f, 0.4588235294117647f, 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5f, 0.5f, 0.6509803921568628f, 0.7803921568627451f, 0.9f}
+ ),
+ new TonalPalette(
+ new float[] {0.955f, 0.961f, 0.958f, 0.9596491228070175f, 0.9593837535014005f,
+ 0.9514767932489452f, 0.943859649122807f, 0.9396825396825397f,
+ 0.9395424836601307f, 0.9393939393939394f, 0.9362745098039216f,
+ 0.9754098360655739f, 0.9824561403508771f},
+ new float[] {0.87f, 0.85f, 0.85f, 0.84070796460177f, 0.8206896551724138f,
+ 0.7979797979797981f, 0.7661290322580644f, 0.9051724137931036f,
+ 1f, 1f, 1f, 1f, 1f},
+ new float[] {0.06f, 0.11f, 0.16f, 0.22156862745098038f, 0.2843137254901961f,
+ 0.388235294117647f, 0.48627450980392156f, 0.5450980392156863f,
+ 0.6f, 0.6764705882352942f, 0.8f, 0.8803921568627451f,
+ 0.9254901960784314f}
+ ),
+ new TonalPalette(
+ new float[] {0.866f, 0.855f, 0.841025641025641f, 0.8333333333333334f,
+ 0.8285256410256411f, 0.821522309711286f, 0.8083333333333333f,
+ 0.8046594982078853f, 0.8005822416302766f, 0.7842377260981912f,
+ 0.7771084337349398f, 0.7747747747747749f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
+ 0.737142857142857f, 0.6434108527131781f, 0.46835443037974644f},
+ new float[] {0.05f, 0.08f, 0.12745098039215685f, 0.15490196078431373f,
+ 0.20392156862745098f, 0.24901960784313726f, 0.3137254901960784f,
+ 0.36470588235294116f, 0.44901960784313727f, 0.6568627450980392f,
+ 0.7470588235294118f, 0.8450980392156863f}
+ ),
+ new TonalPalette(
+ new float[] {0.925f, 0.93f, 0.938f, 0.947f, 0.955952380952381f,
+ 0.9681069958847737f, 0.9760479041916167f, 0.9873563218390804f, 0f, 0f,
+ 0.009057971014492771f, 0.026748971193415648f,
+ 0.041666666666666616f, 0.05303030303030304f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 0.8350515463917526f, 0.6929460580912863f,
+ 0.6387665198237885f, 0.6914893617021276f, 0.7583892617449666f,
+ 0.8070175438596495f, 0.9310344827586209f, 1f, 1f},
+ new float[] {0.10f, 0.13f, 0.17f, 0.2f, 0.27450980392156865f,
+ 0.3803921568627451f, 0.4725490196078432f, 0.5549019607843138f,
+ 0.6313725490196078f, 0.707843137254902f, 0.7764705882352941f,
+ 0.8294117647058823f, 0.9058823529411765f, 0.9568627450980391f}
+ ),
+ new TonalPalette(
+ new float[] {0.733f, 0.736f, 0.744f, 0.7514619883040936f, 0.7679738562091503f,
+ 0.7802083333333333f, 0.7844311377245509f, 0.796875f,
+ 0.8165618448637316f, 0.8487179487179487f, 0.8582375478927203f,
+ 0.8562091503267975f, 0.8666666666666667f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 0.8163265306122449f, 0.6653386454183268f,
+ 0.7547169811320753f, 0.929824561403509f, 0.9558823529411766f,
+ 0.9560439560439562f, 1f, 1f},
+ new float[] {0.07f, 0.12f, 0.17f, 0.2235294117647059f, 0.3f,
+ 0.38431372549019605f, 0.492156862745098f, 0.5843137254901961f,
+ 0.6647058823529411f, 0.7333333333333334f, 0.8215686274509804f, 0.9f,
+ 0.9411764705882353f}
+ ),
+ new TonalPalette(
+ new float[] {0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+ 0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+ 0.6666666666666666f, 0.6666666666666666f, 0.6666666666666666f,
+ 0.6666666666666666f, 0.6666666666666666f},
+ new float[] {0.25f, 0.24590163934426232f, 0.17880794701986752f,
+ 0.14606741573033713f, 0.13761467889908252f, 0.14893617021276592f,
+ 0.16756756756756758f, 0.20312500000000017f, 0.26086956521739135f,
+ 0.29999999999999966f, 0.5000000000000004f},
+ new float[] {0.18f, 0.2392156862745098f, 0.296078431372549f,
+ 0.34901960784313724f, 0.4274509803921569f, 0.5392156862745098f,
+ 0.6372549019607843f, 0.7490196078431373f, 0.8196078431372549f,
+ 0.8823529411764706f, 0.9372549019607843f}
+ ),
+ new TonalPalette(
+ new float[] {0.938f, 0.944f, 0.952f, 0.961f, 0.9678571428571429f,
+ 0.9944812362030905f, 0f, 0f,
+ 0.0047348484848484815f, 0.00316455696202532f, 0f,
+ 0.9980392156862745f, 0.9814814814814816f, 0.9722222222222221f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 0.7023255813953488f, 0.6638655462184874f,
+ 0.6521739130434782f, 0.7719298245614035f, 0.8315789473684211f,
+ 0.6867469879518071f, 0.7264957264957265f, 0.8181818181818182f,
+ 0.8181818181818189f},
+ new float[] {0.08f, 0.13f, 0.18f, 0.23f, 0.27450980392156865f,
+ 0.4215686274509804f,
+ 0.4666666666666667f, 0.503921568627451f, 0.5529411764705883f,
+ 0.6274509803921569f, 0.6745098039215687f, 0.7705882352941176f,
+ 0.892156862745098f, 0.9568627450980391f}
+ ),
+ new TonalPalette(
+ new float[] {0.88f, 0.888f, 0.897f, 0.9052287581699346f, 0.9112021857923498f,
+ 0.9270152505446624f, 0.9343137254901961f, 0.9391534391534391f,
+ 0.9437984496124031f, 0.943661971830986f, 0.9438943894389439f,
+ 0.9426229508196722f, 0.9444444444444444f},
+ new float[] {1f, 1f, 1f, 1f, 0.8133333333333332f, 0.7927461139896375f,
+ 0.7798165137614679f, 0.7777777777777779f, 0.8190476190476191f,
+ 0.8255813953488372f, 0.8211382113821142f, 0.8133333333333336f,
+ 0.8000000000000006f},
+ new float[] {0.08f, 0.12f, 0.16f, 0.2f, 0.29411764705882354f,
+ 0.3784313725490196f, 0.42745098039215684f, 0.4764705882352941f,
+ 0.5882352941176471f, 0.6627450980392157f, 0.7588235294117647f,
+ 0.8529411764705882f, 0.9411764705882353f}
+ ),
+ new TonalPalette(
+ new float[] {0.669f, 0.680f, 0.6884057971014492f, 0.6974789915966387f,
+ 0.7079889807162534f, 0.7154471544715447f, 0.7217741935483872f,
+ 0.7274143302180687f, 0.7272727272727273f, 0.7258064516129031f,
+ 0.7252252252252251f, 0.7333333333333333f},
+ new float[] {0.81f, 0.81f, 0.8214285714285715f, 0.6878612716763006f,
+ 0.6080402010050251f, 0.5774647887323943f, 0.5391304347826086f,
+ 0.46724890829694316f, 0.4680851063829788f, 0.462686567164179f,
+ 0.45679012345678977f, 0.4545454545454551f},
+ new float[] {0.12f, 0.16f, 0.2196078431372549f, 0.33921568627450976f,
+ 0.39019607843137255f, 0.4176470588235294f, 0.45098039215686275f,
+ 0.5509803921568628f, 0.6313725490196078f, 0.7372549019607844f,
+ 0.8411764705882353f, 0.9352941176470588f}
+ ),
+ new TonalPalette(
+ new float[] {0.6470588235294118f, 0.6516666666666667f, 0.6464174454828661f,
+ 0.6441441441441442f, 0.6432748538011696f, 0.6416666666666667f,
+ 0.6402439024390243f, 0.6412429378531074f, 0.6435185185185186f,
+ 0.6428571428571429f},
+ new float[] {0.8095238095238095f, 0.6578947368421053f, 0.5721925133689839f,
+ 0.5362318840579711f, 0.5f, 0.4424778761061947f, 0.44086021505376327f,
+ 0.44360902255639095f, 0.4499999999999997f, 0.4375000000000006f},
+ new float[] {0.16470588235294117f, 0.2980392156862745f, 0.36666666666666664f,
+ 0.40588235294117647f, 0.44705882352941173f,
+ 0.5568627450980392f, 0.6352941176470588f, 0.7392156862745098f,
+ 0.8431372549019608f, 0.9372549019607843f}
+ ),
+ new TonalPalette(
+ new float[] {0.469f, 0.46732026143790845f, 0.4718614718614719f,
+ 0.4793650793650794f, 0.48071625344352614f, 0.4829683698296837f,
+ 0.484375f, 0.4841269841269842f, 0.48444444444444457f,
+ 0.48518518518518516f, 0.4907407407407408f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 0.6274509803921569f, 0.41832669322709176f,
+ 0.41899441340782106f, 0.4128440366972478f, 0.4090909090909088f},
+ new float[] {0.07f, 0.1f, 0.15098039215686274f, 0.20588235294117646f,
+ 0.2372549019607843f, 0.26862745098039215f, 0.4f, 0.5078431372549019f,
+ 0.6490196078431372f, 0.7862745098039216f, 0.9137254901960784f}
+ ),
+ new TonalPalette(
+ new float[] {0.542f, 0.5444444444444444f, 0.5555555555555556f,
+ 0.5555555555555556f, 0.553763440860215f, 0.5526315789473684f,
+ 0.5555555555555556f, 0.5555555555555555f, 0.5555555555555556f,
+ 0.5512820512820514f, 0.5666666666666667f},
+ new float[] {0.25f, 0.24590163934426232f, 0.19148936170212766f,
+ 0.1791044776119403f, 0.18343195266272191f, 0.18446601941747576f,
+ 0.1538461538461539f, 0.15625000000000003f, 0.15328467153284678f,
+ 0.15662650602409653f, 0.151515151515151f},
+ new float[] {0.05f, 0.1196078431372549f, 0.1843137254901961f,
+ 0.2627450980392157f,
+ 0.33137254901960783f, 0.403921568627451f, 0.5411764705882354f,
+ 0.6235294117647059f, 0.7313725490196079f, 0.8372549019607843f,
+ 0.9352941176470588f}
+ ),
+ new TonalPalette(
+ new float[] {0.022222222222222223f, 0.02469135802469136f, 0.031249999999999997f,
+ 0.03947368421052631f, 0.04166666666666668f,
+ 0.043650793650793655f, 0.04411764705882352f, 0.04166666666666652f,
+ 0.04444444444444459f, 0.05555555555555529f},
+ new float[] {0.33333333333333337f, 0.2783505154639175f, 0.2580645161290323f,
+ 0.25675675675675674f, 0.2528735632183908f, 0.17500000000000002f,
+ 0.15315315315315312f, 0.15189873417721522f,
+ 0.15789473684210534f, 0.15789473684210542f},
+ new float[] {0.08823529411764705f, 0.19019607843137254f, 0.2431372549019608f,
+ 0.2901960784313725f, 0.3411764705882353f, 0.47058823529411764f,
+ 0.5647058823529412f, 0.6901960784313725f, 0.8137254901960784f,
+ 0.9254901960784314f}
+ ),
+ new TonalPalette(
+ new float[] {0.027f, 0.03f, 0.038f, 0.044f, 0.050884955752212385f,
+ 0.07254901960784313f, 0.0934640522875817f,
+ 0.10457516339869281f, 0.11699346405228758f,
+ 0.1255813953488372f, 0.1268939393939394f, 0.12533333333333332f,
+ 0.12500000000000003f, 0.12777777777777777f},
+ new float[] {1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f},
+ new float[] {0.25f, 0.3f, 0.35f, 0.4f, 0.44313725490196076f, 0.5f, 0.5f, 0.5f,
+ 0.5f, 0.5784313725490196f,
+ 0.6549019607843137f, 0.7549019607843137f, 0.8509803921568627f,
+ 0.9411764705882353f}
+ )
+ };
+
+ private static final TonalPalette GREY_PALETTE = new TonalPalette(
+ new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+ new float[]{0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f},
+ new float[]{0.08f, 0.11f, 0.14901960784313725f, 0.2f, 0.2980392156862745f, 0.4f,
+ 0.4980392156862745f, 0.6196078431372549f, 0.7176470588235294f,
+ 0.8196078431372549f, 0.9176470588235294f, 0.9490196078431372f}
+ );
+
+ @SuppressWarnings("WeakerAccess")
+ static final ColorRange[] BLACKLISTED_COLORS = new ColorRange[] {
+
+ // Red
+ new ColorRange(
+ new Range<>(0f, 20f) /* H */,
+ new Range<>(0.7f, 1f) /* S */,
+ new Range<>(0.21f, 0.79f)) /* L */,
+ new ColorRange(
+ new Range<>(0f, 20f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.355f, 0.653f)),
+
+ // Red Orange
+ new ColorRange(
+ new Range<>(20f, 40f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.28f, 0.643f)),
+ new ColorRange(
+ new Range<>(20f, 40f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.414f, 0.561f)),
+ new ColorRange(
+ new Range<>(20f, 40f),
+ new Range<>(0f, 3f),
+ new Range<>(0.343f, 0.584f)),
+
+ // Orange
+ new ColorRange(
+ new Range<>(40f, 60f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.173f, 0.349f)),
+ new ColorRange(
+ new Range<>(40f, 60f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.233f, 0.427f)),
+ new ColorRange(
+ new Range<>(40f, 60f),
+ new Range<>(0f, 0.3f),
+ new Range<>(0.231f, 0.484f)),
+
+ // Yellow 60
+ new ColorRange(
+ new Range<>(60f, 80f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.488f, 0.737f)),
+ new ColorRange(
+ new Range<>(60f, 80f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.673f, 0.837f)),
+
+ // Yellow Green 80
+ new ColorRange(
+ new Range<>(80f, 100f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.469f, 0.61f)),
+
+ // Yellow green 100
+ new ColorRange(
+ new Range<>(100f, 120f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.388f, 0.612f)),
+ new ColorRange(
+ new Range<>(100f, 120f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.424f, 0.541f)),
+
+ // Green
+ new ColorRange(
+ new Range<>(120f, 140f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.375f, 0.52f)),
+ new ColorRange(
+ new Range<>(120f, 140f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.435f, 0.524f)),
+
+ // Green Blue 140
+ new ColorRange(
+ new Range<>(140f, 160f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.496f, 0.641f)),
+
+ // Seafoam
+ new ColorRange(
+ new Range<>(160f, 180f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.496f, 0.567f)),
+
+ // Cyan
+ new ColorRange(
+ new Range<>(180f, 200f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.52f, 0.729f)),
+
+ // Blue
+ new ColorRange(
+ new Range<>(220f, 240f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.396f, 0.571f)),
+ new ColorRange(
+ new Range<>(220f, 240f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.425f, 0.551f)),
+
+ // Blue Purple 240
+ new ColorRange(
+ new Range<>(240f, 260f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.418f, 0.639f)),
+ new ColorRange(
+ new Range<>(220f, 240f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.441f, 0.576f)),
+
+ // Blue Purple 260
+ new ColorRange(
+ new Range<>(260f, 280f),
+ new Range<>(0.3f, 1f), // Bigger range
+ new Range<>(0.461f, 0.553f)),
+
+ // Fuchsia
+ new ColorRange(
+ new Range<>(300f, 320f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.484f, 0.588f)),
+ new ColorRange(
+ new Range<>(300f, 320f),
+ new Range<>(0.3f, 0.7f),
+ new Range<>(0.48f, 0.592f)),
+
+ // Pink
+ new ColorRange(
+ new Range<>(320f, 340f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.466f, 0.629f)),
+
+ // Soft red
+ new ColorRange(
+ new Range<>(340f, 360f),
+ new Range<>(0.7f, 1f),
+ new Range<>(0.437f, 0.596f))
+ };
+
+ /**
+ * Representation of an HSL color range.
+ * <ul>
+ * <li>hsl[0] is Hue [0 .. 360)</li>
+ * <li>hsl[1] is Saturation [0...1]</li>
+ * <li>hsl[2] is Lightness [0...1]</li>
+ * </ul>
+ */
+ static class ColorRange {
+ private Range<Float> mHue;
+ private Range<Float> mSaturation;
+ private Range<Float> mLightness;
+
+ ColorRange(Range<Float> hue, Range<Float> saturation, Range<Float> lightness) {
+ mHue = hue;
+ mSaturation = saturation;
+ mLightness = lightness;
+ }
+
+ boolean containsColor(float h, float s, float l) {
+ if (!mHue.contains(h)) {
+ return false;
+ } else if (!mSaturation.contains(s)) {
+ return false;
+ } else if (!mLightness.contains(l)) {
+ return false;
+ }
+ return true;
+ }
+
+ float[] getCenter() {
+ return new float[] {
+ mHue.getLower() + (mHue.getUpper() - mHue.getLower()) / 2f,
+ mSaturation.getLower() + (mSaturation.getUpper() - mSaturation.getLower()) / 2f,
+ mLightness.getLower() + (mLightness.getUpper() - mLightness.getLower()) / 2f
+ };
+ }
+
+ @Override
+ public String toString() {
+ return String.format("H: %s, S: %s, L %s", mHue, mSaturation, mLightness);
+ }
+ }
+
+ private static List<Integer> getMainColors(WallpaperColorsCompat wallpaperColors) {
+ LinkedList<Integer> colors = new LinkedList<>();
+ if (wallpaperColors.getPrimaryColor() != 0) {
+ colors.add(wallpaperColors.getPrimaryColor());
+ }
+ if (wallpaperColors.getSecondaryColor() != 0) {
+ colors.add(wallpaperColors.getSecondaryColor());
+ }
+ if (wallpaperColors.getTertiaryColor() != 0) {
+ colors.add(wallpaperColors.getTertiaryColor());
+ }
+ return colors;
+ }
+}
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
index a2e118e..b9dd3b5 100644
--- a/src/com/android/launcher3/dynamicui/ColorExtractionService.java
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
@@ -21,6 +21,7 @@
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
@@ -82,6 +83,10 @@
if (wallpaperManager.getWallpaperInfo() != null) {
// We can't extract colors from live wallpapers; always use the default color.
extractedColors.updateHotseatPalette(null);
+
+ if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ extractedColors.updateWallpaperThemePalette(null);
+ }
} else {
// We extract colors for the hotseat and status bar separately,
// since they only consider part of the wallpaper.
@@ -90,6 +95,10 @@
if (FeatureFlags.LIGHT_STATUS_BAR) {
extractedColors.updateStatusBarPalette(getStatusBarPalette());
}
+
+ if (FeatureFlags.QSB_IN_HOTSEAT || FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ extractedColors.updateWallpaperThemePalette(getWallpaperPalette());
+ }
}
// Save the extracted colors and wallpaper id to LauncherProvider.
@@ -173,4 +182,23 @@
.clearFilters()
.generate();
}
+
+ @TargetApi(Build.VERSION_CODES.N)
+ private Palette getWallpaperPalette() {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
+ if (Utilities.ATLEAST_NOUGAT) {
+ try (ParcelFileDescriptor fd = wallpaperManager
+ .getWallpaperFile(WallpaperManager.FLAG_SYSTEM)) {
+ Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor());
+ if (bitmap != null) {
+ return Palette.from(bitmap).clearFilters().generate();
+ }
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Fetching partial bitmap failed, trying old method", e);
+ }
+ }
+
+ Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
+ return Palette.from(wallpaper).clearFilters().generate();
+ }
}
diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java
index 711508e..2d8bb86 100644
--- a/src/com/android/launcher3/dynamicui/ExtractedColors.java
+++ b/src/com/android/launcher3/dynamicui/ExtractedColors.java
@@ -16,13 +16,19 @@
package com.android.launcher3.dynamicui;
+import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Color;
+import android.support.annotation.Nullable;
import android.support.v4.graphics.ColorUtils;
import android.support.v7.graphics.Palette;
import android.util.Log;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
+import java.util.Arrays;
/**
* Saves and loads colors extracted from the wallpaper, as well as the associated wallpaper id.
@@ -32,31 +38,56 @@
public static final int DEFAULT_LIGHT = Color.WHITE;
public static final int DEFAULT_DARK = Color.BLACK;
- public static final int DEFAULT_COLOR = DEFAULT_LIGHT;
// These color profile indices should NOT be changed, since they are used when saving and
// loading extracted colors. New colors should always be added at the end.
public static final int VERSION_INDEX = 0;
public static final int HOTSEAT_INDEX = 1;
public static final int STATUS_BAR_INDEX = 2;
- // public static final int VIBRANT_INDEX = 2;
- // public static final int VIBRANT_DARK_INDEX = 3;
- // public static final int VIBRANT_LIGHT_INDEX = 4;
- // public static final int MUTED_INDEX = 5;
- // public static final int MUTED_DARK_INDEX = 6;
- // public static final int MUTED_LIGHT_INDEX = 7;
+ public static final int WALLPAPER_VIBRANT_INDEX = 3;
+ public static final int ALLAPPS_GRADIENT_MAIN_INDEX = 4;
+ public static final int ALLAPPS_GRADIENT_SECONDARY_INDEX = 5;
- public static final int NUM_COLOR_PROFILES = 2;
- private static final int VERSION = 1;
+ private static final int VERSION;
+ private static final int[] DEFAULT_VALUES;
+
+ static {
+ if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ VERSION = 3;
+ DEFAULT_VALUES = new int[] {
+ VERSION, // VERSION_INDEX
+ 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha
+ DEFAULT_DARK, // STATUS_BAR_INDEX
+ 0xFFCCCCCC, // WALLPAPER_VIBRANT_INDEX
+ 0xFF000000, // ALLAPPS_GRADIENT_MAIN_INDEX
+ 0xFF000000 // ALLAPPS_GRADIENT_SECONDARY_INDEX
+ };
+ } else if (FeatureFlags.QSB_IN_HOTSEAT) {
+ VERSION = 2;
+ DEFAULT_VALUES = new int[] {
+ VERSION, // VERSION_INDEX
+ 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha
+ DEFAULT_DARK, // STATUS_BAR_INDEX
+ 0xFFCCCCCC, // WALLPAPER_VIBRANT_INDEX
+ };
+ } else {
+ VERSION = 1;
+ DEFAULT_VALUES = new int[] {
+ VERSION, // VERSION_INDEX
+ 0x40FFFFFF, // HOTSEAT_INDEX: White with 25% alpha
+ DEFAULT_DARK, // STATUS_BAR_INDEX
+ };
+ }
+ }
private static final String COLOR_SEPARATOR = ",";
- private int[] mColors;
+ private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
+ private final int[] mColors;
public ExtractedColors() {
// The first entry is reserved for the version number.
- mColors = new int[NUM_COLOR_PROFILES + 1];
- mColors[VERSION_INDEX] = VERSION;
+ mColors = Arrays.copyOf(DEFAULT_VALUES, DEFAULT_VALUES.length);
}
public void setColorAtIndex(int index, int color) {
@@ -79,17 +110,6 @@
}
/**
- * Decodes a comma-separated String into {@link #mColors}.
- */
- void decodeFromString(String colorsString) {
- String[] splitColorsString = colorsString.split(COLOR_SEPARATOR);
- mColors = new int[splitColorsString.length];
- for (int i = 0; i < mColors.length; i++) {
- mColors[i] = Integer.parseInt(splitColorsString[i]);
- }
- }
-
- /**
* Loads colors and wallpaper id from {@link Utilities#getPrefs(Context)}.
* These were saved there in {@link ColorExtractionService}.
*/
@@ -97,19 +117,22 @@
String encodedString = Utilities.getPrefs(context).getString(
ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + "");
- decodeFromString(encodedString);
-
- if (mColors[VERSION_INDEX] != VERSION) {
+ String[] splitColorsString = encodedString.split(COLOR_SEPARATOR);
+ if (splitColorsString.length == DEFAULT_VALUES.length &&
+ Integer.parseInt(splitColorsString[VERSION_INDEX]) == VERSION) {
+ // Parse and apply the saved values.
+ for (int i = 0; i < mColors.length; i++) {
+ mColors[i] = Integer.parseInt(splitColorsString[i]);
+ }
+ } else {
+ // Leave the values as default values as the saved values may not be compatible.
ExtractionUtils.startColorExtractionService(context);
}
}
/** @param index must be one of the index values defined at the top of this class. */
- public int getColor(int index, int defaultColor) {
- if (index > VERSION_INDEX && index < mColors.length) {
- return mColors[index];
- }
- return defaultColor;
+ public int getColor(int index) {
+ return mColors[index];
}
/**
@@ -125,7 +148,7 @@
} else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) {
hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255));
} else {
- hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.25f * 255));
+ hotseatColor = DEFAULT_VALUES[HOTSEAT_INDEX];
}
setColorAtIndex(HOTSEAT_INDEX, hotseatColor);
}
@@ -134,4 +157,28 @@
setColorAtIndex(STATUS_BAR_INDEX, ExtractionUtils.isSuperLight(statusBarPalette) ?
DEFAULT_LIGHT : DEFAULT_DARK);
}
+
+ public void updateWallpaperThemePalette(@Nullable Palette wallpaperPalette) {
+ int defaultColor = DEFAULT_VALUES[WALLPAPER_VIBRANT_INDEX];
+ setColorAtIndex(WALLPAPER_VIBRANT_INDEX, wallpaperPalette == null
+ ? defaultColor : wallpaperPalette.getVibrantColor(defaultColor));
+ }
+
+ public void addOnChangeListener(OnChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void notifyChange() {
+ for (OnChangeListener listener : mListeners) {
+ listener.onExtractedColorsChanged();
+ }
+ }
+
+ /**
+ * Interface for listening for extracted color changes
+ */
+ public interface OnChangeListener {
+
+ void onExtractedColorsChanged();
+ }
}
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
index 92cb5dc..cc0e0be 100644
--- a/src/com/android/launcher3/dynamicui/ExtractionUtils.java
+++ b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
@@ -29,6 +29,7 @@
import android.support.v7.graphics.Palette;
import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
import java.util.List;
@@ -47,6 +48,9 @@
* Launcher will be notified in Launcher#onSettingsChanged(String, String).
*/
public static void startColorExtractionServiceIfNecessary(final Context context) {
+ if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ return;
+ }
// Run on a background thread, since the service is asynchronous anyway.
Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
@@ -60,6 +64,9 @@
/** Starts the {@link ColorExtractionService} without checking the wallpaper id */
public static void startColorExtractionService(Context context) {
+ if (FeatureFlags.LAUNCHER3_GRADIENT_ALL_APPS) {
+ return;
+ }
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(new JobInfo.Builder(Utilities.COLOR_EXTRACTION_JOB_ID,
diff --git a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
new file mode 100644
index 0000000..512e89a
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
@@ -0,0 +1,120 @@
+package com.android.launcher3.dynamicui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Pair;
+
+import com.android.launcher3.compat.WallpaperColorsCompat;
+import com.android.launcher3.compat.WallpaperManagerCompat;
+
+import java.util.ArrayList;
+
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+
+public class WallpaperColorInfo implements WallpaperManagerCompat.OnColorsChangedListenerCompat {
+
+ private static final int FALLBACK_COLOR = Color.WHITE;
+ private static final Object sInstanceLock = new Object();
+ private static WallpaperColorInfo sInstance;
+
+ public static WallpaperColorInfo getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ sInstance = new WallpaperColorInfo(context.getApplicationContext());
+ }
+ return sInstance;
+ }
+ }
+
+ private final ArrayList<OnChangeListener> mListeners = new ArrayList<>();
+ private final WallpaperManagerCompat mWallpaperManager;
+ private final ColorExtractionAlgorithm mExtractionType;
+ private int mMainColor;
+ private int mSecondaryColor;
+ private boolean mIsDark;
+ private boolean mSupportsDarkText;
+ private OnThemeChangeListener mOnThemeChangeListener;
+
+ private WallpaperColorInfo(Context context) {
+ mWallpaperManager = WallpaperManagerCompat.getInstance(context);
+ mWallpaperManager.addOnColorsChangedListener(this);
+ mExtractionType = ColorExtractionAlgorithm.newInstance(context);
+ update(mWallpaperManager.getWallpaperColors(FLAG_SYSTEM));
+ }
+
+ public int getMainColor() {
+ return mMainColor;
+ }
+
+ public int getSecondaryColor() {
+ return mSecondaryColor;
+ }
+
+ public boolean isDark() {
+ return mIsDark;
+ }
+
+ public boolean supportsDarkText() {
+ return mSupportsDarkText;
+ }
+
+ @Override
+ public void onColorsChanged(WallpaperColorsCompat colors, int which) {
+ if ((which & FLAG_SYSTEM) != 0) {
+ boolean wasDarkTheme = mIsDark;
+ boolean didSupportDarkText = mSupportsDarkText;
+ update(colors);
+ notifyChange(wasDarkTheme != mIsDark || didSupportDarkText != mSupportsDarkText);
+ }
+ }
+
+ private void update(WallpaperColorsCompat wallpaperColors) {
+ Pair<Integer, Integer> colors = mExtractionType.extractInto(wallpaperColors);
+ if (colors != null) {
+ mMainColor = colors.first;
+ mSecondaryColor = colors.second;
+ } else {
+ mMainColor = FALLBACK_COLOR;
+ mSecondaryColor = FALLBACK_COLOR;
+ }
+ mSupportsDarkText = wallpaperColors != null
+ ? (wallpaperColors.getColorHints()
+ & WallpaperColorsCompat.HINT_SUPPORTS_DARK_TEXT) > 0 : false;
+ float[] hsl = new float[3];
+ ColorUtils.colorToHSL(mMainColor, hsl);
+ mIsDark = hsl[2] < 0.2f;
+ }
+
+ public void setOnThemeChangeListener(OnThemeChangeListener onThemeChangeListener) {
+ this.mOnThemeChangeListener = onThemeChangeListener;
+ }
+
+ public void addOnChangeListener(OnChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void removeOnChangeListener(OnChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ public void notifyChange(boolean themeChanged) {
+ if (themeChanged) {
+ if (mOnThemeChangeListener != null) {
+ mOnThemeChangeListener.onThemeChanged();
+ }
+ } else {
+ for (OnChangeListener listener : mListeners) {
+ listener.onExtractedColorsChanged(this);
+ }
+ }
+ }
+
+ public interface OnChangeListener {
+ void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
+ }
+
+ public interface OnThemeChangeListener {
+ void onThemeChanged();
+ }
+}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 840fcf5..ff357c0 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -1,12 +1,5 @@
package com.android.launcher3.folder;
-import android.view.View;
-
-import com.android.launcher3.config.FeatureFlags;
-
-import java.util.ArrayList;
-import java.util.List;
-
public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
@@ -27,7 +20,7 @@
private float mBaselineIconScale;
@Override
- public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+ public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
mAvailableSpace = availableSpace;
mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f;
mIconSize = intrinsicIconSize;
@@ -36,8 +29,8 @@
}
@Override
- public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
- int curNumItems, FolderIcon.PreviewItemDrawingParams params) {
+ public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+ PreviewItemDrawingParams params) {
float totalScale = scaleForItem(index, curNumItems);
float transX;
@@ -54,7 +47,7 @@
}
if (params == null) {
- params = new FolderIcon.PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+ params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
} else {
params.update(transX, transY, totalScale);
params.overlayAlpha = overlayAlpha;
@@ -121,6 +114,11 @@
}
@Override
+ public float getIconSize() {
+ return mIconSize;
+ }
+
+ @Override
public int maxNumItems() {
return MAX_NUM_ITEMS_IN_PREVIEW;
}
@@ -129,24 +127,4 @@
public boolean clipToBackground() {
return true;
}
-
- @Override
- public List<View> getItemsToDisplay(Folder folder) {
- List<View> items = new ArrayList<>(folder.getItemsInReadingOrder());
- int numItems = items.size();
- if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION && numItems > MAX_NUM_ITEMS_IN_PREVIEW) {
- // We match the icons in the preview with the layout of the opened folder (b/27944225),
- // but we still need to figure out how we want to handle updating the preview when the
- // upper left quadrant changes.
- int appsPerRow = folder.mContent.getPageAt(0).getCountX();
- int appsToDelete = appsPerRow - MAX_NUM_ITEMS_PER_ROW;
-
- // We only display the upper left quadrant.
- while (appsToDelete > 0) {
- items.remove(MAX_NUM_ITEMS_PER_ROW);
- appsToDelete--;
- }
- }
- return items.subList(0, Math.min(numItems, MAX_NUM_ITEMS_IN_PREVIEW));
- }
}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 0e9d08d..f68b394 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -34,7 +34,6 @@
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
@@ -46,6 +45,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Alarm;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -66,8 +66,9 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.AccessibleDragListenerAdapter;
+import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.anim.CircleRevealOutlineProvider;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragController.DragListener;
import com.android.launcher3.dragndrop.DragLayer;
@@ -75,12 +76,13 @@
import com.android.launcher3.pageindicators.PageIndicatorDots;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
-import com.android.launcher3.util.CircleRevealOutlineProvider;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -131,10 +133,12 @@
private final Alarm mOnScrollHintAlarm = new Alarm();
@Thunk final Alarm mScrollPauseAlarm = new Alarm();
- @Thunk final ArrayList<View> mItemsInReadingOrder = new ArrayList<View>();
+ @Thunk final ArrayList<View> mItemsInRankOrder = new ArrayList<>();
+
+ private AnimatorSet mCurrentAnimator;
private final int mExpandDuration;
- private final int mMaterialExpandDuration;
+ public final int mMaterialExpandDuration;
private final int mMaterialExpandStagger;
protected final Launcher mLauncher;
@@ -279,11 +283,8 @@
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
ShortcutInfo item = (ShortcutInfo) tag;
- if (!v.isInTouchMode()) {
- return false;
- }
- mEmptyCellRank = item.rank;
+ mEmptyCellRank = mContent.getReadingOrderPosForRank(item.rank);
mCurrentDragView = v;
mDragController.addDragListener(this);
@@ -331,6 +332,7 @@
if (mIsExternalDrag && mDragInProgress) {
completeDragExit();
}
+ mDragInProgress = false;
mDragController.removeDragListener(this);
}
@@ -504,51 +506,31 @@
mState = STATE_SMALL;
}
- /**
- * Opens the user folder described by the specified tag. The opening of the folder
- * is animated relative to the specified View. If the View is null, no animation
- * is played.
- */
- public void animateOpen() {
- Folder openFolder = getOpen(mLauncher);
- if (openFolder != null && openFolder != this) {
- // Close any open folder before opening a folder.
- openFolder.close(true);
+ private void startAnimation(final AnimatorSet a) {
+ if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) {
+ mCurrentAnimator.cancel();
}
-
- DragLayer dragLayer = mLauncher.getDragLayer();
- // Just verify that the folder hasn't already been added to the DragLayer.
- // There was a one-off crash where the folder had a parent already.
- if (getParent() == null) {
- dragLayer.addView(this);
- mDragController.addDropTarget(this);
- } else {
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
- Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
- + getParent());
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mState = STATE_ANIMATING;
+ mCurrentAnimator = a;
}
- }
- mIsOpen = true;
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mCurrentAnimator = null;
+ }
+ });
+ a.start();
+ }
- mContent.completePendingPageChanges();
- if (!mDragInProgress) {
- // Open on the first page.
- mContent.snapToPageImmediately(0);
- }
-
- // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
- // leads to an inconsistent state if you drag out of the folder and drag back in without
- // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
- mDeleteFolderOnDropCompleted = false;
-
- final Runnable onCompleteRunnable;
+ private AnimatorSet getOpeningAnimator() {
prepareReveal();
- centerAboutIcon();
-
mFolderIcon.growAndFadeOut();
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+
int width = getFolderWidth();
int height = getFolderHeight();
@@ -590,24 +572,78 @@
anim.play(textAlpha);
anim.play(reveal);
- mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
- mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
+ AnimationLayerSet layerSet = new AnimationLayerSet();
+ layerSet.addView(mContent);
+ layerSet.addView(mFooter);
+ anim.addListener(layerSet);
+
+ return anim;
+ }
+
+ /**
+ * Opens the user folder described by the specified tag. The opening of the folder
+ * is animated relative to the specified View. If the View is null, no animation
+ * is played.
+ */
+ public void animateOpen() {
+ Folder openFolder = getOpen(mLauncher);
+ if (openFolder != null && openFolder != this) {
+ // Close any open folder before opening a folder.
+ openFolder.close(true);
+ }
+
+ DragLayer dragLayer = mLauncher.getDragLayer();
+ // Just verify that the folder hasn't already been added to the DragLayer.
+ // There was a one-off crash where the folder had a parent already.
+ if (getParent() == null) {
+ dragLayer.addView(this);
+ mDragController.addDropTarget(this);
+ } else {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
+ + getParent());
+ }
+ }
+
+ mIsOpen = true;
+
+ mContent.completePendingPageChanges();
+ if (!mDragInProgress) {
+ // Open on the first page.
+ mContent.snapToPageImmediately(0);
+ }
+
+ // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+ // leads to an inconsistent state if you drag out of the folder and drag back in without
+ // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
+ mDeleteFolderOnDropCompleted = false;
+
+ final Runnable onCompleteRunnable;
+ centerAboutIcon();
+
+ AnimatorSet anim = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ ? new FolderAnimationManager(this, true /* isOpening */).getAnimator()
+ : getOpeningAnimator();
onCompleteRunnable = new Runnable() {
@Override
public void run() {
- mContent.setLayerType(LAYER_TYPE_NONE, null);
- mFooter.setLayerType(LAYER_TYPE_NONE, null);
mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
}
};
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.setBackgroundVisible(false);
+ mFolderIcon.drawLeaveBehindIfExists();
+ } else {
+ mFolderIcon.setVisibility(INVISIBLE);
+ }
+
Utilities.sendCustomAccessibilityEvent(
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
- mState = STATE_ANIMATING;
}
@Override
public void onAnimationEnd(Animator animation) {
@@ -653,7 +689,7 @@
}
mPageIndicator.stopAllAnimations();
- anim.start();
+ startAnimation(anim);
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
@@ -669,7 +705,7 @@
}
public void beginExternalDrag() {
- mEmptyCellRank = mContent.allocateRankForNewItem();
+ mEmptyCellRank = mContent.getReadingOrderPosForRank(mContent.allocateRankForNewItem());
mIsExternalDrag = true;
mDragInProgress = true;
@@ -692,7 +728,11 @@
}
if (mFolderIcon != null) {
- mFolderIcon.shrinkAndFadeIn(animate);
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.clearLeaveBehindIfExists();
+ } else {
+ mFolderIcon.shrinkAndFadeIn(animate);
+ }
}
if (!(getParent() instanceof DragLayer)) return;
@@ -709,12 +749,24 @@
parent.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
+ private AnimatorSet getClosingAnimator() {
+ AnimatorSet animatorSet = LauncherAnimUtils.createAnimatorSet();
+ animatorSet.play(LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f));
+
+ AnimationLayerSet layerSet = new AnimationLayerSet();
+ layerSet.addView(this);
+ animatorSet.addListener(layerSet);
+ animatorSet.setDuration(mExpandDuration);
+ return animatorSet;
+ }
+
private void animateClosed() {
- final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
- oa.addListener(new AnimatorListenerAdapter() {
+ AnimatorSet a = FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION
+ ? new FolderAnimationManager(this, false /* isOpening */).getAnimator()
+ : getClosingAnimator();
+ a.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- setLayerType(LAYER_TYPE_NONE, null);
closeComplete(true);
}
@Override
@@ -723,12 +775,9 @@
Folder.this,
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
- mState = STATE_ANIMATING;
}
});
- oa.setDuration(mExpandDuration);
- setLayerType(LAYER_TYPE_HARDWARE, null);
- oa.start();
+ startAnimation(a);
}
private void closeComplete(boolean wasAnimated) {
@@ -739,8 +788,20 @@
}
mDragController.removeDropTarget(this);
clearFocus();
- if (wasAnimated) {
- mFolderIcon.requestFocus();
+ if (mFolderIcon != null) {
+ mFolderIcon.setVisibility(View.VISIBLE);
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ mFolderIcon.mFolderName.setTextVisibility(true);
+ mFolderIcon.setBackgroundVisible(true);
+ mFolderIcon.mBackground.fadeInBackgroundShadow();
+ }
+ if (wasAnimated) {
+ mFolderIcon.mBackground.animateBackgroundStroke();
+ if (mFolderIcon.hasBadge()) {
+ mFolderIcon.createBadgeScaleAnimator(0f, 1f).start();
+ }
+ mFolderIcon.requestFocus();
+ }
}
if (mRearrangeOnClose) {
@@ -936,7 +997,7 @@
ShortcutInfo info = (ShortcutInfo) d.dragInfo;
View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
? mCurrentDragView : mContent.createNewView(info);
- ArrayList<View> views = getItemsInReadingOrder();
+ ArrayList<View> views = getItemsInRankOrder();
views.add(info.rank, icon);
mContent.arrangeChildren(views, views.size());
mItemsInvalidated = true;
@@ -1011,7 +1072,7 @@
}
private void updateItemLocationsInDatabaseBatch() {
- ArrayList<View> list = getItemsInReadingOrder();
+ ArrayList<View> list = getItemsInRankOrder();
ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
for (int i = 0; i < list.size(); i++) {
View v = list.get(i);
@@ -1030,6 +1091,9 @@
}
public boolean isDropEnabled() {
+ if (FeatureFlags.LAUNCHER3_NEW_FOLDER_ANIMATION) {
+ return mState != STATE_ANIMATING;
+ }
return true;
}
@@ -1167,7 +1231,7 @@
* otherwise it is ignored.
*/
public void rearrangeChildren(int itemCount) {
- ArrayList<View> views = getItemsInReadingOrder();
+ ArrayList<View> views = getItemsInRankOrder();
mContent.arrangeChildren(views, Math.max(itemCount, views.size()));
mItemsInvalidated = true;
}
@@ -1291,53 +1355,61 @@
}
mContent.completePendingPageChanges();
- View currentDragView;
- final ShortcutInfo si;
- if (d.dragInfo instanceof AppInfo) {
- // Came from all apps -- make a copy.
- si = ((AppInfo) d.dragInfo).makeShortcut();
- } else {
- // ShortcutInfo
- si = (ShortcutInfo) d.dragInfo;
- }
- if (mIsExternalDrag) {
- currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
- // Actually move the item in the database if it was an external drag. Call this
- // before creating the view, so that ShortcutInfo is updated appropriately.
- mLauncher.getModelWriter().addOrMoveItemInDatabase(
- si, mInfo.id, 0, si.cellX, si.cellY);
+ if (d.dragInfo instanceof PendingAddShortcutInfo) {
+ PendingAddShortcutInfo pasi = (PendingAddShortcutInfo) d.dragInfo;
+ pasi.container = mInfo.id;
+ pasi.rank = mEmptyCellRank;
- // We only need to update the locations if it doesn't get handled in #onDropCompleted.
- if (d.dragSource != this) {
- updateItemLocationsInDatabaseBatch();
- }
- mIsExternalDrag = false;
- } else {
- currentDragView = mCurrentDragView;
- mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
- }
-
- if (d.dragView.hasDrawn()) {
-
- // Temporarily reset the scale such that the animation target gets calculated correctly.
- float scaleX = getScaleX();
- float scaleY = getScaleY();
- setScaleX(1.0f);
- setScaleY(1.0f);
- mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
- cleanUpRunnable, null);
- setScaleX(scaleX);
- setScaleY(scaleY);
- } else {
+ mLauncher.addPendingItem(pasi, pasi.container, pasi.screenId, null, pasi.spanX,
+ pasi.spanY);
d.deferDragViewCleanupPostAnimation = false;
- currentDragView.setVisibility(VISIBLE);
- }
- mItemsInvalidated = true;
- rearrangeChildren();
+ mRearrangeOnClose = true;
+ } else {
+ final ShortcutInfo si;
+ if (d.dragInfo instanceof AppInfo) {
+ // Came from all apps -- make a copy.
+ si = ((AppInfo) d.dragInfo).makeShortcut();
+ } else {
+ // ShortcutInfo
+ si = (ShortcutInfo) d.dragInfo;
+ }
- // Temporarily suppress the listener, as we did all the work already here.
- try (SuppressInfoChanges s = new SuppressInfoChanges()) {
- mInfo.add(si, false);
+ View currentDragView;
+ if (mIsExternalDrag) {
+ // Actually move the item in the database if it was an external drag. Call this
+ // before creating the view, so that ShortcutInfo is updated appropriately.
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(si, mInfo.id, 0, si.cellX, si.cellY);
+ }
+
+ currentDragView = mIsExternalDrag
+ ? mContent.createNewView(si)
+ : mCurrentDragView;
+ mIsExternalDrag = false;
+
+ // Note: addViewForRankDuringDragAndDrop handles rearranging the children.
+ mContent.addViewForRankDuringDragAndDrop(currentDragView, si, mEmptyCellRank);
+ mItemsInvalidated = true;
+
+ if (d.dragView.hasDrawn()) {
+ // Temporarily reset the scale such that the animation target gets calculated
+ // correctly.
+ float scaleX = getScaleX();
+ float scaleY = getScaleY();
+ setScaleX(1.0f);
+ setScaleY(1.0f);
+ mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, currentDragView,
+ cleanUpRunnable, null);
+ setScaleX(scaleX);
+ setScaleY(scaleY);
+ } else {
+ d.deferDragViewCleanupPostAnimation = false;
+ currentDragView.setVisibility(VISIBLE);
+ }
+
+ // Temporarily suppress the listener, as we did all the work already here.
+ try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+ mInfo.add(si, false);
+ }
}
// Clear the drag info, as it is no longer being dragged.
@@ -1366,11 +1438,15 @@
}
@Override
- public void onAdd(ShortcutInfo item) {
- mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
+ public void onAdd(ShortcutInfo item, int rank) {
+ View view = mContent.createAndAddViewForRank(item, rank);
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(item, mInfo.id, 0, item.cellX,
+ item.cellY);
+
+ ArrayList<View> items = new ArrayList<>(getItemsInRankOrder());
+ items.add(rank, view);
+ mContent.arrangeChildren(items, items.size());
mItemsInvalidated = true;
- mLauncher.getModelWriter().addOrMoveItemInDatabase(
- item, mInfo.id, 0, item.cellX, item.cellY);
}
public void onRemove(ShortcutInfo item) {
@@ -1414,20 +1490,50 @@
public void onTitleChanged(CharSequence title) {
}
- public ArrayList<View> getItemsInReadingOrder() {
+ public ArrayList<View> getItemsInRankOrder() {
if (mItemsInvalidated) {
- mItemsInReadingOrder.clear();
- mContent.iterateOverItems(new ItemOperator() {
+ mItemsInRankOrder.clear();
+ mItemsInRankOrder.addAll(getItemsInReadingOrder());
+ mItemsInRankOrder.sort(VIEW_RANK_COMPARATOR);
- @Override
- public boolean evaluate(ItemInfo info, View view) {
- mItemsInReadingOrder.add(view);
- return false;
- }
- });
mItemsInvalidated = false;
}
- return mItemsInReadingOrder;
+ return mItemsInRankOrder;
+ }
+
+ /**
+ * This is an expensive call. Consider using {@link #getItemsInRankOrder()} instead.
+ */
+ public ArrayList<View> getItemsInReadingOrder() {
+ final ArrayList<View> itemsInReadingOrder = new ArrayList<>();
+ mContent.iterateOverItems(new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View view) {
+ itemsInReadingOrder.add(view);
+ return false;
+ }
+ });
+ return itemsInReadingOrder;
+ }
+
+ public List<BubbleTextView> getItemsOnCurrentPage() {
+ ArrayList<View> allItems = getItemsInRankOrder();
+ int currentPage = mContent.getCurrentPage();
+ int lastPage = mContent.getPageCount() - 1;
+ int totalItemsInFolder = allItems.size();
+ int itemsPerPage = mContent.itemsPerPage();
+ int numItemsOnCurrentPage = currentPage == lastPage
+ ? totalItemsInFolder - (itemsPerPage * currentPage)
+ : itemsPerPage;
+
+ int startIndex = currentPage * itemsPerPage;
+ int endIndex = startIndex + numItemsOnCurrentPage;
+
+ List<BubbleTextView> itemsOnCurrentPage = new ArrayList<>(numItemsOnCurrentPage);
+ for (int i = startIndex; i < endIndex; ++i) {
+ itemsOnCurrentPage.add((BubbleTextView) allItems.get(i));
+ }
+ return itemsOnCurrentPage;
}
public void onFocusChange(View v, boolean hasFocus) {
@@ -1519,6 +1625,13 @@
}
};
+ public static final Comparator<View> VIEW_RANK_COMPARATOR = new Comparator<View>() {
+ @Override
+ public int compare(View lhs, View rhs) {
+ return ITEM_POS_COMPARATOR.compare((ItemInfo) lhs.getTag(), (ItemInfo) rhs.getTag());
+ }
+ };
+
/**
* Temporary resource held while we don't want to handle info changes
*/
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
new file mode 100644
index 0000000..7e8d0c7
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Property;
+import android.view.View;
+import android.view.animation.AnimationUtils;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.util.Themes;
+
+import java.util.List;
+
+/**
+ * Manages the opening and closing animations for a {@link Folder}.
+ *
+ * All of the animations are done in the Folder.
+ * ie. When the user taps on the FolderIcon, we immediately hide the FolderIcon and show the Folder
+ * in its place before starting the animation.
+ */
+public class FolderAnimationManager {
+
+ private Folder mFolder;
+ private FolderPagedView mContent;
+ private GradientDrawable mFolderBackground;
+
+ private FolderIcon mFolderIcon;
+ private PreviewBackground mPreviewBackground;
+
+ private Context mContext;
+ private Launcher mLauncher;
+
+ private final boolean mIsOpening;
+
+ private final int mDuration;
+ private final int mDelay;
+
+ private final TimeInterpolator mFolderInterpolator;
+ private final TimeInterpolator mLargeFolderPreviewItemOpenInterpolator;
+ private final TimeInterpolator mLargeFolderPreviewItemCloseInterpolator;
+
+ private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+ private static final Property<View, Float> SCALE_PROPERTY =
+ new Property<View, Float>(Float.class, "scale") {
+ @Override
+ public Float get(View view) {
+ return view.getScaleX();
+ }
+
+ @Override
+ public void set(View view, Float scale) {
+ view.setScaleX(scale);
+ view.setScaleY(scale);
+ }
+ };
+
+ public FolderAnimationManager(Folder folder, boolean isOpening) {
+ mFolder = folder;
+ mContent = folder.mContent;
+ mFolderBackground = (GradientDrawable) mFolder.getBackground();
+
+ mFolderIcon = folder.mFolderIcon;
+ mPreviewBackground = mFolderIcon.mBackground;
+
+ mContext = folder.getContext();
+ mLauncher = folder.mLauncher;
+
+ mIsOpening = isOpening;
+
+ mDuration = mFolder.mMaterialExpandDuration;
+ mDelay = mContext.getResources().getInteger(R.integer.config_folderDelay);
+
+ mFolderInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.folder_interpolator);
+ mLargeFolderPreviewItemOpenInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.large_folder_preview_item_open_interpolator);
+ mLargeFolderPreviewItemCloseInterpolator = AnimationUtils.loadInterpolator(mContext,
+ R.interpolator.large_folder_preview_item_close_interpolator);
+ }
+
+
+ /**
+ * Prepares the Folder for animating between open / closed states.
+ */
+ public AnimatorSet getAnimator() {
+ final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) mFolder.getLayoutParams();
+ FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+ final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+
+ // Match position of the FolderIcon
+ final Rect folderIconPos = new Rect();
+ float scaleRelativeToDragLayer = mLauncher.getDragLayer()
+ .getDescendantRectRelativeToSelf(mFolderIcon, folderIconPos);
+ float initialSize = (mPreviewBackground.getRadius() * 2) * scaleRelativeToDragLayer;
+
+ // Match size/scale of icons in the preview
+ float previewScale = rule.scaleForItem(0, itemsInPreview.size());
+ float previewSize = rule.getIconSize() * previewScale;
+ float initialScale = previewSize / itemsInPreview.get(0).getIconSize()
+ * scaleRelativeToDragLayer;
+ final float finalScale = 1f;
+ float scale = mIsOpening ? initialScale : finalScale;
+ mFolder.setScaleX(scale);
+ mFolder.setScaleY(scale);
+ mFolder.setPivotX(0);
+ mFolder.setPivotY(0);
+
+ // We want to create a small X offset for the preview items, so that they follow their
+ // expected path to their final locations. ie. an icon should not move right, if it's final
+ // location is to its left. This value is arbitrarily defined.
+ int previewItemOffsetX = (int) (previewSize / 2);
+ if (Utilities.isRtl(mContext.getResources())) {
+ previewItemOffsetX = (int) (lp.width * initialScale - initialSize - previewItemOffsetX);
+ }
+
+ final int paddingOffsetX = (int) ((mFolder.getPaddingLeft() + mContent.getPaddingLeft())
+ * initialScale);
+ final int paddingOffsetY = (int) ((mFolder.getPaddingTop() + mContent.getPaddingTop())
+ * initialScale);
+
+ // Background can have a scaled radius in drag and drop mode.
+ int radiusDiff = mPreviewBackground.getScaledRadius()- mPreviewBackground.getRadius();
+
+ int initialX = folderIconPos.left + mPreviewBackground.getOffsetX() - paddingOffsetX
+ - previewItemOffsetX + radiusDiff;
+ int initialY = folderIconPos.top + mPreviewBackground.getOffsetY() - paddingOffsetY
+ + radiusDiff;
+ final float xDistance = initialX - lp.x;
+ final float yDistance = initialY - lp.y;
+
+ // Set up the Folder background.
+ final int finalColor = Themes.getAttrColor(mContext, android.R.attr.colorPrimary);
+ final int initialColor =
+ ColorUtils.setAlphaComponent(finalColor, mPreviewBackground.getBackgroundAlpha());
+ mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
+
+ // Set up the reveal animation that clips the Folder.
+ int totalOffsetX = paddingOffsetX + previewItemOffsetX;
+ Rect startRect = new Rect(
+ Math.round(totalOffsetX / initialScale),
+ Math.round(paddingOffsetY / initialScale),
+ Math.round((totalOffsetX + initialSize) / initialScale),
+ Math.round((paddingOffsetY + initialSize) / initialScale));
+ Rect endRect = new Rect(0, 0, lp.width, lp.height);
+ float initialRadius = initialSize / initialScale / 2f;
+ float finalRadius = Utilities.pxFromDp(2, mContext.getResources().getDisplayMetrics());
+
+ // Create the animators.
+ AnimatorSet a = LauncherAnimUtils.createAnimatorSet();
+
+ // Initialize the Folder items' text.
+ PropertyResetListener colorResetListener = new PropertyResetListener<>(
+ BubbleTextView.TEXT_ALPHA_PROPERTY,
+ Color.alpha(Themes.getAttrColor(mContext, android.R.attr.textColorSecondary)));
+ for (BubbleTextView icon : mFolder.getItemsOnCurrentPage()) {
+ if (mIsOpening) {
+ icon.setTextVisibility(false);
+ }
+ ObjectAnimator anim = icon.createTextAlphaAnimator(mIsOpening);
+ anim.addListener(colorResetListener);
+ play(a, anim);
+ }
+
+ play(a, getAnimator(mFolder, View.TRANSLATION_X, xDistance, 0f));
+ play(a, getAnimator(mFolder, View.TRANSLATION_Y, yDistance, 0f));
+ play(a, getAnimator(mFolder, SCALE_PROPERTY, initialScale, finalScale));
+ play(a, getAnimator(mFolderBackground, "color", initialColor, finalColor));
+ play(a, mFolderIcon.mFolderName.createTextAlphaAnimator(!mIsOpening));
+ RoundedRectRevealOutlineProvider outlineProvider = new RoundedRectRevealOutlineProvider(
+ initialRadius, finalRadius, startRect, endRect) {
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return true;
+ }
+ };
+ play(a, outlineProvider.createRevealAnimator(mFolder, !mIsOpening));
+
+ // Animate the elevation midway so that the shadow is not noticeable in the background.
+ int midDuration = mDuration / 2;
+ Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
+ play(a, z, mIsOpening ? midDuration : 0, midDuration);
+
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mFolder.setTranslationX(0.0f);
+ mFolder.setTranslationY(0.0f);
+ mFolder.setTranslationZ(0.0f);
+ mFolder.setScaleX(1f);
+ mFolder.setScaleY(1f);
+ }
+ });
+
+ // We set the interpolator on all current child animators here, because the preview item
+ // animators may use a different interpolator.
+ for (Animator animator : a.getChildAnimations()) {
+ animator.setInterpolator(mFolderInterpolator);
+ }
+
+ addPreviewItemAnimators(a, initialScale / scaleRelativeToDragLayer, previewItemOffsetX);
+ return a;
+ }
+
+ /**
+ * Animate the items that are displayed in the preview.
+ */
+ private void addPreviewItemAnimators(AnimatorSet animatorSet, final float folderScale,
+ int previewItemOffsetX) {
+ FolderIcon.PreviewLayoutRule rule = mFolderIcon.getLayoutRule();
+ final List<BubbleTextView> itemsInPreview = mFolderIcon.getItemsToDisplay();
+ final int numItemsInPreview = itemsInPreview.size();
+
+ TimeInterpolator previewItemInterpolator = getPreviewItemInterpolator();
+
+ ShortcutAndWidgetContainer cwc = mContent.getPageAt(0).getShortcutsAndWidgets();
+ for (int i = 0; i < numItemsInPreview; ++i) {
+ final BubbleTextView btv = itemsInPreview.get(i);
+ CellLayout.LayoutParams btvLp = (CellLayout.LayoutParams) btv.getLayoutParams();
+
+ // Calculate the final values in the LayoutParams.
+ btvLp.isLockedToGrid = true;
+ cwc.setupLp(btv);
+
+ // Match scale of icons in the preview.
+ float previewScale = rule.scaleForItem(i, numItemsInPreview);
+ float previewSize = rule.getIconSize() * previewScale;
+ float iconScale = previewSize / itemsInPreview.get(i).getIconSize();
+
+ final float initialScale = iconScale / folderScale;
+ final float finalScale = 1f;
+ float scale = mIsOpening ? initialScale : finalScale;
+ btv.setScaleX(scale);
+ btv.setScaleY(scale);
+
+ // Match positions of the icons in the folder with their positions in the preview
+ rule.computePreviewItemDrawingParams(i, numItemsInPreview, mTmpParams);
+ // The PreviewLayoutRule assumes that the icon size takes up the entire width so we
+ // offset by the actual size.
+ int iconOffsetX = (int) ((btvLp.width - btv.getIconSize()) * iconScale) / 2;
+
+ final int previewPosX =
+ (int) ((mTmpParams.transX - iconOffsetX + previewItemOffsetX) / folderScale);
+ final int previewPosY = (int) (mTmpParams.transY / folderScale);
+
+ final float xDistance = previewPosX - btvLp.x;
+ final float yDistance = previewPosY - btvLp.y;
+
+ Animator translationX = getAnimator(btv, View.TRANSLATION_X, xDistance, 0f);
+ translationX.setInterpolator(previewItemInterpolator);
+ play(animatorSet, translationX);
+
+ Animator translationY = getAnimator(btv, View.TRANSLATION_Y, yDistance, 0f);
+ translationY.setInterpolator(previewItemInterpolator);
+ play(animatorSet, translationY);
+
+ Animator scaleAnimator = getAnimator(btv, SCALE_PROPERTY, initialScale, finalScale);
+ scaleAnimator.setInterpolator(previewItemInterpolator);
+ play(animatorSet, scaleAnimator);
+
+ if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ // These delays allows the preview items to move as part of the Folder's motion,
+ // and its only necessary for large folders because of differing interpolators.
+ int delay = mIsOpening ? mDelay : mDelay * 2;
+ if (mIsOpening) {
+ translationX.setStartDelay(delay);
+ translationY.setStartDelay(delay);
+ scaleAnimator.setStartDelay(delay);
+ }
+ translationX.setDuration(translationX.getDuration() - delay);
+ translationY.setDuration(translationY.getDuration() - delay);
+ scaleAnimator.setDuration(scaleAnimator.getDuration() - delay);
+ }
+
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ // Necessary to initialize values here because of the start delay.
+ if (mIsOpening) {
+ btv.setTranslationX(xDistance);
+ btv.setTranslationY(yDistance);
+ btv.setScaleX(initialScale);
+ btv.setScaleY(initialScale);
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ btv.setTranslationX(0.0f);
+ btv.setTranslationY(0.0f);
+ btv.setScaleX(1f);
+ btv.setScaleY(1f);
+ }
+ });
+ }
+ }
+
+ private void play(AnimatorSet as, Animator a) {
+ play(as, a, a.getStartDelay(), mDuration);
+ }
+
+ private void play(AnimatorSet as, Animator a, long startDelay, int duration) {
+ a.setStartDelay(startDelay);
+ a.setDuration(duration);
+ as.play(a);
+ }
+
+ private TimeInterpolator getPreviewItemInterpolator() {
+ if (mFolder.getItemCount() > FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ // With larger folders, we want the preview items to reach their final positions faster
+ // (when opening) and later (when closing) so that they appear aligned with the rest of
+ // the folder items when they are both visible.
+ return mIsOpening
+ ? mLargeFolderPreviewItemOpenInterpolator
+ : mLargeFolderPreviewItemCloseInterpolator;
+ }
+ return mFolderInterpolator;
+ }
+
+ private Animator getAnimator(View view, Property property, float v1, float v2) {
+ return mIsOpening
+ ? ObjectAnimator.ofFloat(view, property, v1, v2)
+ : ObjectAnimator.ofFloat(view, property, v2, v1);
+ }
+
+ private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
+ return mIsOpening
+ ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
+ : ObjectAnimator.ofArgb(drawable, property, v2, v1);
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 8d58fef..3a0e71f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -19,25 +19,15 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
+import android.support.annotation.NonNull;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Property;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -56,7 +46,6 @@
import com.android.launcher3.CheckLongPressHelper;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.ItemInfo;
@@ -77,6 +66,7 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.util.Thunk;
+import com.android.launcher3.widget.PendingAddShortcutInfo;
import java.util.ArrayList;
import java.util.List;
@@ -97,8 +87,6 @@
private CheckLongPressHelper mLongPressHelper;
private StylusEventHelper mStylusEventHelper;
- // The number of icons to display in the
- private static final int CONSUMPTION_ANIMATION_DURATION = 100;
private static final int DROP_IN_ANIMATION_DURATION = 400;
private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
@@ -113,11 +101,12 @@
// These variables are all associated with the drawing of the preview; they are stored
// as member variables for shared usage and to avoid computation on each frame
- private int mIntrinsicIconSize = -1;
+ private float mIntrinsicIconSize = -1;
private int mTotalWidth = -1;
private int mPrevTopPadding = -1;
PreviewBackground mBackground = new PreviewBackground();
+ private boolean mBackgroundIsVisible = true;
private PreviewLayoutRule mPreviewLayoutRule;
@@ -126,8 +115,9 @@
private float mSlop;
+ FolderIconPreviewVerifier mPreviewVerifier;
private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
- private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
+ private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<>();
private Drawable mReferenceDrawable = null;
private Alarm mOpenAlarm = new Alarm();
@@ -181,7 +171,8 @@
}
DeviceProfile grid = launcher.getDeviceProfile();
- FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
+ FolderIcon icon = (FolderIcon) LayoutInflater.from(group.getContext())
+ .inflate(resId, group, false);
icon.setClipToPadding(false);
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
@@ -221,6 +212,7 @@
private void setFolder(Folder folder) {
mFolder = folder;
+ mPreviewVerifier = new FolderIconPreviewVerifier(mLauncher.getDeviceProfile().inv);
updateItemDrawingParams(false);
}
@@ -233,8 +225,7 @@
}
public boolean acceptDrop(ItemInfo dragInfo) {
- final ItemInfo item = dragInfo;
- return !mFolder.isDestroyed() && willAcceptItem(item);
+ return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
}
public void addItem(ShortcutInfo item) {
@@ -249,11 +240,9 @@
mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
mOpenAlarm.setOnAlarmListener(mOnOpenListener);
if (SPRING_LOADING_ENABLED &&
- ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
- // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
- // though widget-style shortcuts can be added to folders. The issue is that we need
- // to deal with configuration activities which are currently handled in
- // Workspace#onDropExternal.
+ ((dragInfo instanceof AppInfo)
+ || (dragInfo instanceof ShortcutInfo)
+ || (dragInfo instanceof PendingAddShortcutInfo))) {
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
}
}
@@ -383,14 +372,11 @@
private void computePreviewDrawingParams(int drawableSize, int totalSize) {
if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize ||
mPrevTopPadding != getPaddingTop()) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
-
mIntrinsicIconSize = drawableSize;
mTotalWidth = totalSize;
mPrevTopPadding = getPaddingTop();
- mBackground.setup(getResources().getDisplayMetrics(), grid, this, mTotalWidth,
- getPaddingTop());
+ mBackground.setup(mLauncher, this, mTotalWidth, getPaddingTop());
mPreviewLayoutRule.init(mBackground.previewSize, mIntrinsicIconSize,
Utilities.isRtl(getResources()));
@@ -407,6 +393,10 @@
mBadgeInfo = badgeInfo;
}
+ public PreviewLayoutRule getLayoutRule() {
+ return mPreviewLayoutRule;
+ }
+
/**
* Sets mBadgeScale to 1 or 0, animating if wasBadged or isBadged is false
* (the badge is being added or removed).
@@ -415,44 +405,19 @@
float newBadgeScale = isBadged ? 1f : 0f;
// Animate when a badge is first added or when it is removed.
if ((wasBadged ^ isBadged) && isShown()) {
- ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
+ createBadgeScaleAnimator(newBadgeScale).start();
} else {
mBadgeScale = newBadgeScale;
invalidate();
}
}
- static class PreviewItemDrawingParams {
- PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
- this.transX = transX;
- this.transY = transY;
- this.scale = scale;
- this.overlayAlpha = overlayAlpha;
- }
+ public Animator createBadgeScaleAnimator(float... badgeScales) {
+ return ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, badgeScales);
+ }
- public void update(float transX, float transY, float scale) {
- // We ensure the update will not interfere with an animation on the layout params
- // If the final values differ, we cancel the animation.
- if (anim != null) {
- if (anim.finalTransX == transX || anim.finalTransY == transY
- || anim.finalScale == scale) {
- return;
- }
- anim.cancel();
- }
-
- this.transX = transX;
- this.transY = transY;
- this.scale = scale;
- }
-
- float transX;
- float transY;
- float scale;
- public float overlayAlpha;
- boolean hidden;
- FolderPreviewItemAnim anim;
- Drawable drawable;
+ public boolean hasBadge() {
+ return mBadgeInfo != null && mBadgeInfo.hasBadge();
}
private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
@@ -464,12 +429,12 @@
float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
- center[0] = (int) Math.round(offsetX);
- center[1] = (int) Math.round(offsetY);
+ center[0] = Math.round(offsetX);
+ center[1] = Math.round(offsetY);
return mTmpParams.scale;
}
- private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+ PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
PreviewItemDrawingParams params) {
// We use an index of -1 to represent an icon on the workspace for the destroy and
// create animations
@@ -496,343 +461,32 @@
Drawable d = params.drawable;
if (d != null) {
- mTempBounds.set(d.getBounds());
- d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
- if (d instanceof FastBitmapDrawable) {
- FastBitmapDrawable fd = (FastBitmapDrawable) d;
- fd.drawWithBrightness(canvas, params.overlayAlpha);
- } else {
- d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
- PorterDuff.Mode.SRC_ATOP);
- d.draw(canvas);
- d.clearColorFilter();
- }
- d.setBounds(mTempBounds);
+ Rect bounds = d.getBounds();
+ canvas.save();
+ canvas.translate(-bounds.left, -bounds.top);
+ canvas.scale(mIntrinsicIconSize / bounds.width(), mIntrinsicIconSize / bounds.height());
+ d.draw(canvas);
+ canvas.restore();
}
canvas.restore();
}
- /**
- * This object represents a FolderIcon preview background. It stores drawing / measurement
- * information, handles drawing, and animation (accept state <--> rest state).
- */
- public static class PreviewBackground {
-
- private final PorterDuffXfermode mClipPorterDuffXfermode
- = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
- // Create a RadialGradient such that it draws a black circle and then extends with
- // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
- // just at the edge quickly change it to transparent.
- private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
- new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
- new float[] {0, 0.999f, 1},
- Shader.TileMode.CLAMP);
-
- private final PorterDuffXfermode mShadowPorterDuffXfermode
- = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
- private RadialGradient mShadowShader = null;
-
- private final Matrix mShaderMatrix = new Matrix();
- private final Path mPath = new Path();
-
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- private float mScale = 1f;
- private float mColorMultiplier = 1f;
- private float mStrokeWidth;
- private View mInvalidateDelegate;
-
- public int previewSize;
- private int basePreviewOffsetX;
- private int basePreviewOffsetY;
-
- private CellLayout mDrawingDelegate;
- public int delegateCellX;
- public int delegateCellY;
-
- // When the PreviewBackground is drawn under an icon (for creating a folder) the border
- // should not occlude the icon
- public boolean isClipping = true;
-
- // Drawing / animation configurations
- private static final float ACCEPT_SCALE_FACTOR = 1.25f;
- private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
-
- // Expressed on a scale from 0 to 255.
- private static final int BG_OPACITY = 160;
- private static final int MAX_BG_OPACITY = 225;
- private static final int BG_INTENSITY = 245;
- private static final int SHADOW_OPACITY = 40;
-
- ValueAnimator mScaleAnimator;
-
- public void setup(DisplayMetrics dm, DeviceProfile grid, View invalidateDelegate,
- int availableSpace, int topPadding) {
- mInvalidateDelegate = invalidateDelegate;
-
- final int previewSize = grid.folderIconSizePx;
- final int previewPadding = grid.folderIconPreviewPadding;
-
- this.previewSize = (previewSize - 2 * previewPadding);
-
- basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
- basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
-
- // Stroke width is 1dp
- mStrokeWidth = dm.density;
-
- float radius = getScaledRadius();
- float shadowRadius = radius + mStrokeWidth;
- int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
- mShadowShader = new RadialGradient(0, 0, 1,
- new int[] {shadowColor, Color.TRANSPARENT},
- new float[] {radius / shadowRadius, 1},
- Shader.TileMode.CLAMP);
-
- invalidate();
- }
-
- int getRadius() {
- return previewSize / 2;
- }
-
- int getScaledRadius() {
- return (int) (mScale * getRadius());
- }
-
- int getOffsetX() {
- return basePreviewOffsetX - (getScaledRadius() - getRadius());
- }
-
- int getOffsetY() {
- return basePreviewOffsetY - (getScaledRadius() - getRadius());
- }
-
- /**
- * Returns the progress of the scale animation, where 0 means the scale is at 1f
- * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
- */
- float getScaleProgress() {
- return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
- }
-
- void invalidate() {
- if (mInvalidateDelegate != null) {
- mInvalidateDelegate.invalidate();
- }
-
- if (mDrawingDelegate != null) {
- mDrawingDelegate.invalidate();
- }
- }
-
- void setInvalidateDelegate(View invalidateDelegate) {
- mInvalidateDelegate = invalidateDelegate;
- invalidate();
- }
-
- public void drawBackground(Canvas canvas) {
- mPaint.setStyle(Paint.Style.FILL);
- int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
- mPaint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
-
- drawCircle(canvas, 0 /* deltaRadius */);
-
- // Draw shadow.
- if (mShadowShader == null) {
- return;
- }
- float radius = getScaledRadius();
- float shadowRadius = radius + mStrokeWidth;
- mPaint.setColor(Color.BLACK);
- int offsetX = getOffsetX();
- int offsetY = getOffsetY();
- final int saveCount;
- if (canvas.isHardwareAccelerated()) {
- saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
- offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
- null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
-
- } else {
- saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
- clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
- }
-
- mShaderMatrix.setScale(shadowRadius, shadowRadius);
- mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
- mShadowShader.setLocalMatrix(mShaderMatrix);
- mPaint.setShader(mShadowShader);
- canvas.drawPaint(mPaint);
- mPaint.setShader(null);
-
- if (canvas.isHardwareAccelerated()) {
- mPaint.setXfermode(mShadowPorterDuffXfermode);
- canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
- mPaint.setXfermode(null);
- }
-
- canvas.restoreToCount(saveCount);
- }
-
- public void drawBackgroundStroke(Canvas canvas) {
- mPaint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(mStrokeWidth);
- drawCircle(canvas, 1 /* deltaRadius */);
- }
-
- public void drawLeaveBehind(Canvas canvas) {
- float originalScale = mScale;
- mScale = 0.5f;
-
- mPaint.setStyle(Paint.Style.FILL);
- mPaint.setColor(Color.argb(160, 245, 245, 245));
- drawCircle(canvas, 0 /* deltaRadius */);
-
- mScale = originalScale;
- }
-
- private void drawCircle(Canvas canvas,float deltaRadius) {
- float radius = getScaledRadius();
- canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
- radius - deltaRadius, mPaint);
- }
-
- // It is the callers responsibility to save and restore the canvas layers.
- private void clipCanvasSoftware(Canvas canvas, Region.Op op) {
- mPath.reset();
- float r = getScaledRadius();
- mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
- canvas.clipPath(mPath, op);
- }
-
- // It is the callers responsibility to save and restore the canvas layers.
- private void clipCanvasHardware(Canvas canvas) {
- mPaint.setColor(Color.BLACK);
- mPaint.setXfermode(mClipPorterDuffXfermode);
-
- float radius = getScaledRadius();
- mShaderMatrix.setScale(radius, radius);
- mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
- mClipShader.setLocalMatrix(mShaderMatrix);
- mPaint.setShader(mClipShader);
- canvas.drawPaint(mPaint);
- mPaint.setXfermode(null);
- mPaint.setShader(null);
- }
-
- private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
- if (mDrawingDelegate != delegate) {
- delegate.addFolderBackground(this);
- }
-
- mDrawingDelegate = delegate;
- delegateCellX = cellX;
- delegateCellY = cellY;
-
- invalidate();
- }
-
- private void clearDrawingDelegate() {
- if (mDrawingDelegate != null) {
- mDrawingDelegate.removeFolderBackground(this);
- }
-
- mDrawingDelegate = null;
- invalidate();
- }
-
- private boolean drawingDelegated() {
- return mDrawingDelegate != null;
- }
-
- private void animateScale(float finalScale, float finalMultiplier,
- final Runnable onStart, final Runnable onEnd) {
- final float scale0 = mScale;
- final float scale1 = finalScale;
-
- final float bgMultiplier0 = mColorMultiplier;
- final float bgMultiplier1 = finalMultiplier;
-
- if (mScaleAnimator != null) {
- mScaleAnimator.cancel();
- }
-
- mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-
- mScaleAnimator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float prog = animation.getAnimatedFraction();
- mScale = prog * scale1 + (1 - prog) * scale0;
- mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
- invalidate();
- }
- });
- mScaleAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- if (onStart != null) {
- onStart.run();
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (onEnd != null) {
- onEnd.run();
- }
- mScaleAnimator = null;
- }
- });
-
- mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
- mScaleAnimator.start();
- }
-
- public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
- Runnable onStart = new Runnable() {
- @Override
- public void run() {
- delegateDrawing(cl, cellX, cellY);
- }
- };
- animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
- }
-
- public void animateToRest() {
- // This can be called multiple times -- we need to make sure the drawing delegate
- // is saved and restored at the beginning of the animation, since cancelling the
- // existing animation can clear the delgate.
- final CellLayout cl = mDrawingDelegate;
- final int cellX = delegateCellX;
- final int cellY = delegateCellY;
-
- Runnable onStart = new Runnable() {
- @Override
- public void run() {
- delegateDrawing(cl, cellX, cellY);
- }
- };
- Runnable onEnd = new Runnable() {
- @Override
- public void run() {
- clearDrawingDelegate();
- }
- };
- animateScale(1f, 1f, onStart, onEnd);
- }
- }
-
public void setFolderBackground(PreviewBackground bg) {
mBackground = bg;
mBackground.setInvalidateDelegate(this);
}
+ public void setBackgroundVisible(boolean visible) {
+ mBackgroundIsVisible = visible;
+ invalidate();
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
+ if (!mBackgroundIsVisible) return;
+
if (mReferenceDrawable != null) {
computePreviewDrawingParams(mReferenceDrawable);
}
@@ -892,89 +546,14 @@
}
}
- class FolderPreviewItemAnim {
- ValueAnimator mValueAnimator;
- float finalScale;
- float finalTransX;
- float finalTransY;
-
- /**
- *
- * @param params layout params to animate
- * @param index0 original index of the item to be animated
- * @param nItems0 original number of items in the preview
- * @param index1 new index of the item to be animated
- * @param nItems1 new number of items in the preview
- * @param duration duration in ms of the animation
- * @param onCompleteRunnable runnable to execute upon animation completion
- */
- public FolderPreviewItemAnim(final PreviewItemDrawingParams params, int index0, int nItems0,
- int index1, int nItems1, int duration, final Runnable onCompleteRunnable) {
-
- computePreviewItemDrawingParams(index1, nItems1, mTmpParams);
-
- finalScale = mTmpParams.scale;
- finalTransX = mTmpParams.transX;
- finalTransY = mTmpParams.transY;
-
- computePreviewItemDrawingParams(index0, nItems0, mTmpParams);
-
- final float scale0 = mTmpParams.scale;
- final float transX0 = mTmpParams.transX;
- final float transY0 = mTmpParams.transY;
-
- mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
- mValueAnimator.addUpdateListener(new AnimatorUpdateListener(){
- public void onAnimationUpdate(ValueAnimator animation) {
- float progress = animation.getAnimatedFraction();
-
- params.transX = transX0 + progress * (finalTransX - transX0);
- params.transY = transY0 + progress * (finalTransY - transY0);
- params.scale = scale0 + progress * (finalScale - scale0);
- invalidate();
- }
- });
-
- mValueAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
- params.anim = null;
- }
- });
- mValueAnimator.setDuration(duration);
- }
-
- public void start() {
- mValueAnimator.start();
- }
-
- public void cancel() {
- mValueAnimator.cancel();
- }
-
- public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
- return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
- finalScale == anim.finalScale;
-
- }
- }
-
private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
final Runnable onCompleteRunnable) {
-
FolderPreviewItemAnim anim;
if (!reverse) {
- anim = new FolderPreviewItemAnim(mDrawingParams.get(0), -1, -1, 0, 2, duration,
+ anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), -1, -1, 0, 2, duration,
onCompleteRunnable);
} else {
- anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
+ anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), 0, 2, -1, -1, duration,
onCompleteRunnable);
}
anim.start();
@@ -992,8 +571,36 @@
return mFolderName.getVisibility() == VISIBLE;
}
+ public List<BubbleTextView> getItemsToDisplay() {
+ mPreviewVerifier.setFolderInfo(mFolder.getInfo());
+
+ List<BubbleTextView> itemsToDisplay = new ArrayList<>();
+ List<View> allItems = mFolder.getItemsInRankOrder();
+ int numItems = allItems.size();
+ for (int rank = 0; rank < numItems; ++rank) {
+ if (mPreviewVerifier.isItemInPreview(rank)) {
+ itemsToDisplay.add((BubbleTextView) allItems.get(rank));
+ }
+
+ if (itemsToDisplay.size() == FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ return itemsToDisplay;
+ }
+
+ @Override
+ protected boolean verifyDrawable(@NonNull Drawable who) {
+ for (int i = 0; i < mDrawingParams.size(); i++) {
+ if (mDrawingParams.get(i).drawable == who) {
+ return true;
+ }
+ }
+ return super.verifyDrawable(who);
+ }
+
private void updateItemDrawingParams(boolean animate) {
- List<View> items = mPreviewLayoutRule.getItemsToDisplay(mFolder);
+ List<BubbleTextView> items = getItemsToDisplay();
int nItemsInPreview = items.size();
int prevNumItems = mDrawingParams.size();
@@ -1008,7 +615,13 @@
for (int i = 0; i < mDrawingParams.size(); i++) {
PreviewItemDrawingParams p = mDrawingParams.get(i);
- p.drawable = ((TextView) items.get(i)).getCompoundDrawables()[1];
+ p.drawable = items.get(i).getCompoundDrawables()[1];
+
+ if (p.drawable != null && !mFolder.isOpen()) {
+ // Set the callback to FolderIcon as it is responsible to drawing the icon. The
+ // callback will be release when the folder is opened.
+ p.drawable.setCallback(this);
+ }
if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
computePreviewItemDrawingParams(i, nItemsInPreview, p);
@@ -1016,7 +629,7 @@
mReferenceDrawable = p.drawable;
}
} else {
- FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
+ FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
if (p.anim != null) {
@@ -1044,7 +657,7 @@
}
@Override
- public void onAdd(ShortcutInfo item) {
+ public void onAdd(ShortcutInfo item, int rank) {
boolean wasBadged = mBadgeInfo.hasBadge();
mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
boolean isBadged = mBadgeInfo.hasBadge();
@@ -1110,28 +723,21 @@
}
public void shrinkAndFadeIn(boolean animate) {
- final CellLayout cl = (CellLayout) getParent().getParent();
- ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
-
// We remove and re-draw the FolderIcon in-case it has changed
final PreviewImageView previewImage = PreviewImageView.get(getContext());
previewImage.removeFromParent();
copyToPreview(previewImage);
- if (cl != null) {
- cl.clearFolderLeaveBehind();
- }
+ clearLeaveBehindIfExists();
ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(previewImage, 1, 1, 1);
oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- if (cl != null) {
- // Remove the ImageView copy of the FolderIcon and make the original visible.
- previewImage.removeFromParent();
- setVisibility(View.VISIBLE);
- }
+ // Remove the ImageView copy of the FolderIcon and make the original visible.
+ previewImage.removeFromParent();
+ setVisibility(View.VISIBLE);
}
});
oa.start();
@@ -1140,7 +746,15 @@
}
}
- public void growAndFadeOut() {
+ public void clearLeaveBehindIfExists() {
+ ((CellLayout.LayoutParams) getLayoutParams()).canReorder = true;
+ if (mInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ CellLayout cl = (CellLayout) getParent().getParent();
+ cl.clearFolderLeaveBehind();
+ }
+ }
+
+ public void drawLeaveBehindIfExists() {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
// While the folder is open, the position of the icon cannot change.
lp.canReorder = false;
@@ -1148,6 +762,10 @@
CellLayout cl = (CellLayout) getParent().getParent();
cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
}
+ }
+
+ public void growAndFadeOut() {
+ drawLeaveBehindIfExists();
// Push an ImageView copy of the FolderIcon into the DragLayer and hide the original
PreviewImageView previewImage = PreviewImageView.get(getContext());
@@ -1172,13 +790,13 @@
}
}
- public interface PreviewLayoutRule {
+ interface PreviewLayoutRule {
PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
PreviewItemDrawingParams params);
- void init(int availableSpace, int intrinsicIconSize, boolean rtl);
+ void init(int availableSpace, float intrinsicIconSize, boolean rtl);
float scaleForItem(int index, int totalNumItems);
+ float getIconSize();
int maxNumItems();
boolean clipToBackground();
- List<View> getItemsToDisplay(Folder folder);
}
}
diff --git a/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.java
new file mode 100644
index 0000000..d0d8e79
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderIconPreviewVerifier.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.folder;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * Verifies whether an item in a Folder is displayed in the FolderIcon preview.
+ */
+public class FolderIconPreviewVerifier {
+
+ public FolderIconPreviewVerifier(InvariantDeviceProfile profile) {
+ // b/37570804
+ }
+
+ public void setFolderInfo(FolderInfo info) {
+ // b/37570804
+ }
+
+ public boolean isItemInPreview(int rank) {
+ return rank < FolderIcon.NUM_ITEMS_IN_PREVIEW;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index eecce18..33ac5ba 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -1,4 +1,4 @@
-/**
+/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -19,6 +19,8 @@
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
@@ -35,21 +37,17 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.keyboard.ViewGroupFocusHelper;
import com.android.launcher3.pageindicators.PageIndicator;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -68,14 +66,14 @@
*/
private static final float SCROLL_HINT_FRACTION = 0.07f;
- private static final int[] sTempPosArray = new int[2];
+ private static final int[] sTmpArray = new int[2];
public final boolean mIsRtl;
private final LayoutInflater mInflater;
private final ViewGroupFocusHelper mFocusIndicatorHelper;
- @Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
+ @Thunk final ArrayMap<View, Runnable> mPendingAnimations = new ArrayMap<>();
@ViewDebug.ExportedProperty(category = "launcher")
private final int mMaxCountX;
@@ -108,52 +106,69 @@
mIsRtl = Utilities.isRtl(getResources());
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- setEdgeGlowColor(Themes.getAttrColor(context, android.R.attr.colorEdgeEffect));
mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
}
public void setFolder(Folder folder) {
mFolder = folder;
mKeyListener = new PagedFolderKeyEventListener(folder);
- mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
+ mPageIndicator = folder.findViewById(R.id.folder_page_indicator);
initParentViews(folder);
}
/**
- * Sets up the grid size such that {@param count} items can fit in the grid.
+ * Calculates the grid size such that {@param count} items can fit in the grid.
* The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
* maintaining the restrictions of {@link #mMaxCountX} & {@link #mMaxCountY}.
*/
- private void setupContentDimensions(int count) {
- mAllocatedContentSize = count;
+ public static void calculateGridSize(int count, int countX, int countY, int maxCountX,
+ int maxCountY, int maxItemsPerPage, int[] out) {
boolean done;
- if (count >= mMaxItemsPerPage) {
- mGridCountX = mMaxCountX;
- mGridCountY = mMaxCountY;
+ int gridCountX = countX;
+ int gridCountY = countY;
+
+ if (count >= maxItemsPerPage) {
+ gridCountX = maxCountX;
+ gridCountY = maxCountY;
done = true;
} else {
done = false;
}
while (!done) {
- int oldCountX = mGridCountX;
- int oldCountY = mGridCountY;
- if (mGridCountX * mGridCountY < count) {
+ int oldCountX = gridCountX;
+ int oldCountY = gridCountY;
+ if (gridCountX * gridCountY < count) {
// Current grid is too small, expand it
- if ((mGridCountX <= mGridCountY || mGridCountY == mMaxCountY) && mGridCountX < mMaxCountX) {
- mGridCountX++;
- } else if (mGridCountY < mMaxCountY) {
- mGridCountY++;
+ if ((gridCountX <= gridCountY || gridCountY == maxCountY)
+ && gridCountX < maxCountX) {
+ gridCountX++;
+ } else if (gridCountY < maxCountY) {
+ gridCountY++;
}
- if (mGridCountY == 0) mGridCountY++;
- } else if ((mGridCountY - 1) * mGridCountX >= count && mGridCountY >= mGridCountX) {
- mGridCountY = Math.max(0, mGridCountY - 1);
- } else if ((mGridCountX - 1) * mGridCountY >= count) {
- mGridCountX = Math.max(0, mGridCountX - 1);
+ if (gridCountY == 0) gridCountY++;
+ } else if ((gridCountY - 1) * gridCountX >= count && gridCountY >= gridCountX) {
+ gridCountY = Math.max(0, gridCountY - 1);
+ } else if ((gridCountX - 1) * gridCountY >= count) {
+ gridCountX = Math.max(0, gridCountX - 1);
}
- done = mGridCountX == oldCountX && mGridCountY == oldCountY;
+ done = gridCountX == oldCountX && gridCountY == oldCountY;
}
+ out[0] = gridCountX;
+ out[1] = gridCountY;
+ }
+
+ /**
+ * Sets up the grid size such that {@param count} items can fit in the grid.
+ */
+ public void setupContentDimensions(int count) {
+ mAllocatedContentSize = count;
+ calculateGridSize(count, mGridCountX, mGridCountY, mMaxCountX, mMaxCountY, mMaxItemsPerPage,
+ sTmpArray);
+ mGridCountX = sTmpArray[0];
+ mGridCountY = sTmpArray[1];
+
// Update grid size
for (int i = getPageCount() - 1; i >= 0; i--) {
getPageAt(i).setGridSize(mGridCountX, mGridCountY);
@@ -171,8 +186,8 @@
* @return list of items that could not be bound, probably because we hit the max size limit.
*/
public ArrayList<ShortcutInfo> bindItems(ArrayList<ShortcutInfo> items) {
- ArrayList<View> icons = new ArrayList<View>();
- ArrayList<ShortcutInfo> extra = new ArrayList<ShortcutInfo>();
+ ArrayList<View> icons = new ArrayList<>();
+ ArrayList<ShortcutInfo> extra = new ArrayList<>();
for (ShortcutInfo item : items) {
if (!ALLOW_FOLDER_SCROLL && icons.size() >= mMaxItemsPerPage) {
@@ -185,21 +200,37 @@
return extra;
}
+ public void allocateSpaceForRank(int rank) {
+ ArrayList<View> views = new ArrayList<>(mFolder.getItemsInRankOrder());
+ views.add(rank, null);
+ arrangeChildren(views, views.size(), false);
+ }
+
+ private ArrayList<View> createListWithViewAtPos(ArrayList<View> list, View v, int position) {
+ ArrayList<View> newList = new ArrayList<>(list.size() + 1);
+ newList.addAll(list);
+ newList.add(position, v);
+ return newList;
+ }
+
/**
- * Create space for a new item at the end, and returns the rank for that item.
+ * Create space for a new item and returns the rank for that item.
* Also sets the current page to the last page.
*/
public int allocateRankForNewItem() {
- int rank = getItemCount();
- ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
- views.add(rank, null);
+ ArrayList<View> rankOrder = mFolder.getItemsInRankOrder();
+ int rank = rankOrder.size();
+
+ ArrayList<View> views = createListWithViewAtPos(rankOrder, null, rank);
arrangeChildren(views, views.size(), false);
+
setCurrentPage(rank / mMaxItemsPerPage);
return rank;
}
public View createAndAddViewForRank(ShortcutInfo item, int rank) {
View icon = createNewView(item);
+ allocateSpaceForRank(rank);
addViewForRank(icon, item, rank);
return icon;
}
@@ -209,25 +240,65 @@
* related attributes. It assumes that {@param item} is already attached to the view.
*/
public void addViewForRank(View view, ShortcutInfo item, int rank) {
- int pagePos = rank % mMaxItemsPerPage;
- int pageNo = rank / mMaxItemsPerPage;
+ updateShortcutInfoWithRank(item, rank);
- item.rank = rank;
- item.cellX = pagePos % mGridCountX;
- item.cellY = pagePos / mGridCountX;
+ ArrayList<View> views = createListWithViewAtPos(mFolder.getItemsInRankOrder(), view, rank);
+ arrangeChildren(views, views.size(), false);
+ }
+
+ /**
+ * Similar to {@link #addViewForRank(View, ShortcutInfo, int)}}, but specific to real time
+ * reorder.
+ *
+ * The difference here is that during real time reorder, we are moving the Views in a contained
+ * order.
+ */
+ public void addViewForRankDuringReorder(View view, ShortcutInfo item, int rank) {
+ updateShortcutInfoWithRank(item, rank);
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
lp.cellX = item.cellX;
lp.cellY = item.cellY;
+
+ int pageNo = rank / mMaxItemsPerPage;
getPageAt(pageNo).addViewToCellLayout(
view, -1, mFolder.mLauncher.getViewIdForItem(item), lp, true);
}
+ /**
+ * Similar to {@link #addViewForRank(View, ShortcutInfo, int)}, but specific to drag and drop.
+ *
+ * The difference is that we handle the drag and drop by adjusting the reading order of the
+ * children, rather than based on their rank.
+ */
+ public void addViewForRankDuringDragAndDrop(View view, ShortcutInfo item, int readingRank) {
+ updateShortcutInfoWithRank(item, readingRank);
+
+ ArrayList<View> views = createListWithViewAtPos(mFolder.getItemsInReadingOrder(), view,
+ readingRank);
+
+ int numItems = views.size();
+ ArrayList<View> rankOrder = new ArrayList<>(numItems);
+ for (int i = 0; i < numItems; ++i) {
+ rankOrder.add(views.get(getReadingOrderPosForRank(i)));
+ }
+
+ arrangeChildren(rankOrder, numItems, false);
+ }
+
+ private void updateShortcutInfoWithRank(ShortcutInfo info, int rank) {
+ info.rank = rank;
+ getCellXYPositionForRank(rank, sTmpArray);
+ info.cellX = sTmpArray[0];
+ info.cellY = sTmpArray[1];
+ }
+
@SuppressLint("InflateParams")
public View createNewView(ShortcutInfo item) {
final BubbleTextView textView = (BubbleTextView) mInflater.inflate(
R.layout.folder_application, null, false);
textView.applyFromShortcutInfo(item);
+ textView.setHapticFeedbackEnabled(false);
textView.setOnClickListener(mFolder);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorHelper);
@@ -289,17 +360,18 @@
* It essentially removes all views from all the pages and then adds them again in appropriate
* page.
*
- * @param list the ordered list of children.
+ * @param rankOrderedList the rank-ordered list of children.
* @param itemCount if greater than the total children count, empty spaces are left
* at the end, otherwise it is ignored.
*
*/
- public void arrangeChildren(ArrayList<View> list, int itemCount) {
- arrangeChildren(list, itemCount, true);
+ public void arrangeChildren(ArrayList<View> rankOrderedList, int itemCount) {
+ arrangeChildren(rankOrderedList, itemCount, true);
}
@SuppressLint("RtlHardcoded")
- private void arrangeChildren(ArrayList<View> list, int itemCount, boolean saveChanges) {
+ private void arrangeChildren(ArrayList<View> rankOrderedList, int itemCount,
+ boolean saveChanges) {
ArrayList<CellLayout> pages = new ArrayList<CellLayout>();
for (int i = 0; i < getChildCount(); i++) {
CellLayout page = (CellLayout) getChildAt(i);
@@ -314,9 +386,11 @@
int position = 0;
int newX, newY, rank;
+ FolderIconPreviewVerifier verifier = new FolderIconPreviewVerifier(
+ Launcher.getLauncher(getContext()).getDeviceProfile().inv);
rank = 0;
for (int i = 0; i < itemCount; i++) {
- View v = list.size() > i ? list.get(i) : null;
+ View v = rankOrderedList.size() > i ? rankOrderedList.get(i) : null;
if (currentPage == null || position >= mMaxItemsPerPage) {
// Next page
if (pageItr.hasNext()) {
@@ -329,8 +403,10 @@
if (v != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) v.getLayoutParams();
- newX = position % mGridCountX;
- newY = position / mGridCountX;
+ getCellXYPositionForRank(rank, sTmpArray);
+ newX = sTmpArray[0];
+ newY = sTmpArray[1];
+
ItemInfo info = (ItemInfo) v.getTag();
if (info.cellX != newX || info.cellY != newY || info.rank != rank) {
info.cellX = newX;
@@ -346,7 +422,7 @@
currentPage.addViewToCellLayout(
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
- if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) {
+ if (verifier.isItemInPreview(rank) && v instanceof BubbleTextView) {
((BubbleTextView) v).verifyHighRes();
}
}
@@ -400,12 +476,12 @@
public int findNearestArea(int pixelX, int pixelY) {
int pageIndex = getNextPage();
CellLayout page = getPageAt(pageIndex);
- page.findNearestArea(pixelX, pixelY, 1, 1, sTempPosArray);
+ page.findNearestArea(pixelX, pixelY, 1, 1, sTmpArray);
if (mFolder.isLayoutRtl()) {
- sTempPosArray[0] = page.getCountX() - sTempPosArray[0] - 1;
+ sTmpArray[0] = page.getCountX() - sTmpArray[0] - 1;
}
return Math.min(mAllocatedContentSize - 1,
- pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]);
+ pageIndex * mMaxItemsPerPage + sTmpArray[1] * mGridCountX + sTmpArray[0]);
}
public boolean isFull() {
@@ -471,8 +547,8 @@
}
@Override
- protected void notifyPageSwitchListener() {
- super.notifyPageSwitchListener();
+ protected void notifyPageSwitchListener(int prevPage) {
+ super.notifyPageSwitchListener(prevPage);
if (mFolder != null) {
mFolder.updateTextViewFocus();
}
@@ -505,7 +581,7 @@
*/
public void completePendingPageChanges() {
if (!mPendingAnimations.isEmpty()) {
- HashMap<View, Runnable> pendingViews = new HashMap<>(mPendingAnimations);
+ ArrayMap<View, Runnable> pendingViews = new ArrayMap<>(mPendingAnimations);
for (Map.Entry<View, Runnable> e : pendingViews.entrySet()) {
e.getKey().animate().cancel();
e.getValue().run();
@@ -534,7 +610,14 @@
if (page != null) {
ShortcutAndWidgetContainer parent = page.getShortcutsAndWidgets();
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
- ((BubbleTextView) parent.getChildAt(i)).verifyHighRes();
+ BubbleTextView icon = ((BubbleTextView) parent.getChildAt(i));
+ icon.verifyHighRes();
+ // Set the callback back to the actual icon, in case
+ // it was captured by the FolderIcon
+ Drawable d = icon.getCompoundDrawables()[1];
+ if (d != null) {
+ d.setCallback(icon);
+ }
}
}
}
@@ -621,7 +704,7 @@
if (v != null) {
if (pageToAnimate != p) {
page.removeView(v);
- addViewForRank(v, (ShortcutInfo) v.getTag(), moveStart);
+ addViewForRankDuringReorder(v, (ShortcutInfo) v.getTag(), moveStart);
} else {
// Do a fake animation before removing it.
final int newRank = moveStart;
@@ -634,14 +717,14 @@
mPendingAnimations.remove(v);
v.setTranslationX(oldTranslateX);
((CellLayout) v.getParent().getParent()).removeView(v);
- addViewForRank(v, (ShortcutInfo) v.getTag(), newRank);
+ addViewForRankDuringReorder(v, (ShortcutInfo) v.getTag(), newRank);
}
};
v.animate()
- .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
- .setDuration(REORDER_ANIMATION_DURATION)
- .setStartDelay(0)
- .withEndAction(endAction);
+ .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
+ .setDuration(REORDER_ANIMATION_DURATION)
+ .setStartDelay(0)
+ .withEndAction(endAction);
mPendingAnimations.put(v, endAction);
}
}
@@ -672,9 +755,101 @@
return mMaxItemsPerPage;
}
- @Override
- protected void getEdgeVerticalPosition(int[] pos) {
- pos[0] = 0;
- pos[1] = getViewportHeight();
+ public int getReadingOrderPosForRank(int rank) {
+ return getReadingOrderPosForRank(rank, mMaxItemsPerPage, mGridCountX, sTmpArray);
+ }
+
+ /**
+ * Returns the reading order position for a given rank.
+ *
+ * ie. For the permutation below, rank 0 returns 0, rank 1 returns 1, rank 4 returns 2,
+ * rank 2 returns 3, rank 3 returns 4, rank 5 returns 5.
+ *
+ * R0 R1 R4
+ * R2 R3 R5
+ *
+ * @param outXY If notnull, we also return the cell X/Y position.
+ */
+ public static int getReadingOrderPosForRank(int rank, int maxItemsPerPage, int gridX,
+ int[] outXY) {
+ outXY = outXY == null ? sTmpArray : outXY;
+ getCellXYPositionForRank(rank, maxItemsPerPage, gridX, outXY);
+
+ if (rank >= maxItemsPerPage) {
+ return rank;
+ }
+
+ return outXY[0] + (gridX * outXY[1]);
+ }
+
+ public void getCellXYPositionForRank(int rank, int[] outXY) {
+ getCellXYPositionForRank(rank, mMaxItemsPerPage, mGridCountX, outXY);
+ }
+
+ /**
+ * Returns the cell XY position for a Folder item with the given rank.
+ */
+ public static void getCellXYPositionForRank(int rank, int maxItemsPerPage, int gridX,
+ int[] outXY) {
+ boolean onFirstPage = rank < maxItemsPerPage;
+
+ if (onFirstPage && gridX == 3) {
+ outXY[0] = FolderPermutation.THREE_COLS[rank][0];
+ outXY[1] = FolderPermutation.THREE_COLS[rank][1];
+ } else if (onFirstPage && gridX == 4) {
+ outXY[0] = FolderPermutation.FOUR_COLS[rank][0];
+ outXY[1] = FolderPermutation.FOUR_COLS[rank][1];
+ } else if (onFirstPage && gridX == 5) {
+ outXY[0] = FolderPermutation.FIVE_COLS[rank][0];
+ outXY[1] = FolderPermutation.FIVE_COLS[rank][1];
+ } else {
+ outXY[0] = (rank % maxItemsPerPage) % gridX;
+ outXY[1] = (rank % maxItemsPerPage) / gridX;
+ }
+ }
+
+ /**
+ * Provides the mapping between a folder item's rank and its cell location, based on the
+ * number of columns.
+ *
+ * We use this mapping, rather than the regular reading order, to preserve the items in the
+ * upper left quadrant of the Folder. This allows a smooth transition between the FolderIcon
+ * and the opened Folder.
+ *
+ * TODO: We will replace these hard coded tables with an algorithm b/62986680
+ */
+ private static class FolderPermutation {
+ /**
+ * R0 R1 R4
+ * R2 R3 R5
+ * R6 R7 R8
+ */
+ static final int[][] THREE_COLS = new int[][] {
+ {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {0, 2}, {1, 2}, {2, 2}
+ };
+
+ /**
+ * R0 R1 R4 R6
+ * R2 R3 R5 R7
+ * R8 R9 R10 R11
+ * R12 R13 R14 R15
+ */
+ static final int[][] FOUR_COLS = new int[][] {
+ {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {3, 1}, {0, 2}, {1, 2},
+ {2, 2}, {3, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}
+ };
+
+ /**
+ * R0 R1 R4 R6 R12
+ * R2 R3 R5 R7 R13
+ * R8 R9 R10 R11 R14
+ * R15 R16 R17 R18 R19
+ * R20 R21 R22 R23 R24
+ */
+ static final int[][] FIVE_COLS = new int[][] {
+ {0, 0}, {1, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 1}, {3, 0}, {3, 1}, {0, 2}, {1, 2},
+ {2, 2}, {3, 2}, {4, 0}, {4, 1}, {4, 2}, {0, 3}, {1, 3}, {2, 3}, {3, 3}, {4, 3},
+ {0, 4}, {1, 4}, {2, 4}, {3, 4}, {4, 4}
+ };
}
}
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
new file mode 100644
index 0000000..0da7c5c
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.LauncherAnimUtils;
+
+/**
+ * Animates a Folder preview item.
+ */
+class FolderPreviewItemAnim {
+ private ValueAnimator mValueAnimator;
+
+ float finalScale;
+ float finalTransX;
+ float finalTransY;
+
+ private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+ /**
+ * @param folderIcon The FolderIcon this preview will be drawn in.
+ * @param params layout params to animate
+ * @param index0 original index of the item to be animated
+ * @param items0 original number of items in the preview
+ * @param index1 new index of the item to be animated
+ * @param items1 new number of items in the preview
+ * @param duration duration in ms of the animation
+ * @param onCompleteRunnable runnable to execute upon animation completion
+ */
+ FolderPreviewItemAnim(final FolderIcon folderIcon, final PreviewItemDrawingParams params,
+ int index0, int items0, int index1, int items1, int duration,
+ final Runnable onCompleteRunnable) {
+ folderIcon.computePreviewItemDrawingParams(index1, items1, mTmpParams);
+
+ finalScale = mTmpParams.scale;
+ finalTransX = mTmpParams.transX;
+ finalTransY = mTmpParams.transY;
+
+ folderIcon.computePreviewItemDrawingParams(index0, items0, mTmpParams);
+
+ final float scale0 = mTmpParams.scale;
+ final float transX0 = mTmpParams.transX;
+ final float transY0 = mTmpParams.transY;
+
+ mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+ mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float progress = animation.getAnimatedFraction();
+
+ params.transX = transX0 + progress * (finalTransX - transX0);
+ params.transY = transY0 + progress * (finalTransY - transY0);
+ params.scale = scale0 + progress * (finalScale - scale0);
+ folderIcon.invalidate();
+ }
+ });
+ mValueAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
+ }
+ params.anim = null;
+ }
+ });
+ mValueAnimator.setDuration(duration);
+ }
+
+ public void start() {
+ mValueAnimator.start();
+ }
+
+ public void cancel() {
+ mValueAnimator.cancel();
+ }
+
+ public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
+ return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
+ finalScale == anim.finalScale;
+
+ }
+}
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
new file mode 100644
index 0000000..eb6a6d5
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RadialGradient;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.support.v4.graphics.ColorUtils;
+import android.util.Property;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.util.Themes;
+
+/**
+ * This object represents a FolderIcon preview background. It stores drawing / measurement
+ * information, handles drawing, and animation (accept state <--> rest state).
+ */
+public class PreviewBackground {
+
+ private static final int CONSUMPTION_ANIMATION_DURATION = 100;
+
+ private final PorterDuffXfermode mClipPorterDuffXfermode
+ = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
+ // Create a RadialGradient such that it draws a black circle and then extends with
+ // transparent. To achieve this, we keep the gradient to black for the range [0, 1) and
+ // just at the edge quickly change it to transparent.
+ private final RadialGradient mClipShader = new RadialGradient(0, 0, 1,
+ new int[] {Color.BLACK, Color.BLACK, Color.TRANSPARENT },
+ new float[] {0, 0.999f, 1},
+ Shader.TileMode.CLAMP);
+
+ private final PorterDuffXfermode mShadowPorterDuffXfermode
+ = new PorterDuffXfermode(PorterDuff.Mode.DST_OUT);
+ private RadialGradient mShadowShader = null;
+
+ private final Matrix mShaderMatrix = new Matrix();
+ private final Path mPath = new Path();
+
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+ float mScale = 1f;
+ private float mColorMultiplier = 1f;
+ private int mBgColor;
+ private float mStrokeWidth;
+ private int mStrokeAlpha = MAX_BG_OPACITY;
+ private int mShadowAlpha = 255;
+ private View mInvalidateDelegate;
+
+ int previewSize;
+ int basePreviewOffsetX;
+ int basePreviewOffsetY;
+
+ private CellLayout mDrawingDelegate;
+ public int delegateCellX;
+ public int delegateCellY;
+
+ // When the PreviewBackground is drawn under an icon (for creating a folder) the border
+ // should not occlude the icon
+ public boolean isClipping = true;
+
+ // Drawing / animation configurations
+ private static final float ACCEPT_SCALE_FACTOR = 1.20f;
+ private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
+
+ // Expressed on a scale from 0 to 255.
+ private static final int BG_OPACITY = 160;
+ private static final int MAX_BG_OPACITY = 225;
+ private static final int SHADOW_OPACITY = 40;
+
+ private ValueAnimator mScaleAnimator;
+ private ObjectAnimator mStrokeAlphaAnimator;
+ private ObjectAnimator mShadowAnimator;
+
+ private static final Property<PreviewBackground, Integer> STROKE_ALPHA =
+ new Property<PreviewBackground, Integer>(Integer.class, "strokeAlpha") {
+ @Override
+ public Integer get(PreviewBackground previewBackground) {
+ return previewBackground.mStrokeAlpha;
+ }
+
+ @Override
+ public void set(PreviewBackground previewBackground, Integer alpha) {
+ previewBackground.mStrokeAlpha = alpha;
+ previewBackground.invalidate();
+ }
+ };
+
+ private static final Property<PreviewBackground, Integer> SHADOW_ALPHA =
+ new Property<PreviewBackground, Integer>(Integer.class, "shadowAlpha") {
+ @Override
+ public Integer get(PreviewBackground previewBackground) {
+ return previewBackground.mShadowAlpha;
+ }
+
+ @Override
+ public void set(PreviewBackground previewBackground, Integer alpha) {
+ previewBackground.mShadowAlpha = alpha;
+ previewBackground.invalidate();
+ }
+ };
+
+ public void setup(Launcher launcher, View invalidateDelegate,
+ int availableSpace, int topPadding) {
+ mInvalidateDelegate = invalidateDelegate;
+ mBgColor = Themes.getAttrColor(launcher, android.R.attr.colorPrimary);
+
+ DeviceProfile grid = launcher.getDeviceProfile();
+ final int previewSize = grid.folderIconSizePx;
+ final int previewPadding = grid.folderIconPreviewPadding;
+
+ this.previewSize = (previewSize - 2 * previewPadding);
+
+ basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
+ basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+
+ // Stroke width is 1dp
+ mStrokeWidth = launcher.getResources().getDisplayMetrics().density;
+
+ float radius = getScaledRadius();
+ float shadowRadius = radius + mStrokeWidth;
+ int shadowColor = Color.argb(SHADOW_OPACITY, 0, 0, 0);
+ mShadowShader = new RadialGradient(0, 0, 1,
+ new int[] {shadowColor, Color.TRANSPARENT},
+ new float[] {radius / shadowRadius, 1},
+ Shader.TileMode.CLAMP);
+
+ invalidate();
+ }
+
+ int getRadius() {
+ return previewSize / 2;
+ }
+
+ int getScaledRadius() {
+ return (int) (mScale * getRadius());
+ }
+
+ int getOffsetX() {
+ return basePreviewOffsetX - (getScaledRadius() - getRadius());
+ }
+
+ int getOffsetY() {
+ return basePreviewOffsetY - (getScaledRadius() - getRadius());
+ }
+
+ /**
+ * Returns the progress of the scale animation, where 0 means the scale is at 1f
+ * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
+ */
+ float getScaleProgress() {
+ return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
+ }
+
+ void invalidate() {
+ if (mInvalidateDelegate != null) {
+ mInvalidateDelegate.invalidate();
+ }
+
+ if (mDrawingDelegate != null) {
+ mDrawingDelegate.invalidate();
+ }
+ }
+
+ void setInvalidateDelegate(View invalidateDelegate) {
+ mInvalidateDelegate = invalidateDelegate;
+ invalidate();
+ }
+
+ public void drawBackground(Canvas canvas) {
+ mPaint.setStyle(Paint.Style.FILL);
+ int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+ mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, alpha));
+
+ drawCircle(canvas, 0 /* deltaRadius */);
+
+ // Draw shadow.
+ if (mShadowShader == null) {
+ return;
+ }
+ float radius = getScaledRadius();
+ float shadowRadius = radius + mStrokeWidth;
+ mPaint.setColor(Color.BLACK);
+ int offsetX = getOffsetX();
+ int offsetY = getOffsetY();
+ final int saveCount;
+ if (canvas.isHardwareAccelerated()) {
+ saveCount = canvas.saveLayer(offsetX - mStrokeWidth, offsetY,
+ offsetX + radius + shadowRadius, offsetY + shadowRadius + shadowRadius,
+ null, Canvas.CLIP_TO_LAYER_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
+
+ } else {
+ saveCount = canvas.save(Canvas.CLIP_SAVE_FLAG);
+ clipCanvasSoftware(canvas, Region.Op.DIFFERENCE);
+ }
+
+ mShaderMatrix.setScale(shadowRadius, shadowRadius);
+ mShaderMatrix.postTranslate(radius + offsetX, shadowRadius + offsetY);
+ mShadowShader.setLocalMatrix(mShaderMatrix);
+ mPaint.setAlpha(mShadowAlpha);
+ mPaint.setShader(mShadowShader);
+ canvas.drawPaint(mPaint);
+ mPaint.setAlpha(255);
+ mPaint.setShader(null);
+ if (canvas.isHardwareAccelerated()) {
+ mPaint.setXfermode(mShadowPorterDuffXfermode);
+ canvas.drawCircle(radius + offsetX, radius + offsetY, radius, mPaint);
+ mPaint.setXfermode(null);
+ }
+
+ canvas.restoreToCount(saveCount);
+ }
+
+ public void fadeInBackgroundShadow() {
+ if (mShadowAnimator != null) {
+ mShadowAnimator.cancel();
+ }
+ mShadowAnimator = ObjectAnimator
+ .ofInt(this, SHADOW_ALPHA, 0, 255)
+ .setDuration(100);
+ mShadowAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mShadowAnimator = null;
+ }
+ });
+ mShadowAnimator.start();
+ }
+
+ public void animateBackgroundStroke() {
+ if (mStrokeAlphaAnimator != null) {
+ mStrokeAlphaAnimator.cancel();
+ }
+ mStrokeAlphaAnimator = ObjectAnimator
+ .ofInt(this, STROKE_ALPHA, MAX_BG_OPACITY / 2, MAX_BG_OPACITY)
+ .setDuration(100);
+ mStrokeAlphaAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mStrokeAlphaAnimator = null;
+ }
+ });
+ mStrokeAlphaAnimator.start();
+ }
+
+ public void drawBackgroundStroke(Canvas canvas) {
+ mPaint.setColor(ColorUtils.setAlphaComponent(mBgColor, mStrokeAlpha));
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeWidth(mStrokeWidth);
+ drawCircle(canvas, 1 /* deltaRadius */);
+ }
+
+ public void drawLeaveBehind(Canvas canvas) {
+ float originalScale = mScale;
+ mScale = 0.5f;
+
+ mPaint.setStyle(Paint.Style.FILL);
+ mPaint.setColor(Color.argb(160, 245, 245, 245));
+ drawCircle(canvas, 0 /* deltaRadius */);
+
+ mScale = originalScale;
+ }
+
+ private void drawCircle(Canvas canvas,float deltaRadius) {
+ float radius = getScaledRadius();
+ canvas.drawCircle(radius + getOffsetX(), radius + getOffsetY(),
+ radius - deltaRadius, mPaint);
+ }
+
+ // It is the callers responsibility to save and restore the canvas layers.
+ void clipCanvasSoftware(Canvas canvas, Region.Op op) {
+ mPath.reset();
+ float r = getScaledRadius();
+ mPath.addCircle(r + getOffsetX(), r + getOffsetY(), r, Path.Direction.CW);
+ canvas.clipPath(mPath, op);
+ }
+
+ // It is the callers responsibility to save and restore the canvas layers.
+ void clipCanvasHardware(Canvas canvas) {
+ mPaint.setColor(Color.BLACK);
+ mPaint.setXfermode(mClipPorterDuffXfermode);
+
+ float radius = getScaledRadius();
+ mShaderMatrix.setScale(radius, radius);
+ mShaderMatrix.postTranslate(radius + getOffsetX(), radius + getOffsetY());
+ mClipShader.setLocalMatrix(mShaderMatrix);
+ mPaint.setShader(mClipShader);
+ canvas.drawPaint(mPaint);
+ mPaint.setXfermode(null);
+ mPaint.setShader(null);
+ }
+
+ private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
+ if (mDrawingDelegate != delegate) {
+ delegate.addFolderBackground(this);
+ }
+
+ mDrawingDelegate = delegate;
+ delegateCellX = cellX;
+ delegateCellY = cellY;
+
+ invalidate();
+ }
+
+ private void clearDrawingDelegate() {
+ if (mDrawingDelegate != null) {
+ mDrawingDelegate.removeFolderBackground(this);
+ }
+
+ mDrawingDelegate = null;
+ invalidate();
+ }
+
+ boolean drawingDelegated() {
+ return mDrawingDelegate != null;
+ }
+
+ private void animateScale(float finalScale, float finalMultiplier,
+ final Runnable onStart, final Runnable onEnd) {
+ final float scale0 = mScale;
+ final float scale1 = finalScale;
+
+ final float bgMultiplier0 = mColorMultiplier;
+ final float bgMultiplier1 = finalMultiplier;
+
+ if (mScaleAnimator != null) {
+ mScaleAnimator.cancel();
+ }
+
+ mScaleAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+
+ mScaleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ float prog = animation.getAnimatedFraction();
+ mScale = prog * scale1 + (1 - prog) * scale0;
+ mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
+ invalidate();
+ }
+ });
+ mScaleAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ if (onStart != null) {
+ onStart.run();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (onEnd != null) {
+ onEnd.run();
+ }
+ mScaleAnimator = null;
+ }
+ });
+
+ mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
+ mScaleAnimator.start();
+ }
+
+ public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
+ Runnable onStart = new Runnable() {
+ @Override
+ public void run() {
+ delegateDrawing(cl, cellX, cellY);
+ }
+ };
+ animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
+ }
+
+ public void animateToRest() {
+ // This can be called multiple times -- we need to make sure the drawing delegate
+ // is saved and restored at the beginning of the animation, since cancelling the
+ // existing animation can clear the delgate.
+ final CellLayout cl = mDrawingDelegate;
+ final int cellX = delegateCellX;
+ final int cellY = delegateCellY;
+
+ Runnable onStart = new Runnable() {
+ @Override
+ public void run() {
+ delegateDrawing(cl, cellX, cellY);
+ }
+ };
+ Runnable onEnd = new Runnable() {
+ @Override
+ public void run() {
+ clearDrawingDelegate();
+ }
+ };
+ animateScale(1f, 1f, onStart, onEnd);
+ }
+
+ public int getBackgroundAlpha() {
+ return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+ }
+
+ public float getStrokeWidth() {
+ return mStrokeWidth;
+ }
+}
diff --git a/src/com/android/launcher3/folder/PreviewImageView.java b/src/com/android/launcher3/folder/PreviewImageView.java
index c4f3ee1..65d9db1 100644
--- a/src/com/android/launcher3/folder/PreviewImageView.java
+++ b/src/com/android/launcher3/folder/PreviewImageView.java
@@ -22,7 +22,6 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.ImageView;
import com.android.launcher3.Launcher;
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
new file mode 100644
index 0000000..607b7ca
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Manages the parameters used to draw a Folder preview item.
+ */
+class PreviewItemDrawingParams {
+ float transX;
+ float transY;
+ float scale;
+ float overlayAlpha;
+ FolderPreviewItemAnim anim;
+ public boolean hidden;
+ Drawable drawable;
+
+ PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+ this.transX = transX;
+ this.transY = transY;
+ this.scale = scale;
+ this.overlayAlpha = overlayAlpha;
+ }
+
+ public void update(float transX, float transY, float scale) {
+ // We ensure the update will not interfere with an animation on the layout params
+ // If the final values differ, we cancel the animation.
+ if (anim != null) {
+ if (anim.finalTransX == transX || anim.finalTransY == transY
+ || anim.finalScale == scale) {
+ return;
+ }
+ anim.cancel();
+ }
+
+ this.transX = transX;
+ this.transY = transY;
+ this.scale = scale;
+ }
+}
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 297203a..138dc1c 100644
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -16,12 +16,6 @@
package com.android.launcher3.folder;
-import android.view.View;
-
-import com.android.launcher3.folder.FolderIcon.PreviewItemDrawingParams;
-
-import java.util.List;
-
public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
static final int MAX_NUM_ITEMS_IN_PREVIEW = 3;
@@ -39,7 +33,7 @@
private float mMaxPerspectiveShift;
@Override
- public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+ public void init(int availableSpace, float intrinsicIconSize, boolean rtl) {
mAvailableSpaceInPreview = availableSpace;
// cos(45) = 0.707 + ~= 0.1) = 0.8f
@@ -87,6 +81,11 @@
}
@Override
+ public float getIconSize() {
+ return mBaselineIconSize;
+ }
+
+ @Override
public float scaleForItem(int index, int numItems) {
// Scale is determined by the position of the icon in the preview.
index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1;
@@ -98,10 +97,4 @@
public boolean clipToBackground() {
return false;
}
-
- @Override
- public List<View> getItemsToDisplay(Folder folder) {
- List<View> items = folder.getItemsInReadingOrder();
- return items.subList(0, Math.min(items.size(), MAX_NUM_ITEMS_IN_PREVIEW));
- }
}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index bb136f7..10e91c0 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -23,13 +23,12 @@
import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.view.View;
-import android.widget.TextView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetHostView;
import com.android.launcher3.R;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
/**
@@ -57,8 +56,8 @@
blurSizeOutline =
context.getResources().getDimensionPixelSize(R.dimen.blur_size_medium_outline);
- if (mView instanceof TextView) {
- Drawable d = Workspace.getTextViewIcon((TextView) mView);
+ if (mView instanceof BubbleTextView) {
+ Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
previewPadding = blurSizeOutline - bounds.left - bounds.top;
} else {
@@ -71,8 +70,8 @@
*/
private void drawDragView(Canvas destCanvas) {
destCanvas.save();
- if (mView instanceof TextView) {
- Drawable d = Workspace.getTextViewIcon((TextView) mView);
+ if (mView instanceof BubbleTextView) {
+ Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
destCanvas.translate(blurSizeOutline / 2 - bounds.left,
blurSizeOutline / 2 - bounds.top);
@@ -112,8 +111,8 @@
int width = mView.getWidth();
int height = mView.getHeight();
- if (mView instanceof TextView) {
- Drawable d = Workspace.getTextViewIcon((TextView) mView);
+ if (mView instanceof BubbleTextView) {
+ Drawable d = ((BubbleTextView) mView).getIcon();
Rect bounds = getDrawableBounds(d);
width = bounds.width();
height = bounds.height();
@@ -138,7 +137,7 @@
}
public final void generateDragOutline(Canvas canvas) {
- if (ProviderConfig.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && generatedDragOutline != null) {
throw new RuntimeException("Drag outline generated twice");
}
diff --git a/src/com/android/launcher3/graphics/DrawableFactory.java b/src/com/android/launcher3/graphics/DrawableFactory.java
index 60bbce4..45344c0 100644
--- a/src/com/android/launcher3/graphics/DrawableFactory.java
+++ b/src/com/android/launcher3/graphics/DrawableFactory.java
@@ -28,16 +28,14 @@
import android.os.Process;
import android.os.UserHandle;
import android.support.annotation.UiThread;
+import android.util.ArrayMap;
import android.util.Log;
-
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
-import java.util.HashMap;
-
/**
* Factory for creating new drawables.
*/
@@ -61,7 +59,7 @@
}
protected final UserHandle mMyUser = Process.myUserHandle();
- protected final HashMap<UserHandle, Bitmap> mUserBadges = new HashMap<>();
+ protected final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
/**
* Returns a FastBitmapDrawable with the icon.
diff --git a/src/com/android/launcher3/graphics/FastScrollThumbDrawable.java b/src/com/android/launcher3/graphics/FastScrollThumbDrawable.java
new file mode 100644
index 0000000..6ebc74e
--- /dev/null
+++ b/src/com/android/launcher3/graphics/FastScrollThumbDrawable.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.launcher3.graphics;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+public class FastScrollThumbDrawable extends Drawable {
+
+ private static final Matrix sMatrix = new Matrix();
+
+ private final Path mPath = new Path();
+ private final Paint mPaint;
+ private final boolean mIsRtl;
+
+ public FastScrollThumbDrawable(Paint paint, boolean isRtl) {
+ mPaint = paint;
+ mIsRtl = isRtl;
+ }
+
+ @Override
+ public void getOutline(Outline outline) {
+ if (mPath.isConvex()) {
+ outline.setConvexPath(mPath);
+ }
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mPath.reset();
+
+ float r = bounds.height() * 0.5f;
+ // The path represents a rotate tear-drop shape, with radius of one corner is 1/5th of the
+ // other 3 corners.
+ float diameter = 2 * r;
+ float r2 = r / 5;
+ mPath.addRoundRect(bounds.left, bounds.top, bounds.left + diameter, bounds.top + diameter,
+ new float[] {r, r, r, r, r2, r2, r, r},
+ Path.Direction.CCW);
+
+ sMatrix.setRotate(-45, bounds.left + r, bounds.top + r);
+ if (mIsRtl) {
+ sMatrix.postTranslate(bounds.width(), 0);
+ sMatrix.postScale(-1, 1, bounds.width(), 0);
+ }
+ mPath.transform(sMatrix);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.drawPath(mPath, mPaint);
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ // Not supported
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // Not supported
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+}
diff --git a/src/com/android/launcher3/graphics/GradientView.java b/src/com/android/launcher3/graphics/GradientView.java
new file mode 100644
index 0000000..9dd9504
--- /dev/null
+++ b/src/com/android/launcher3/graphics/GradientView.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RadialGradient;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
+
+/**
+ * Draws a translucent radial gradient background from an initial state with progress 0.0 to a
+ * final state with progress 1.0;
+ */
+public class GradientView extends View implements WallpaperColorInfo.OnChangeListener {
+
+ private static final int DEFAULT_COLOR = Color.WHITE;
+ private static final float GRADIENT_ALPHA_MASK_LENGTH_DP = 300;
+ private static final boolean DEBUG = false;
+
+ private final Bitmap mFinalGradientMask;
+ private final Bitmap mAlphaGradientMask;
+
+ private int mColor1 = DEFAULT_COLOR;
+ private int mColor2 = DEFAULT_COLOR;
+ private int mWidth;
+ private int mHeight;
+ private final RectF mAlphaMaskRect = new RectF();
+ private final RectF mFinalMaskRect = new RectF();
+ private final Paint mPaint = new Paint();
+ private float mProgress;
+ private final int mMaskHeight;
+ private final Context mAppContext;
+ private final Paint mDebugPaint = DEBUG ? new Paint() : null;
+ private final Interpolator mAccelerator = new AccelerateInterpolator();
+ private final float mAlphaStart;
+ private final WallpaperColorInfo mWallpaperColorInfo;
+
+ public GradientView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ this.mAppContext = context.getApplicationContext();
+ this.mMaskHeight = Utilities.pxFromDp(GRADIENT_ALPHA_MASK_LENGTH_DP,
+ mAppContext.getResources().getDisplayMetrics());
+ Launcher launcher = Launcher.getLauncher(context);
+ this.mAlphaStart = launcher.getDeviceProfile().isVerticalBarLayout() ? 0 : 100;
+ this.mWallpaperColorInfo = WallpaperColorInfo.getInstance(launcher);
+ updateColors();
+
+ int finalAlpha = 0xBF;
+ mFinalGradientMask = Utilities.convertToAlphaMask(
+ Utilities.createOnePixBitmap(), finalAlpha);
+ Bitmap alphaMaskFromResource = BitmapFactory.decodeResource(context.getResources(),
+ R.drawable.all_apps_alpha_mask);
+ mAlphaGradientMask = Utilities.convertToAlphaMask(
+ alphaMaskFromResource, finalAlpha);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mWallpaperColorInfo.addOnChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mWallpaperColorInfo.removeOnChangeListener(this);
+ }
+
+ @Override
+ public void onExtractedColorsChanged(WallpaperColorInfo info) {
+ updateColors();
+ invalidate();
+ }
+
+ private void updateColors() {
+ this.mColor1 = mWallpaperColorInfo.getMainColor();
+ this.mColor2 = mWallpaperColorInfo.getSecondaryColor();
+ if (mWidth + mHeight > 0) {
+ createRadialShader();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ this.mWidth = getMeasuredWidth();
+ this.mHeight = getMeasuredHeight();
+ if (mWidth + mHeight > 0) {
+ createRadialShader();
+ }
+ }
+
+ // only being called when colors change
+ private void createRadialShader() {
+ final float gradientCenterY = 1.05f;
+ float radius = Math.max(mHeight, mWidth) * gradientCenterY;
+
+ float posScreenBottom = (radius - mHeight) / radius; // center lives below screen
+ RadialGradient shader = new RadialGradient(
+ mWidth * 0.5f,
+ mHeight * gradientCenterY,
+ radius,
+ new int[] {mColor1, mColor1, mColor2},
+ new float[] {0f, posScreenBottom, 1f},
+ Shader.TileMode.CLAMP);
+ mPaint.setShader(shader);
+ }
+
+ public void setProgress(float progress) {
+ this.mProgress = progress;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ float head = 0.29f;
+ float linearProgress = head + (mProgress * (1f - head));
+ float startMaskY = (1f - linearProgress) * mHeight - mMaskHeight * linearProgress;
+ float interpolatedAlpha = (255 - mAlphaStart) * mAccelerator.getInterpolation(mProgress);
+ mPaint.setAlpha((int) (mAlphaStart + interpolatedAlpha));
+ mAlphaMaskRect.set(0, startMaskY, mWidth, startMaskY + mMaskHeight);
+ mFinalMaskRect.set(0, startMaskY + mMaskHeight, mWidth, mHeight);
+ canvas.drawBitmap(mAlphaGradientMask, null, mAlphaMaskRect, mPaint);
+ canvas.drawBitmap(mFinalGradientMask, null, mFinalMaskRect, mPaint);
+
+ if (DEBUG) {
+ mDebugPaint.setColor(0xFF00FF00);
+ canvas.drawLine(0, startMaskY, mWidth, startMaskY, mDebugPaint);
+ canvas.drawLine(0, startMaskY + mMaskHeight, mWidth, startMaskY + mMaskHeight, mDebugPaint);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
index c9873d9..b221828 100644
--- a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java
@@ -31,7 +31,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.R;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import java.nio.ByteBuffer;
@@ -86,7 +86,7 @@
* bitmap.
*/
public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
- if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
}
diff --git a/src/com/android/launcher3/graphics/IconPalette.java b/src/com/android/launcher3/graphics/IconPalette.java
index a17ceeb..6e01ed5 100644
--- a/src/com/android/launcher3/graphics/IconPalette.java
+++ b/src/com/android/launcher3/graphics/IconPalette.java
@@ -169,43 +169,40 @@
* This was copied from com.android.internal.util.NotificationColorUtil.
*/
private static int ensureTextContrast(int color, int bg) {
- return findContrastColor(color, bg, true, 4.5);
+ return findContrastColor(color, bg, 4.5);
}
/**
* Finds a suitable color such that there's enough contrast.
*
- * @param color the color to start searching from.
- * @param other the color to ensure contrast against. Assumed to be lighter than {@param color}
- * @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
+ * @param fg the color to start searching from.
+ * @param bg the color to ensure contrast against.
* @param minRatio the minimum contrast ratio required.
* @return a color with the same hue as {@param color}, potentially darkened to meet the
* contrast ratio.
*
* This was copied from com.android.internal.util.NotificationColorUtil.
*/
- private static int findContrastColor(int color, int other, boolean findFg, double minRatio) {
- int fg = findFg ? color : other;
- int bg = findFg ? other : color;
+ private static int findContrastColor(int fg, int bg, double minRatio) {
if (ColorUtils.calculateContrast(fg, bg) >= minRatio) {
- return color;
+ return fg;
}
double[] lab = new double[3];
- ColorUtils.colorToLAB(findFg ? fg : bg, lab);
+ ColorUtils.colorToLAB(bg, lab);
+ double bgL = lab[0];
+ ColorUtils.colorToLAB(fg, lab);
+ double fgL = lab[0];
+ boolean isBgDark = bgL < 50;
- double low = 0, high = lab[0];
+ double low = isBgDark ? fgL : 0, high = isBgDark ? 100 : fgL;
final double a = lab[1], b = lab[2];
for (int i = 0; i < 15 && high - low > 0.00001; i++) {
final double l = (low + high) / 2;
- if (findFg) {
- fg = ColorUtils.LABToColor(l, a, b);
- } else {
- bg = ColorUtils.LABToColor(l, a, b);
- }
+ fg = ColorUtils.LABToColor(l, a, b);
if (ColorUtils.calculateContrast(fg, bg) > minRatio) {
- low = l;
+ if (isBgDark) high = l; else low = l;
} else {
- high = l;
+ if (isBgDark) low = l; else high = l;
}
}
return ColorUtils.LABToColor(low, a, b);
diff --git a/src/com/android/launcher3/graphics/IconShapeOverride.java b/src/com/android/launcher3/graphics/IconShapeOverride.java
index 6e4d366..e2d1d50 100644
--- a/src/com/android/launcher3/graphics/IconShapeOverride.java
+++ b/src/com/android/launcher3/graphics/IconShapeOverride.java
@@ -15,13 +15,14 @@
*/
package com.android.launcher3.graphics;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Build;
import android.os.SystemClock;
@@ -34,11 +35,10 @@
import android.util.Log;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
import java.lang.reflect.Field;
@@ -101,7 +101,7 @@
} catch (Exception e) {
Log.e(TAG, "Unable to override icon shape", e);
// revert value.
- prefs(context).edit().remove(KEY_PREFERENCE).apply();
+ getDevicePrefs(context).edit().remove(KEY_PREFERENCE).apply();
}
}
@@ -116,11 +116,7 @@
}
private static String getAppliedValue(Context context) {
- return prefs(context).getString(KEY_PREFERENCE, "");
- }
-
- private static SharedPreferences prefs(Context context) {
- return context.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
+ return getDevicePrefs(context).getString(KEY_PREFERENCE, "");
}
public static void handlePreferenceUi(ListPreference preference) {
@@ -169,7 +165,7 @@
mContext.getString(R.string.icon_shape_override_progress),
true /* indeterminate */,
false /* cancelable */);
- new LooperExecuter(LauncherModel.getWorkerLooper()).execute(
+ new LooperExecutor(LauncherModel.getWorkerLooper()).execute(
new OverrideApplyHandler(mContext, newValue));
}
return false;
@@ -189,7 +185,7 @@
@Override
public void run() {
// Synchronously write the preference.
- prefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
+ getDevicePrefs(mContext).edit().putString(KEY_PREFERENCE, mValue).commit();
// Clear the icon cache.
LauncherAppState.getInstance(mContext).getIconCache().clear();
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 19e57024..830ca82 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -322,14 +322,16 @@
IconCache cache = app.getIconCache();
Bitmap unbadgedBitmap = unbadgedDrawable == null
? cache.getDefaultIcon(Process.myUserHandle())
- : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context,
- Build.VERSION_CODES.O);
+ : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context, 0);
if (!badged) {
return unbadgedBitmap;
}
unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap, context);
+ return badgeWithBitmap(unbadgedBitmap, getShortcutInfoBadge(shortcutInfo, cache), context);
+ }
+ public static Bitmap getShortcutInfoBadge(ShortcutInfoCompat shortcutInfo, IconCache cache) {
final Bitmap badgeBitmap;
ComponentName cn = shortcutInfo.getActivity();
if (cn != null) {
@@ -347,7 +349,7 @@
cache.getTitleAndIconForApp(pkgInfo, false);
badgeBitmap = pkgInfo.iconBitmap;
}
- return badgeWithBitmap(unbadgedBitmap, badgeBitmap, context);
+ return badgeBitmap;
}
/**
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 22ce098..06dc7ac 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -69,7 +69,7 @@
private static final int COLOR_TRACK = 0x77EEEEEE;
private static final int COLOR_SHADOW = 0x55000000;
- private static final float SMALL_SCALE = 0.75f;
+ private static final float SMALL_SCALE = 0.6f;
private static final SparseArray<WeakReference<Bitmap>> sShadowCache = new SparseArray<>();
@@ -121,11 +121,11 @@
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mTmpMatrix.setScale(
- (bounds.width() - PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
- (bounds.height() - PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
+ (bounds.width() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE,
+ (bounds.height() - 2 * PROGRESS_WIDTH - 2 * PROGRESS_GAP) / PATH_SIZE);
mTmpMatrix.postTranslate(
- bounds.left + PROGRESS_WIDTH / 2 + PROGRESS_GAP,
- bounds.top + PROGRESS_WIDTH / 2 + PROGRESS_GAP);
+ bounds.left + PROGRESS_WIDTH + PROGRESS_GAP,
+ bounds.top + PROGRESS_WIDTH + PROGRESS_GAP);
mProgressPath.transform(mTmpMatrix, mScaledTrackPath);
float scale = bounds.width() / PATH_SIZE;
@@ -178,7 +178,7 @@
Rect bounds = getBounds();
canvas.scale(mIconScale, mIconScale, bounds.exactCenterX(), bounds.exactCenterY());
- drawInternal(canvas);
+ super.draw(canvas);
canvas.restoreToCount(saveCount);
}
diff --git a/src/com/android/launcher3/graphics/ScrimView.java b/src/com/android/launcher3/graphics/ScrimView.java
new file mode 100644
index 0000000..6d1f30a
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ScrimView.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.Themes;
+
+public class ScrimView extends View {
+
+ private static final boolean DEBUG = false;
+
+ private static final int MASK_HEIGHT_DP = 300;
+ private static final float MASK_START_LENGTH_FACTOR = 1f;
+ private static final boolean APPLY_ALPHA = true;
+
+ private final Bitmap mFinalScrimMask;
+ private final Bitmap mAlphaScrimMask;
+
+ private final int mMaskHeight;
+ private int mVisibleHeight;
+ private final int mHeadStart;
+
+ private final RectF mAlphaMaskRect = new RectF();
+ private final RectF mFinalMaskRect = new RectF();
+ private final Paint mPaint = new Paint();
+ private float mProgress;
+ private final Interpolator mAccelerator = new AccelerateInterpolator();
+ private final Paint mDebugPaint = DEBUG ? new Paint() : null;
+ private final int mAlphaStart;
+
+ public ScrimView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mMaskHeight = Utilities.pxFromDp(MASK_HEIGHT_DP, getResources().getDisplayMetrics());
+ mHeadStart = (int) (mMaskHeight * MASK_START_LENGTH_FACTOR);
+ mAlphaStart = Launcher.getLauncher(context)
+ .getDeviceProfile().isVerticalBarLayout() ? 0 : 55;
+
+ int scrimColor = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
+ int scrimAlpha = Color.alpha(scrimColor);
+ mPaint.setColor(scrimColor);
+ mFinalScrimMask = Utilities.convertToAlphaMask(
+ Utilities.createOnePixBitmap(), scrimAlpha);
+ Bitmap alphaMaskFromResource = BitmapFactory.decodeResource(getResources(),
+ R.drawable.all_apps_alpha_mask);
+ mAlphaScrimMask = Utilities.convertToAlphaMask(alphaMaskFromResource, scrimAlpha);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ mVisibleHeight = MeasureSpec.getSize(heightMeasureSpec);
+ setMeasuredDimension(width, mVisibleHeight * 2);
+ setProgress(mProgress);
+ }
+
+ public void setProgress(float progress) {
+ mProgress = progress;
+ float initialY = mVisibleHeight - mHeadStart;
+ float fullTranslationY = mVisibleHeight;
+ float linTranslationY = initialY - progress * fullTranslationY;
+ setTranslationY(linTranslationY);
+
+ if (APPLY_ALPHA) {
+ int alpha = mAlphaStart + (int) ((255f - mAlphaStart)
+ * mAccelerator.getInterpolation(progress));
+ mPaint.setAlpha(alpha);
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mAlphaMaskRect.set(0, 0, getWidth(), mMaskHeight);
+ mFinalMaskRect.set(0, mMaskHeight, getWidth(), getHeight());
+ canvas.drawBitmap(mAlphaScrimMask, null, mAlphaMaskRect, mPaint);
+ canvas.drawBitmap(mFinalScrimMask, null, mFinalMaskRect, mPaint);
+
+ if (DEBUG) {
+ mDebugPaint.setColor(0xFF0000FF);
+ canvas.drawLine(0, mAlphaMaskRect.top, getWidth(), mAlphaMaskRect.top, mDebugPaint);
+ canvas.drawLine(0, mAlphaMaskRect.bottom, getWidth(), mAlphaMaskRect.bottom, mDebugPaint);
+ }
+ }
+
+}
diff --git a/src/com/android/launcher3/graphics/ShadowDrawable.java b/src/com/android/launcher3/graphics/ShadowDrawable.java
new file mode 100644
index 0000000..ffcedb2
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ShadowDrawable.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BlurMaskFilter;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * A drawable which adds shadow around a child drawable.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class ShadowDrawable extends Drawable {
+
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+
+ private final ShadowDrawableState mState;
+
+ @SuppressWarnings("unused")
+ public ShadowDrawable() {
+ this(new ShadowDrawableState());
+ }
+
+ private ShadowDrawable(ShadowDrawableState state) {
+ mState = state;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ Rect bounds = getBounds();
+ if (bounds.isEmpty()) {
+ return;
+ }
+ if (mState.mLastDrawnBitmap == null) {
+ regenerateBitmapCache();
+ }
+ canvas.drawBitmap(mState.mLastDrawnBitmap, null, bounds, mPaint);
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mPaint.setAlpha(alpha);
+ invalidateSelf();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ mPaint.setColorFilter(colorFilter);
+ invalidateSelf();
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mState;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mState.mIntrinsicHeight;
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mState.mIntrinsicWidth;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return mState.canApplyTheme();
+ }
+
+ @Override
+ public void applyTheme(Resources.Theme t) {
+ TypedArray ta = t.obtainStyledAttributes(new int[] {R.attr.isWorkspaceDarkText});
+ boolean isDark = ta.getBoolean(0, false);
+ ta.recycle();
+ if (mState.mIsDark != isDark) {
+ mState.mIsDark = isDark;
+ mState.mLastDrawnBitmap = null;
+ invalidateSelf();
+ }
+ }
+
+ private void regenerateBitmapCache() {
+ Bitmap bitmap = Bitmap.createBitmap(mState.mIntrinsicWidth, mState.mIntrinsicHeight,
+ Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas(bitmap);
+
+ // Call mutate, so that the pixel allocation by the underlying vector drawable is cleared.
+ Drawable d = mState.mChildState.newDrawable().mutate();
+ d.setBounds(mState.mShadowSize, mState.mShadowSize,
+ mState.mIntrinsicWidth - mState.mShadowSize,
+ mState.mIntrinsicHeight - mState.mShadowSize);
+ d.setTint(mState.mIsDark ? mState.mDarkTintColor : Color.WHITE);
+ d.draw(canvas);
+
+ // Do not draw shadow on dark theme
+ if (!mState.mIsDark) {
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ paint.setMaskFilter(new BlurMaskFilter(mState.mShadowSize, BlurMaskFilter.Blur.NORMAL));
+ int[] offset = new int[2];
+ Bitmap shadow = bitmap.extractAlpha(paint, offset);
+
+ paint.setMaskFilter(null);
+ paint.setColor(mState.mShadowColor);
+ bitmap.eraseColor(Color.TRANSPARENT);
+ canvas.drawBitmap(shadow, offset[0], offset[1], paint);
+ d.draw(canvas);
+ }
+
+ if (Utilities.isAtLeastO()) {
+ bitmap = bitmap.copy(Bitmap.Config.HARDWARE, false);
+ }
+ mState.mLastDrawnBitmap = bitmap;
+ }
+
+ @Override
+ public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs,
+ Resources.Theme theme) throws XmlPullParserException, IOException {
+ super.inflate(r, parser, attrs, theme);
+
+ final TypedArray a = theme == null
+ ? r.obtainAttributes(attrs, R.styleable.ShadowDrawable)
+ : theme.obtainStyledAttributes(attrs, R.styleable.ShadowDrawable, 0, 0);
+ try {
+ Drawable d = a.getDrawable(R.styleable.ShadowDrawable_android_src);
+ if (d == null) {
+ throw new XmlPullParserException("missing src attribute");
+ }
+ mState.mShadowColor = a.getColor(
+ R.styleable.ShadowDrawable_android_shadowColor, Color.BLACK);
+ mState.mShadowSize = a.getDimensionPixelSize(
+ R.styleable.ShadowDrawable_android_elevation, 0);
+ mState.mDarkTintColor = a.getColor(
+ R.styleable.ShadowDrawable_darkTintColor, Color.BLACK);
+
+ mState.mIntrinsicHeight = d.getIntrinsicHeight() + 2 * mState.mShadowSize;
+ mState.mIntrinsicWidth = d.getIntrinsicWidth() + 2 * mState.mShadowSize;
+ mState.mChangingConfigurations = d.getChangingConfigurations();
+
+ mState.mChildState = d.getConstantState();
+ } finally {
+ a.recycle();
+ }
+ }
+
+ private static class ShadowDrawableState extends ConstantState {
+
+ int mChangingConfigurations;
+ int mIntrinsicWidth;
+ int mIntrinsicHeight;
+
+ int mShadowColor;
+ int mShadowSize;
+ int mDarkTintColor;
+
+ boolean mIsDark;
+ Bitmap mLastDrawnBitmap;
+ ConstantState mChildState;
+
+ @Override
+ public Drawable newDrawable() {
+ return new ShadowDrawable(this);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mChangingConfigurations;
+ }
+
+ @Override
+ public boolean canApplyTheme() {
+ return true;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
index 5d8cca8..fffea8e 100644
--- a/src/com/android/launcher3/graphics/ShadowGenerator.java
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -22,11 +22,12 @@
import android.graphics.BlurMaskFilter;
import android.graphics.BlurMaskFilter.Blur;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
+import android.support.v4.graphics.ColorUtils;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.util.Preconditions;
/**
* Utility class to add shadows to bitmaps.
@@ -38,10 +39,10 @@
public static final float BLUR_FACTOR = 0.5f/48;
// Percent of actual icon size
- private static final float KEY_SHADOW_DISTANCE = 1f/48;
- public static final int KEY_SHADOW_ALPHA = 61;
+ public static final float KEY_SHADOW_DISTANCE = 1f/48;
+ private static final int KEY_SHADOW_ALPHA = 61;
- public static final int AMBIENT_SHADOW_ALPHA = 30;
+ private static final int AMBIENT_SHADOW_ALPHA = 30;
private static final Object LOCK = new Object();
// Singleton object guarded by {@link #LOCK}
@@ -83,48 +84,10 @@
return result;
}
- public static Bitmap createPillWithShadow(int rectColor, int width, int height) {
-
- float shadowRadius = height * 1f / 32;
- float shadowYOffset = height * 1f / 16;
-
- int radius = height / 2;
-
- Canvas canvas = new Canvas();
- Paint blurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- blurPaint.setMaskFilter(new BlurMaskFilter(shadowRadius, Blur.NORMAL));
-
- int centerX = Math.round(width / 2 + shadowRadius);
- int centerY = Math.round(radius + shadowRadius + shadowYOffset);
- int center = Math.max(centerX, centerY);
- int size = center * 2;
- Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
- canvas.setBitmap(result);
-
- int left = center - width / 2;
- int top = center - height / 2;
- int right = center + width / 2;
- int bottom = center + height / 2;
-
- // Draw ambient shadow, center aligned within size
- blurPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
- canvas.drawRoundRect(left, top, right, bottom, radius, radius, blurPaint);
-
- // Draw key shadow, bottom aligned within size
- blurPaint.setAlpha(KEY_SHADOW_ALPHA);
- canvas.drawRoundRect(left, top + shadowYOffset, right, bottom + shadowYOffset,
- radius, radius, blurPaint);
-
- // Draw the circle
- Paint drawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
- drawPaint.setColor(rectColor);
- canvas.drawRoundRect(left, top, right, bottom, radius, radius, drawPaint);
-
- return result;
- }
-
public static ShadowGenerator getInstance(Context context) {
- Preconditions.assertNonUiThread();
+ // 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);
@@ -152,4 +115,58 @@
}
return scale;
}
+
+ public static class Builder {
+
+ public final RectF bounds = new RectF();
+ public final int color;
+
+ public int ambientShadowAlpha = AMBIENT_SHADOW_ALPHA;
+
+ public float shadowBlur;
+
+ public float keyShadowDistance;
+ public int keyShadowAlpha = KEY_SHADOW_ALPHA;
+ public float radius;
+
+ public Builder(int color) {
+ this.color = color;
+ }
+
+ public Builder setupBlurForSize(int height) {
+ shadowBlur = height * 1f / 32;
+ keyShadowDistance = height * 1f / 16;
+ return this;
+ }
+
+ public Bitmap createPill(int width, int height) {
+ radius = height / 2;
+
+ int centerX = Math.round(width / 2 + shadowBlur);
+ int centerY = Math.round(radius + shadowBlur + keyShadowDistance);
+ int center = Math.max(centerX, centerY);
+ bounds.set(0, 0, width, height);
+ bounds.offsetTo(center - width / 2, center - height / 2);
+
+ int size = center * 2;
+ Bitmap result = Bitmap.createBitmap(size, size, Config.ARGB_8888);
+ drawShadow(new Canvas(result));
+ return result;
+ }
+
+ public void drawShadow(Canvas c) {
+ Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ p.setColor(color);
+
+ // Key shadow
+ p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
+ ColorUtils.setAlphaComponent(Color.BLACK, keyShadowAlpha));
+ c.drawRoundRect(bounds, radius, radius, p);
+
+ // Ambient shadow
+ p.setShadowLayer(shadowBlur, 0, 0,
+ ColorUtils.setAlphaComponent(Color.BLACK, ambientShadowAlpha));
+ c.drawRoundRect(bounds, radius, radius, p);
+ }
+ }
}
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
index f72ce03..d719575 100644
--- a/src/com/android/launcher3/graphics/TintedDrawableSpan.java
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -34,6 +34,7 @@
super(ALIGN_BOTTOM);
mDrawable = context.getDrawable(resourceId);
mOldTint = 0;
+ mDrawable.setTint(0);
}
@Override
diff --git a/src/com/android/launcher3/keyboard/CustomActionsPopup.java b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
index bb0b58a..938955c 100644
--- a/src/com/android/launcher3/keyboard/CustomActionsPopup.java
+++ b/src/com/android/launcher3/keyboard/CustomActionsPopup.java
@@ -24,10 +24,10 @@
import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener;
-import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.popup.PopupContainerWithArrow;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index ffb41b7..4c83e9a 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -6,9 +6,8 @@
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import java.io.BufferedReader;
import java.io.File;
@@ -40,7 +39,7 @@
private static File sLogsDirectory = null;
public static void setDir(File logsDir) {
- if (ProviderConfig.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD || Utilities.IS_DEBUG_DEVICE) {
synchronized (DATE_FORMAT) {
// If the target directory changes, stop any active thread.
if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
@@ -77,7 +76,7 @@
}
public static void print(String tag, String msg, Exception e) {
- if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_DOGFOOD_BUILD) {
return;
}
String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
@@ -103,7 +102,7 @@
* @param out if not null, all the persisted logs are copied to the writer.
*/
public static void flushAll(PrintWriter out) throws InterruptedException {
- if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (!FeatureFlags.IS_DOGFOOD_BUILD) {
return;
}
CountDownLatch latch = new CountDownLatch(1);
@@ -136,7 +135,7 @@
@Override
public boolean handleMessage(Message msg) {
- if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (sLogsDirectory == null || !FeatureFlags.IS_DOGFOOD_BUILD) {
return true;
}
switch (msg.what) {
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 499fdc7..72a083b 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.logging;
-import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.SparseArray;
import android.view.View;
@@ -67,8 +66,14 @@
}
public static String getActionStr(Action action) {
+ String str = "";
switch (action.type) {
- case Action.Type.TOUCH: return getFieldName(action.touch, Action.Touch.class);
+ case Action.Type.TOUCH:
+ str += getFieldName(action.touch, Action.Touch.class);
+ if (action.touch == Action.Touch.SWIPE) {
+ str += " direction=" + getFieldName(action.dir, Action.Direction.class);
+ }
+ return str;
case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
default: return UNKNOWN;
}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 258af16..edbb88c 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.SystemClock;
+import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.view.ViewParent;
@@ -29,7 +30,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
@@ -60,7 +61,7 @@
private static final String TAG = "UserEvent";
private static final boolean IS_VERBOSE =
- ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+ FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
public static UserEventDispatcher newInstance(Context context, boolean isInLandscapeMode,
boolean isInMultiWindowMode) {
@@ -90,7 +91,7 @@
/**
* Recursively finds the parent of the given child which implements IconLogInfoProvider
*/
- public static LogContainerProvider getLaunchProviderRecursive(View v) {
+ public static LogContainerProvider getLaunchProviderRecursive(@Nullable View v) {
ViewParent parent;
if (v != null) {
parent = v.getParent();
@@ -147,7 +148,11 @@
return event;
}
- public boolean fillInLogContainerData(LauncherEvent event, View v) {
+ /**
+ * Fills in the container data on the given event if the given view is not null.
+ * @return whether container data was added.
+ */
+ private boolean fillInLogContainerData(LauncherEvent event, @Nullable View v) {
// Fill in grid(x,y), pageIndex of the child and container type of the parent
LogContainerProvider provider = getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
@@ -203,9 +208,16 @@
}
public void logActionOnControl(int action, int controlType) {
- LauncherEvent event = newLauncherEvent(
- newTouchAction(action), newTarget(Target.Type.CONTROL));
+ logActionOnControl(action, controlType, null);
+ }
+
+ public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer) {
+ final LauncherEvent event = controlInContainer == null
+ ? 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);
dispatchUserEvent(event, null);
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index 9696054..68012c4 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -17,10 +17,12 @@
import android.content.Context;
import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.os.Process;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.LongSparseArray;
import android.util.Pair;
-
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
@@ -33,29 +35,30 @@
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.GridOccupancy;
+import com.android.launcher3.util.ManagedProfileHeuristic.UserFolderInfo;
import com.android.launcher3.util.Provider;
-
import java.util.ArrayList;
import java.util.List;
/**
* Task to add auto-created workspace items.
*/
-public class AddWorkspaceItemsTask extends ExtendedModelTask {
+public class AddWorkspaceItemsTask extends BaseModelUpdateTask {
- private final Provider<List<ItemInfo>> mAppsProvider;
+ private final Provider<List<Pair<ItemInfo, Object>>> mAppsProvider;
/**
* @param appsProvider items to add on the workspace
*/
- public AddWorkspaceItemsTask(Provider<List<ItemInfo>> appsProvider) {
+ public AddWorkspaceItemsTask(Provider<List<Pair<ItemInfo, Object>>> appsProvider) {
mAppsProvider = appsProvider;
}
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
- List<ItemInfo> workspaceApps = mAppsProvider.get();
+ List<Pair<ItemInfo, Object>> workspaceApps = mAppsProvider.get();
if (workspaceApps.isEmpty()) {
return;
}
@@ -63,13 +66,17 @@
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final ArrayList<Long> addedWorkspaceScreensFinal = new ArrayList<>();
+ ArrayMap<UserHandle, UserFolderInfo> userFolderMap = new ArrayMap<>();
// Get the list of workspace screens. We need to append to this list and
// can not use sBgWorkspaceScreens because loadWorkspace() may not have been
// called.
ArrayList<Long> workspaceScreens = LauncherModel.loadWorkspaceScreensDb(context);
synchronized(dataModel) {
- for (ItemInfo item : workspaceApps) {
+
+ List<ItemInfo> filteredItems = new ArrayList<>();
+ for (Pair<ItemInfo, Object> entry : workspaceApps) {
+ ItemInfo item = entry.first;
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
item.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
// Short-circuit this logic if the icon exists somewhere on the workspace
@@ -78,6 +85,32 @@
}
}
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ if (item instanceof AppInfo) {
+ item = ((AppInfo) item).makeShortcut();
+ }
+
+ if (!Process.myUserHandle().equals(item.user)) {
+ // Check if this belongs to a work folder.
+ if (!(entry.second instanceof LauncherActivityInfo)) {
+ continue;
+ }
+
+ UserFolderInfo userFolderInfo = userFolderMap.get(item.user);
+ if (userFolderInfo == null) {
+ userFolderInfo = new UserFolderInfo(context, item.user, dataModel);
+ userFolderMap.put(item.user, userFolderInfo);
+ }
+ item = userFolderInfo.convertToWorkspaceItem(
+ (ShortcutInfo) item, (LauncherActivityInfo) entry.second);
+ }
+ }
+ if (item != null) {
+ filteredItems.add(item);
+ }
+ }
+
+ for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
Pair<Long, int[]> coords = findSpaceForItem(app, dataModel, workspaceScreens,
addedWorkspaceScreensFinal, item.spanX, item.spanY);
@@ -111,8 +144,8 @@
scheduleCallbackTask(new CallbackTask() {
@Override
public void execute(Callbacks callbacks) {
- final ArrayList<ItemInfo> addAnimated = new ArrayList<ItemInfo>();
- final ArrayList<ItemInfo> addNotAnimated = new ArrayList<ItemInfo>();
+ final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
+ final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
if (!addedItemsFinal.isEmpty()) {
ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
long lastScreenId = info.screenId;
@@ -129,6 +162,10 @@
}
});
}
+
+ for (UserFolderInfo userFolderInfo : userFolderMap.values()) {
+ userFolderInfo.applyPendingState(getModelWriter());
+ }
}
protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) {
@@ -140,7 +177,7 @@
* the workspace has been loaded. We identify a shortcut by its intent.
*/
protected boolean shortcutExists(BgDataModel dataModel, Intent intent, UserHandle user) {
- final String intentWithPkg, intentWithoutPkg;
+ final String compPkgName, intentWithPkg, intentWithoutPkg;
if (intent == null) {
// Skip items with null intents
return true;
@@ -148,19 +185,21 @@
if (intent.getComponent() != null) {
// If component is not null, an intent with null package will produce
// the same result and should also be a match.
- String packageName = intent.getComponent().getPackageName();
+ compPkgName = intent.getComponent().getPackageName();
if (intent.getPackage() != null) {
intentWithPkg = intent.toUri(0);
intentWithoutPkg = new Intent(intent).setPackage(null).toUri(0);
} else {
- intentWithPkg = new Intent(intent).setPackage(packageName).toUri(0);
+ intentWithPkg = new Intent(intent).setPackage(compPkgName).toUri(0);
intentWithoutPkg = intent.toUri(0);
}
} else {
+ compPkgName = null;
intentWithPkg = intent.toUri(0);
intentWithoutPkg = intent.toUri(0);
}
+ boolean isLauncherAppTarget = Utilities.isLauncherAppTarget(intent);
synchronized (dataModel) {
for (ItemInfo item : dataModel.itemsIdMap) {
if (item instanceof ShortcutInfo) {
@@ -172,6 +211,16 @@
if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
return true;
}
+
+ // checking for existing promise icon with same package name
+ if (isLauncherAppTarget
+ && info.isPromise()
+ && info.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)
+ && info.getTargetComponent() != null
+ && compPkgName != null
+ && compPkgName.equals(info.getTargetComponent().getPackageName())) {
+ return true;
+ }
}
}
}
@@ -263,4 +312,5 @@
}
return occupied.findVacantCell(xy, spanX, spanY);
}
+
}
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
new file mode 100644
index 0000000..9b4510f
--- /dev/null
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -0,0 +1,135 @@
+/*
+ * 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.model;
+
+import android.os.UserHandle;
+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 com.android.launcher3.LauncherModel.CallbackTask;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.MultiHashMap;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * Extension of {@link ModelUpdateTask} with some utility methods
+ */
+public abstract class BaseModelUpdateTask implements ModelUpdateTask {
+
+ private static final boolean DEBUG_TASKS = false;
+ private static final String TAG = "BaseModelUpdateTask";
+
+ private LauncherAppState mApp;
+ private LauncherModel mModel;
+ private BgDataModel mDataModel;
+ private AllAppsList mAllAppsList;
+ private Executor mUiExecutor;
+
+ public void init(LauncherAppState app, LauncherModel model,
+ BgDataModel dataModel, AllAppsList allAppsList, Executor uiExecutor) {
+ mApp = app;
+ mModel = model;
+ mDataModel = dataModel;
+ mAllAppsList = allAppsList;
+ mUiExecutor = uiExecutor;
+ }
+
+ @Override
+ public final void run() {
+ if (!mModel.isModelLoaded()) {
+ if (DEBUG_TASKS) {
+ Log.d(TAG, "Ignoring model task since loader is pending=" + this);
+ }
+ // Loader has not yet run.
+ return;
+ }
+ execute(mApp, mDataModel, mAllAppsList);
+ }
+
+ /**
+ * Execute the actual task. Called on the worker thread.
+ */
+ public abstract void execute(
+ LauncherAppState app, BgDataModel dataModel, AllAppsList apps);
+
+ /**
+ * Schedules a {@param task} to be executed on the current callbacks.
+ */
+ public final void scheduleCallbackTask(final CallbackTask task) {
+ final Callbacks callbacks = mModel.getCallback();
+ mUiExecutor.execute(new Runnable() {
+ public void run() {
+ Callbacks cb = mModel.getCallback();
+ if (callbacks == cb && cb != null) {
+ task.execute(callbacks);
+ }
+ }
+ });
+ }
+
+ public ModelWriter getModelWriter() {
+ // Updates from model task, do not deal with icon position in hotseat.
+ return mModel.getWriter(false /* hasVerticalHotseat */);
+ }
+
+
+ public void bindUpdatedShortcuts(
+ ArrayList<ShortcutInfo> updatedShortcuts, UserHandle user) {
+ bindUpdatedShortcuts(updatedShortcuts, new ArrayList<ShortcutInfo>(), user);
+ }
+
+ public void bindUpdatedShortcuts(
+ final ArrayList<ShortcutInfo> updatedShortcuts,
+ final ArrayList<ShortcutInfo> removedShortcuts,
+ final UserHandle user) {
+ if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user);
+ }
+ });
+ }
+ }
+
+ public void bindDeepShortcuts(BgDataModel dataModel) {
+ final MultiHashMap<ComponentKey, String> shortcutMapCopy = dataModel.deepShortcutMap.clone();
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindDeepShortcutMap(shortcutMapCopy);
+ }
+ });
+ }
+
+ public void bindUpdatedWidgets(BgDataModel dataModel) {
+ final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+ = dataModel.widgetsModel.getWidgetsMap();
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAllWidgets(widgets);
+ }
+ });
+ }
+}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 0e73ca6..816c1d4 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -27,16 +27,14 @@
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.config.ProviderConfig;
-import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.DumpTargetWrapper;
-import com.android.launcher3.shortcuts.DeepShortcutManager;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
-import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.model.nano.LauncherDumpProto;
import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
-import com.android.launcher3.model.nano.LauncherDumpProto.ItemType;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.MultiHashMap;
@@ -93,11 +91,21 @@
public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
/**
+ * True if the launcher has permission to access deep shortcuts.
+ */
+ public boolean hasShortcutHostPermission;
+
+ /**
* Maps all launcher activities to the id's of their shortcuts (if they have any).
*/
public final MultiHashMap<ComponentKey, String> deepShortcutMap = new MultiHashMap<>();
/**
+ * Entire list of widgets.
+ */
+ public final WidgetsModel widgetsModel = new WidgetsModel();
+
+ /**
* Clears all the data
*/
public synchronized void clear() {
@@ -144,7 +152,7 @@
for (ArrayList<String> map : deepShortcutMap.values()) {
writer.print(prefix + " ");
for (String str : map) {
- writer.print(str.toString() + ", ");
+ writer.print(str + ", ");
}
writer.println();
}
@@ -158,7 +166,7 @@
DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
LongArrayMap<DumpTargetWrapper> workspaces = new LongArrayMap<>();
for (int i = 0; i < workspaceScreens.size(); i++) {
- workspaces.put(new Long(workspaceScreens.get(i)),
+ workspaces.put(workspaceScreens.get(i),
new DumpTargetWrapper(ContainerType.WORKSPACE, i));
}
DumpTargetWrapper dtw;
@@ -175,7 +183,7 @@
if (fInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.add(dtw);
} else if (fInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- workspaces.get(new Long(fInfo.screenId)).add(dtw);
+ workspaces.get(fInfo.screenId).add(dtw);
}
}
// Add leaf nodes (L3): *Info
@@ -189,7 +197,7 @@
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.add(dtw);
} else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- workspaces.get(new Long(info.screenId)).add(dtw);
+ workspaces.get(info.screenId).add(dtw);
}
}
for (int i = 0; i < appWidgets.size(); i++) {
@@ -199,7 +207,7 @@
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.add(dtw);
} else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
- workspaces.get(new Long(info.screenId)).add(dtw);
+ workspaces.get(info.screenId).add(dtw);
}
}
@@ -242,7 +250,7 @@
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
folders.remove(item.id);
- if (ProviderConfig.IS_DOGFOOD_BUILD) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
for (ItemInfo info : itemsIdMap) {
if (info.container == item.id) {
// We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 46130fc..8597e10 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -26,7 +26,6 @@
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.ShortcutInfo;
import java.util.ArrayList;
@@ -35,7 +34,7 @@
/**
* Handles changes due to cache updates.
*/
-public class CacheDataUpdatedTask extends ExtendedModelTask {
+public class CacheDataUpdatedTask extends BaseModelUpdateTask {
public static final int OP_CACHE_UPDATE = 1;
public static final int OP_SESSION_UPDATE = 2;
diff --git a/src/com/android/launcher3/model/DbDowngradeHelper.java b/src/com/android/launcher3/model/DbDowngradeHelper.java
new file mode 100644
index 0000000..cd86b72
--- /dev/null
+++ b/src/com/android/launcher3/model/DbDowngradeHelper.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.IOUtils;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * Utility class to handle DB downgrade
+ */
+public class DbDowngradeHelper {
+
+ private static final String TAG = "DbDowngradeHelper";
+
+ private static final String KEY_VERSION = "version";
+ private static final String KEY_DOWNGRADE_TO = "downgrade_to_";
+
+ private final SparseArray<String[]> mStatements = new SparseArray<>();
+ public final int version;
+
+ private DbDowngradeHelper(int version) {
+ this.version = version;
+ }
+
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ ArrayList<String> allCommands = new ArrayList<>();
+
+ for (int i = oldVersion - 1; i >= newVersion; i--) {
+ String[] commands = mStatements.get(i);
+ if (commands == null) {
+ throw new SQLiteException("Downgrade path not supported to version " + i);
+ }
+ Collections.addAll(allCommands, commands);
+ }
+
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
+ for (String sql : allCommands) {
+ db.execSQL(sql);
+ }
+ t.commit();
+ }
+ }
+
+ public static DbDowngradeHelper parse(File file) throws JSONException, IOException {
+ JSONObject obj = new JSONObject(new String(IOUtils.toByteArray(file)));
+ DbDowngradeHelper helper = new DbDowngradeHelper(obj.getInt(KEY_VERSION));
+ for (int version = helper.version - 1; version > 0; version--) {
+ if (obj.has(KEY_DOWNGRADE_TO + version)) {
+ JSONArray statements = obj.getJSONArray(KEY_DOWNGRADE_TO + version);
+ String[] parsed = new String[statements.length()];
+ for (int i = 0; i < parsed.length; i++) {
+ parsed[i] = statements.getString(i);
+ }
+ helper.mStatements.put(version, parsed);
+ }
+ }
+ return helper;
+ }
+
+ public static void updateSchemaFile(File schemaFile, int expectedVersion,
+ Context context, int schemaResId) {
+ try {
+ if (DbDowngradeHelper.parse(schemaFile).version >= expectedVersion) {
+ return;
+ }
+ } catch (Exception e) {
+ // Schema error
+ }
+
+ // Write the updated schema
+ try (FileOutputStream fos = new FileOutputStream(schemaFile);
+ InputStream in = context.getResources().openRawResource(schemaResId)) {
+ IOUtils.copy(in, fos);
+ } catch (IOException e) {
+ Log.e(TAG, "Error writing schema file", e);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/ExtendedModelTask.java b/src/com/android/launcher3/model/ExtendedModelTask.java
deleted file mode 100644
index 0541966..0000000
--- a/src/com/android/launcher3/model/ExtendedModelTask.java
+++ /dev/null
@@ -1,62 +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.model;
-
-import android.os.UserHandle;
-
-import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
-import com.android.launcher3.LauncherModel.CallbackTask;
-import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.MultiHashMap;
-
-import java.util.ArrayList;
-
-/**
- * Extension of {@link BaseModelUpdateTask} with some utility methods
- */
-public abstract class ExtendedModelTask extends BaseModelUpdateTask {
-
- public void bindUpdatedShortcuts(
- ArrayList<ShortcutInfo> updatedShortcuts, UserHandle user) {
- bindUpdatedShortcuts(updatedShortcuts, new ArrayList<ShortcutInfo>(), user);
- }
-
- public void bindUpdatedShortcuts(
- final ArrayList<ShortcutInfo> updatedShortcuts,
- final ArrayList<ShortcutInfo> removedShortcuts,
- final UserHandle user) {
- if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user);
- }
- });
- }
- }
-
- public void bindDeepShortcuts(BgDataModel dataModel) {
- final MultiHashMap<ComponentKey, String> shortcutMapCopy = dataModel.deepShortcutMap.clone();
- scheduleCallbackTask(new CallbackTask() {
- @Override
- public void execute(Callbacks callbacks) {
- callbacks.bindDeepShortcutMap(shortcutMapCopy);
- }
- });
- }
-}
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 221798b..8de0de0 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -13,7 +13,6 @@
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
-
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
@@ -29,10 +28,8 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.LongArrayMap;
-
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
@@ -61,7 +58,6 @@
private final Context mContext;
private final InvariantDeviceProfile mIdp;
- private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
private final ContentValues mTempValues = new ContentValues();
protected final ArrayList<Long> mEntryToRemove = new ArrayList<>();
private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
@@ -728,8 +724,10 @@
int widgetId = c.getInt(indexAppWidgetId);
LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
mContext).getLauncherAppWidgetInfo(widgetId);
- Point spans = pInfo == null ?
- mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
+ Point spans = null;
+ if (pInfo != null) {
+ spans = pInfo.getMinSpans(mIdp, mContext);
+ }
if (spans != null) {
entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
@@ -865,7 +863,7 @@
}
private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
- ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
+ ArrayList<DbEntry> dup = new ArrayList<>(src.size());
for (DbEntry e : src) {
dup.add(e.copy());
}
@@ -909,7 +907,7 @@
try {
boolean dbChanged = false;
- HashSet validPackages = getValidPackages(context);
+ HashSet<String> validPackages = getValidPackages(context);
// Hotseat
int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
if (srcHotseatCount != idp.numHotseatIcons) {
@@ -962,7 +960,7 @@
// this set is removed.
// Since the loader removes such items anyway, removing these items here doesn't cause
// any extra data loss and gives us more free space on the grid for better migration.
- HashSet validPackages = new HashSet<>();
+ HashSet<String> validPackages = new HashSet<>();
for (PackageInfo info : context.getPackageManager()
.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
validPackages.add(info.packageName);
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 36f60b9..bc7da9b 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -184,9 +184,13 @@
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;
}
}
+ if (icon == null) {
+ Log.e(TAG, "Failed to load icon for info " + info);
+ }
return icon;
}
@@ -219,7 +223,7 @@
if (!TextUtils.isEmpty(title)) {
info.title = Utilities.trim(title);
}
- } else if (hasRestoreFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+ } else if (hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
if (TextUtils.isEmpty(info.title)) {
info.title = getTitle();
}
diff --git a/src/com/android/launcher3/model/LoaderResults.java b/src/com/android/launcher3/model/LoaderResults.java
new file mode 100644
index 0000000..0df8b6f
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderResults.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LooperIdleLock;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.ViewOnDrawExecutor;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class to handle results of {@link com.android.launcher3.model.LoaderTask}.
+ */
+public class LoaderResults {
+
+ private static final String TAG = "LoaderResults";
+ private static final long INVALID_SCREEN_ID = -1L;
+ private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
+
+ private final Executor mUiExecutor;
+
+ private final LauncherAppState mApp;
+ private final BgDataModel mBgDataModel;
+ private final AllAppsList mBgAllAppsList;
+ private final int mPageToBindFirst;
+
+ private final WeakReference<Callbacks> mCallbacks;
+
+ public LoaderResults(LauncherAppState app, BgDataModel dataModel,
+ AllAppsList allAppsList, int pageToBindFirst, WeakReference<Callbacks> callbacks) {
+ mUiExecutor = new MainThreadExecutor();
+ mApp = app;
+ mBgDataModel = dataModel;
+ mBgAllAppsList = allAppsList;
+ mPageToBindFirst = pageToBindFirst;
+ mCallbacks = callbacks == null ? new WeakReference<Callbacks>(null) : callbacks;
+ }
+
+ /**
+ * Binds all loaded data to actual views on the main thread.
+ */
+ public void bindWorkspace() {
+ Runnable r;
+
+ Callbacks callbacks = mCallbacks.get();
+ // Don't use these two variables in any of the callback runnables.
+ // Otherwise we hold a reference to them.
+ if (callbacks == null) {
+ // This launcher has exited and nobody bothered to tell us. Just bail.
+ Log.w(TAG, "LoaderTask running with no launcher");
+ return;
+ }
+
+ // Save a copy of all the bg-thread collections
+ ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
+ final ArrayList<Long> orderedScreenIds = new ArrayList<>();
+
+ synchronized (mBgDataModel) {
+ workspaceItems.addAll(mBgDataModel.workspaceItems);
+ appWidgets.addAll(mBgDataModel.appWidgets);
+ orderedScreenIds.addAll(mBgDataModel.workspaceScreens);
+ }
+
+ final int currentScreen;
+ {
+ int currScreen = mPageToBindFirst != PagedView.INVALID_RESTORE_PAGE
+ ? mPageToBindFirst : callbacks.getCurrentWorkspaceScreen();
+ if (currScreen >= orderedScreenIds.size()) {
+ // There may be no workspace screens (just hotseat items and an empty page).
+ currScreen = PagedView.INVALID_RESTORE_PAGE;
+ }
+ currentScreen = currScreen;
+ }
+ final boolean validFirstPage = currentScreen >= 0;
+ final long currentScreenId =
+ validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
+
+ // Separate the items that are on the current screen, and all the other remaining items
+ ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
+ ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
+ ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
+
+ filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
+ otherWorkspaceItems);
+ filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
+ otherAppWidgets);
+ sortWorkspaceItemsSpatially(currentWorkspaceItems);
+ sortWorkspaceItemsSpatially(otherWorkspaceItems);
+
+ // Tell the workspace that we're about to start binding items
+ r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.clearPendingBinds();
+ callbacks.startBinding();
+ }
+ }
+ };
+ mUiExecutor.execute(r);
+
+ // Bind workspace screens
+ mUiExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindScreens(orderedScreenIds);
+ }
+ }
+ });
+
+ Executor mainExecutor = mUiExecutor;
+ // Load items on the current page.
+ bindWorkspaceItems(currentWorkspaceItems, currentAppWidgets, mainExecutor);
+
+ // In case of validFirstPage, only bind the first screen, and defer binding the
+ // remaining screens after first onDraw (and an optional the fade animation whichever
+ // happens later).
+ // This ensures that the first screen is immediately visible (eg. during rotation)
+ // In case of !validFirstPage, bind all pages one after other.
+ final Executor deferredExecutor =
+ validFirstPage ? new ViewOnDrawExecutor(mUiExecutor) : mainExecutor;
+
+ mainExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.finishFirstPageBind(
+ validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
+ }
+ }
+ });
+
+ bindWorkspaceItems(otherWorkspaceItems, otherAppWidgets, deferredExecutor);
+
+ // Tell the workspace that we're done binding items
+ r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.finishBindingItems();
+ }
+ }
+ };
+ deferredExecutor.execute(r);
+
+ if (validFirstPage) {
+ r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ // We are loading synchronously, which means, some of the pages will be
+ // bound after first draw. Inform the callbacks that page binding is
+ // not complete, and schedule the remaining pages.
+ if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
+ callbacks.onPageBoundSynchronously(currentScreen);
+ }
+ callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
+ }
+ }
+ };
+ mUiExecutor.execute(r);
+ }
+ }
+
+
+ /** Filters the set of items who are directly or indirectly (via another container) on the
+ * specified screen. */
+ private void filterCurrentWorkspaceItems(long currentScreenId,
+ ArrayList<ItemInfo> allWorkspaceItems,
+ ArrayList<ItemInfo> currentScreenItems,
+ ArrayList<ItemInfo> otherScreenItems) {
+ // Purge any null ItemInfos
+ Iterator<ItemInfo> iter = allWorkspaceItems.iterator();
+ while (iter.hasNext()) {
+ ItemInfo i = iter.next();
+ if (i == null) {
+ iter.remove();
+ }
+ }
+
+ // Order the set of items by their containers first, this allows use to walk through the
+ // list sequentially, build up a list of containers that are in the specified screen,
+ // as well as all items in those containers.
+ Set<Long> itemsOnScreen = new HashSet<Long>();
+ Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
+ @Override
+ public int compare(ItemInfo lhs, ItemInfo rhs) {
+ return Utilities.longCompare(lhs.container, rhs.container);
+ }
+ });
+ for (ItemInfo info : allWorkspaceItems) {
+ if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ if (info.screenId == currentScreenId) {
+ currentScreenItems.add(info);
+ itemsOnScreen.add(info.id);
+ } else {
+ otherScreenItems.add(info);
+ }
+ } else if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+ currentScreenItems.add(info);
+ itemsOnScreen.add(info.id);
+ } else {
+ if (itemsOnScreen.contains(info.container)) {
+ currentScreenItems.add(info);
+ itemsOnScreen.add(info.id);
+ } else {
+ otherScreenItems.add(info);
+ }
+ }
+ }
+ }
+
+ /** Filters the set of widgets which are on the specified screen. */
+ private void filterCurrentAppWidgets(long currentScreenId,
+ ArrayList<LauncherAppWidgetInfo> appWidgets,
+ ArrayList<LauncherAppWidgetInfo> currentScreenWidgets,
+ ArrayList<LauncherAppWidgetInfo> otherScreenWidgets) {
+
+ for (LauncherAppWidgetInfo widget : appWidgets) {
+ if (widget == null) continue;
+ if (widget.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+ widget.screenId == currentScreenId) {
+ currentScreenWidgets.add(widget);
+ } else {
+ otherScreenWidgets.add(widget);
+ }
+ }
+ }
+
+ /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
+ * right) */
+ private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
+ final InvariantDeviceProfile profile = mApp.getInvariantDeviceProfile();
+ final int screenCols = profile.numColumns;
+ final int screenCellCount = profile.numColumns * profile.numRows;
+ Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
+ @Override
+ public int compare(ItemInfo lhs, ItemInfo rhs) {
+ if (lhs.container == rhs.container) {
+ // Within containers, order by their spatial position in that container
+ switch ((int) lhs.container) {
+ case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
+ long lr = (lhs.screenId * screenCellCount +
+ lhs.cellY * screenCols + lhs.cellX);
+ long rr = (rhs.screenId * screenCellCount +
+ rhs.cellY * screenCols + rhs.cellX);
+ return Utilities.longCompare(lr, rr);
+ }
+ case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
+ // We currently use the screen id as the rank
+ return Utilities.longCompare(lhs.screenId, rhs.screenId);
+ }
+ default:
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ throw new RuntimeException("Unexpected container type when " +
+ "sorting workspace items.");
+ }
+ return 0;
+ }
+ } else {
+ // Between containers, order by hotseat, desktop
+ return Utilities.longCompare(lhs.container, rhs.container);
+ }
+ }
+ });
+ }
+
+ private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems,
+ final ArrayList<LauncherAppWidgetInfo> appWidgets,
+ final Executor executor) {
+
+ // Bind the workspace items
+ int N = workspaceItems.size();
+ for (int i = 0; i < N; i += ITEMS_CHUNK) {
+ final int start = i;
+ final int chunkSize = (i+ITEMS_CHUNK <= N) ? ITEMS_CHUNK : (N-i);
+ final Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindItems(workspaceItems, start, start+chunkSize, false);
+ }
+ }
+ };
+ executor.execute(r);
+ }
+
+ // Bind the widgets, one at a time
+ N = appWidgets.size();
+ for (int i = 0; i < N; i++) {
+ final LauncherAppWidgetInfo widget = appWidgets.get(i);
+ final Runnable r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindAppWidget(widget);
+ }
+ }
+ };
+ executor.execute(r);
+ }
+ }
+
+ public void bindDeepShortcuts() {
+ final MultiHashMap<ComponentKey, String> shortcutMapCopy;
+ synchronized (mBgDataModel) {
+ shortcutMapCopy = mBgDataModel.deepShortcutMap.clone();
+ }
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindDeepShortcutMap(shortcutMapCopy);
+ }
+ }
+ };
+ mUiExecutor.execute(r);
+ }
+
+ public void bindAllApps() {
+ // shallow copy
+ @SuppressWarnings("unchecked")
+ final ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
+
+ Runnable r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindAllApplications(list);
+ }
+ }
+ };
+ mUiExecutor.execute(r);
+ }
+
+ public void bindWidgets() {
+ final MultiHashMap<PackageItemInfo, WidgetItem> widgets
+ = mBgDataModel.widgetsModel.getWidgetsMap();
+ Runnable r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks.get();
+ if (callbacks != null) {
+ callbacks.bindAllWidgets(widgets);
+ }
+ }
+ };
+ mUiExecutor.execute(r);
+ }
+
+ public LooperIdleLock newIdleLock(Object lock) {
+ LooperIdleLock idleLock = new LooperIdleLock(lock, Looper.getMainLooper());
+ // If we are not binding, there is no reason to wait for idle.
+ if (mCallbacks.get() == null) {
+ idleLock.queueIdle();
+ }
+ return idleLock;
+ }
+}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
new file mode 100644
index 0000000..bb2d0b6
--- /dev/null
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -0,0 +1,840 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.PackageInstaller;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.MutableInt;
+
+import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+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.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.folder.FolderIconPreviewVerifier;
+import com.android.launcher3.graphics.LauncherIcons;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.provider.ImportDataTask;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.LooperIdleLock;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CancellationException;
+
+/**
+ * Runnable for the thread that loads the contents of the launcher:
+ * - workspace icons
+ * - widgets
+ * - all apps icons
+ * - deep shortcuts within apps
+ */
+public class LoaderTask implements Runnable {
+ private static final boolean DEBUG_LOADERS = false;
+ private static final String TAG = "LoaderTask";
+
+ private final LauncherAppState mApp;
+ private final AllAppsList mBgAllAppsList;
+ private final BgDataModel mBgDataModel;
+
+ private final LoaderResults mResults;
+
+ private final LauncherAppsCompat mLauncherApps;
+ private final UserManagerCompat mUserManager;
+ private final DeepShortcutManager mShortcutManager;
+ private final PackageInstallerCompat mPackageInstaller;
+ private final AppWidgetManagerCompat mAppWidgetManager;
+ private final IconCache mIconCache;
+
+ private boolean mStopped;
+
+ public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
+ LoaderResults results) {
+ mApp = app;
+ mBgAllAppsList = bgAllAppsList;
+ mBgDataModel = dataModel;
+ mResults = results;
+
+ mLauncherApps = LauncherAppsCompat.getInstance(mApp.getContext());
+ mUserManager = UserManagerCompat.getInstance(mApp.getContext());
+ mShortcutManager = DeepShortcutManager.getInstance(mApp.getContext());
+ mPackageInstaller = PackageInstallerCompat.getInstance(mApp.getContext());
+ mAppWidgetManager = AppWidgetManagerCompat.getInstance(mApp.getContext());
+ mIconCache = mApp.getIconCache();
+ }
+
+ protected synchronized void waitForIdle() {
+ // Wait until the either we're stopped or the other threads are done.
+ // This way we don't start loading all apps until the workspace has settled
+ // down.
+ LooperIdleLock idleLock = mResults.newIdleLock(this);
+ // Just in case mFlushingWorkerThread changes but we aren't woken up,
+ // wait no longer than 1sec at a time
+ while (!mStopped && idleLock.awaitLocked(1000));
+ }
+
+ private synchronized void verifyNotStopped() throws CancellationException {
+ if (mStopped) {
+ throw new CancellationException("Loader stopped");
+ }
+ }
+
+ public void run() {
+ synchronized (this) {
+ // Skip fast if we are already stopped.
+ if (mStopped) {
+ return;
+ }
+ }
+
+ try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
+ long now = 0;
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
+ loadWorkspace();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 1.2: bind workspace workspace");
+ mResults.bindWorkspace();
+
+ // Take a break
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 1 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
+ waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+ verifyNotStopped();
+
+ // second step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.1: loading all apps");
+ loadAllApps();
+
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.2: Binding all apps");
+ verifyNotStopped();
+ mResults.bindAllApps();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 2.3: Update icon cache");
+ updateIconCache();
+
+ // Take a break
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "step 2 completed, wait for idle");
+ now = SystemClock.uptimeMillis();
+ }
+ waitForIdle();
+ if (DEBUG_LOADERS) Log.d(TAG, "Waited " + (SystemClock.uptimeMillis() - now) + "ms");
+ verifyNotStopped();
+
+ // third step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.1: loading deep shortcuts");
+ loadDeepShortcuts();
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3.2: bind deep shortcuts");
+ mResults.bindDeepShortcuts();
+
+ // Take a break
+ if (DEBUG_LOADERS) Log.d(TAG, "step 3 completed, wait for idle");
+ waitForIdle();
+ verifyNotStopped();
+
+ // fourth step
+ if (DEBUG_LOADERS) Log.d(TAG, "step 4.1: loading widgets");
+ mBgDataModel.widgetsModel.update(mApp, null);
+
+ verifyNotStopped();
+ if (DEBUG_LOADERS) Log.d(TAG, "step 4.2: Binding widgets");
+ mResults.bindWidgets();
+
+ transaction.commit();
+ } catch (CancellationException e) {
+ // Loader stopped, ignore
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "Loader cancelled", e);
+ }
+ }
+ }
+
+ public synchronized void stopLocked() {
+ mStopped = true;
+ this.notify();
+ }
+
+ private void loadWorkspace() {
+ if (LauncherAppState.PROFILE_STARTUP) {
+ Trace.beginSection("Loading Workspace");
+ }
+
+ final Context context = mApp.getContext();
+ final ContentResolver contentResolver = context.getContentResolver();
+ final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
+ final boolean isSafeMode = pmHelper.isSafeMode();
+ final boolean isSdCardReady = Utilities.isBootCompleted();
+ final MultiHashMap<UserHandle, String> pendingPackages = new MultiHashMap<>();
+
+ boolean clearDb = false;
+ try {
+ ImportDataTask.performImportIfPossible(context);
+ } catch (Exception e) {
+ // Migration failed. Clear workspace.
+ clearDb = true;
+ }
+
+ if (!clearDb && GridSizeMigrationTask.ENABLED &&
+ !GridSizeMigrationTask.migrateGridIfNeeded(context)) {
+ // Migration failed. Clear workspace.
+ clearDb = true;
+ }
+
+ if (clearDb) {
+ Log.d(TAG, "loadWorkspace: resetting launcher database");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+ }
+
+ Log.d(TAG, "loadWorkspace: loading default favorites");
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
+
+ synchronized (mBgDataModel) {
+ mBgDataModel.clear();
+
+ final HashMap<String, Integer> installingPkgs =
+ mPackageInstaller.updateAndGetActiveSessionCache();
+ mBgDataModel.workspaceScreens.addAll(LauncherModel.loadWorkspaceScreensDb(context));
+
+ Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
+ final LoaderCursor c = new LoaderCursor(contentResolver.query(
+ LauncherSettings.Favorites.CONTENT_URI, null, null, null, null), mApp);
+
+ HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
+
+ try {
+ final int appWidgetIdIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_ID);
+ final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.APPWIDGET_PROVIDER);
+ final int spanXIndex = c.getColumnIndexOrThrow
+ (LauncherSettings.Favorites.SPANX);
+ final int spanYIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.SPANY);
+ final int rankIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.RANK);
+ final int optionsIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.OPTIONS);
+
+ final LongSparseArray<UserHandle> allUsers = c.allUsers;
+ final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
+ final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ long serialNo = mUserManager.getSerialNumberForUser(user);
+ allUsers.put(serialNo, user);
+ quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
+
+ boolean userUnlocked = mUserManager.isUserUnlocked(user);
+
+ // We can only query for shortcuts when the user is unlocked.
+ if (userUnlocked) {
+ List<ShortcutInfoCompat> pinnedShortcuts =
+ mShortcutManager.queryForPinnedShortcuts(null, user);
+ if (mShortcutManager.wasLastCallSuccess()) {
+ for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+ shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+ shortcut);
+ }
+ } else {
+ // Shortcut manager can fail due to some race condition when the
+ // lock state changes too frequently. For the purpose of the loading
+ // shortcuts, consider the user is still locked.
+ userUnlocked = false;
+ }
+ }
+ unlockedUsers.put(serialNo, userUnlocked);
+ }
+
+ ShortcutInfo info;
+ LauncherAppWidgetInfo appWidgetInfo;
+ Intent intent;
+ String targetPkg;
+
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ while (!mStopped && c.moveToNext()) {
+ try {
+ if (c.user == null) {
+ // User has been deleted, remove the item.
+ c.markDeleted("User has been deleted");
+ continue;
+ }
+
+ boolean allowMissingTarget = false;
+ switch (c.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ intent = c.parseIntent();
+ if (intent == null) {
+ c.markDeleted("Invalid or null intent");
+ continue;
+ }
+
+ int disabledState = quietMode.get(c.serialNumber) ?
+ ShortcutInfo.FLAG_DISABLED_QUIET_USER : 0;
+ ComponentName cn = intent.getComponent();
+ targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();
+
+ if (!Process.myUserHandle().equals(c.user)) {
+ if (c.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ c.markDeleted("Legacy shortcuts are only allowed for default user");
+ continue;
+ } else if (c.restoreFlag != 0) {
+ // Don't restore items for other profiles.
+ c.markDeleted("Restore from managed profile not supported");
+ continue;
+ }
+ }
+ if (TextUtils.isEmpty(targetPkg) &&
+ c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
+ c.markDeleted("Only legacy shortcuts can have null package");
+ continue;
+ }
+
+ // If there is no target package, its an implicit intent
+ // (legacy shortcut) which is always valid
+ boolean validTarget = TextUtils.isEmpty(targetPkg) ||
+ mLauncherApps.isPackageEnabledForProfile(targetPkg, c.user);
+
+ if (cn != null && validTarget) {
+ // If the apk is present and the shortcut points to a specific
+ // component.
+
+ // If the component is already present
+ if (mLauncherApps.isActivityEnabledForProfile(cn, c.user)) {
+ // no special handling necessary for this item
+ c.markRestored();
+ } else {
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
+ // We allow auto install apps to have their intent
+ // updated after an install.
+ intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
+ if (intent != null) {
+ c.restoreFlag = 0;
+ c.updater().put(
+ LauncherSettings.Favorites.INTENT,
+ intent.toUri(0)).commit();
+ cn = intent.getComponent();
+ } else {
+ c.markDeleted("Unable to find a launch target");
+ continue;
+ }
+ } else {
+ // The app is installed but the component is no
+ // longer available.
+ c.markDeleted("Invalid component removed: " + cn);
+ continue;
+ }
+ }
+ }
+ // else if cn == null => can't infer much, leave it
+ // else if !validPkg => could be restored icon or missing sd-card
+
+ if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
+ // Points to a valid app (superset of cn != null) but the apk
+ // is not available.
+
+ if (c.restoreFlag != 0) {
+ // Package is not yet available but might be
+ // installed later.
+ FileLog.d(TAG, "package not yet restored: " + targetPkg);
+
+ if (c.hasRestoreFlag(ShortcutInfo.FLAG_RESTORE_STARTED)) {
+ // Restore has started once.
+ } else if (installingPkgs.containsKey(targetPkg)) {
+ // App restore has started. Update the flag
+ c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
+ c.updater().commit();
+ } else {
+ c.markDeleted("Unrestored app removed: " + targetPkg);
+ continue;
+ }
+ } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
+ // Package is present but not available.
+ disabledState |= ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+ // Add the icon on the workspace anyway.
+ allowMissingTarget = true;
+ } else if (!isSdCardReady) {
+ // SdCard is not ready yet. Package might get available,
+ // once it is ready.
+ Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
+ pendingPackages.addToList(c.user, targetPkg);
+ // Add the icon on the workspace anyway.
+ allowMissingTarget = true;
+ } else {
+ // Do not wait for external media load anymore.
+ c.markDeleted("Invalid package removed: " + targetPkg);
+ continue;
+ }
+ }
+
+ if (validTarget) {
+ // The shortcut points to a valid target (either no target
+ // or something which is ready to be used)
+ c.markRestored();
+ }
+
+ boolean useLowResIcon = !c.isOnWorkspaceOrHotseat() &&
+ !verifier.isItemInPreview(c.getInt(rankIndex));
+
+ if (c.restoreFlag != 0) {
+ // Already verified above that user is same as default user
+ info = c.getRestoredItemInfo(intent);
+ } else if (c.itemType ==
+ LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ info = c.getAppShortcutInfo(
+ intent, allowMissingTarget, useLowResIcon);
+ } else if (c.itemType ==
+ LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+
+ ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
+ if (unlockedUsers.get(c.serialNumber)) {
+ ShortcutInfoCompat pinnedShortcut =
+ shortcutKeyToPinnedShortcuts.get(key);
+ if (pinnedShortcut == null) {
+ // The shortcut is no longer valid.
+ c.markDeleted("Pinned shortcut not found");
+ continue;
+ }
+ info = new ShortcutInfo(pinnedShortcut, context);
+ info.iconBitmap = LauncherIcons
+ .createShortcutIcon(pinnedShortcut, context);
+ if (pmHelper.isAppSuspended(
+ pinnedShortcut.getPackage(), info.user)) {
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+ intent = info.intent;
+ } else {
+ // Create a shortcut info in disabled mode for now.
+ info = c.loadSimpleShortcut();
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+ }
+ } else { // item type == ITEM_TYPE_SHORTCUT
+ info = c.loadSimpleShortcut();
+
+ // Shortcuts are only available on the primary profile
+ if (!TextUtils.isEmpty(targetPkg)
+ && pmHelper.isAppSuspended(targetPkg, c.user)) {
+ disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+
+ // App shortcuts that used to be automatically added to Launcher
+ // didn't always have the correct intent flags set, so do that
+ // here
+ if (intent.getAction() != null &&
+ intent.getCategories() != null &&
+ intent.getAction().equals(Intent.ACTION_MAIN) &&
+ intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
+ intent.addFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ }
+ }
+
+ if (info != null) {
+ c.applyCommonProperties(info);
+
+ info.intent = intent;
+ info.rank = c.getInt(rankIndex);
+ info.spanX = 1;
+ info.spanY = 1;
+ info.isDisabled |= disabledState;
+ if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
+ info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
+ }
+
+ if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
+ Integer progress = installingPkgs.get(targetPkg);
+ if (progress != null) {
+ info.setInstallProgress(progress);
+ } else {
+ info.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+ }
+ }
+
+ c.checkAndAddItem(info, mBgDataModel);
+ } else {
+ throw new RuntimeException("Unexpected null ShortcutInfo");
+ }
+ break;
+
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
+ c.applyCommonProperties(folderInfo);
+
+ // Do not trim the folder label, as is was set by the user.
+ folderInfo.title = c.getString(c.titleIndex);
+ folderInfo.spanX = 1;
+ folderInfo.spanY = 1;
+ folderInfo.options = c.getInt(optionsIndex);
+
+ // no special handling required for restored folders
+ c.markRestored();
+
+ c.checkAndAddItem(folderInfo, mBgDataModel);
+ break;
+
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+ // Read all Launcher-specific widget details
+ boolean customWidget = c.itemType ==
+ LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
+
+ int appWidgetId = c.getInt(appWidgetIdIndex);
+ String savedProvider = c.getString(appWidgetProviderIndex);
+
+ final ComponentName component =
+ ComponentName.unflattenFromString(savedProvider);
+
+ final boolean isIdValid = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+ final boolean wasProviderReady = !c.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);
+
+ if (widgetProvidersMap == null) {
+ widgetProvidersMap = mAppWidgetManager.getAllProvidersMap();
+ }
+ final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+ new ComponentKey(
+ ComponentName.unflattenFromString(savedProvider),
+ c.user));
+
+ final boolean isProviderReady = isValidProvider(provider);
+ if (!isSafeMode && !customWidget &&
+ wasProviderReady && !isProviderReady) {
+ c.markDeleted(
+ "Deleting widget that isn't installed anymore: "
+ + provider);
+ } else {
+ if (isProviderReady) {
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ provider.provider);
+
+ // The provider is available. So the widget is either
+ // available or not available. We do not need to track
+ // any future restore updates.
+ int status = c.restoreFlag &
+ ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED &
+ ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+ if (!wasProviderReady) {
+ // If provider was not previously ready, update the
+ // status and UI flag.
+
+ // Id would be valid only if the widget restore broadcast was received.
+ if (isIdValid) {
+ status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ }
+ }
+ appWidgetInfo.restoreStatus = status;
+ } else {
+ Log.v(TAG, "Widget restore pending id=" + c.id
+ + " appWidgetId=" + appWidgetId
+ + " status =" + c.restoreFlag);
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ component);
+ appWidgetInfo.restoreStatus = c.restoreFlag;
+ Integer installProgress = installingPkgs.get(component.getPackageName());
+
+ if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
+ // Restore has started once.
+ } else if (installProgress != null) {
+ // App restore has started. Update the flag
+ appWidgetInfo.restoreStatus |=
+ LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+ } else if (!isSafeMode) {
+ c.markDeleted("Unrestored widget removed: " + component);
+ continue;
+ }
+
+ appWidgetInfo.installProgress =
+ installProgress == null ? 0 : installProgress;
+ }
+ if (appWidgetInfo.hasRestoreFlag(
+ LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+ appWidgetInfo.bindOptions = c.parseIntent();
+ }
+
+ c.applyCommonProperties(appWidgetInfo);
+ appWidgetInfo.spanX = c.getInt(spanXIndex);
+ appWidgetInfo.spanY = c.getInt(spanYIndex);
+ appWidgetInfo.user = c.user;
+
+ if (!c.isOnWorkspaceOrHotseat()) {
+ c.markDeleted("Widget found where container != " +
+ "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+ continue;
+ }
+
+ if (!customWidget) {
+ String providerName =
+ appWidgetInfo.providerName.flattenToString();
+ if (!providerName.equals(savedProvider) ||
+ (appWidgetInfo.restoreStatus != c.restoreFlag)) {
+ c.updater()
+ .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+ providerName)
+ .put(LauncherSettings.Favorites.RESTORED,
+ appWidgetInfo.restoreStatus)
+ .commit();
+ }
+ }
+
+ if (appWidgetInfo.restoreStatus !=
+ LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ String pkg = appWidgetInfo.providerName.getPackageName();
+ appWidgetInfo.pendingItemInfo = new PackageItemInfo(pkg);
+ appWidgetInfo.pendingItemInfo.user = appWidgetInfo.user;
+ mIconCache.getTitleAndIconForApp(
+ appWidgetInfo.pendingItemInfo, false);
+ }
+
+ c.checkAndAddItem(appWidgetInfo, mBgDataModel);
+ }
+ break;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Desktop items loading interrupted", e);
+ }
+ }
+ } finally {
+ Utilities.closeSilently(c);
+ }
+
+ // Break early if we've stopped loading
+ if (mStopped) {
+ mBgDataModel.clear();
+ return;
+ }
+
+ // Remove dead items
+ if (c.commitDeleted()) {
+ // Remove any empty folder
+ ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+ .call(contentResolver,
+ LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+ .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
+ for (long folderId : deletedFolderIds) {
+ mBgDataModel.workspaceItems.remove(mBgDataModel.folders.get(folderId));
+ mBgDataModel.folders.remove(folderId);
+ mBgDataModel.itemsIdMap.remove(folderId);
+ }
+
+ // Remove any ghost widgets
+ LauncherSettings.Settings.call(contentResolver,
+ LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
+ }
+
+ // Unpin shortcuts that don't exist on the workspace.
+ HashSet<ShortcutKey> pendingShortcuts =
+ InstallShortcutReceiver.getPendingShortcuts(context);
+ for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
+ MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
+ if ((numTimesPinned == null || numTimesPinned.value == 0)
+ && !pendingShortcuts.contains(key)) {
+ // Shortcut is pinned but doesn't exist on the workspace; unpin it.
+ mShortcutManager.unpinShortcut(key);
+ }
+ }
+
+ FolderIconPreviewVerifier verifier =
+ new FolderIconPreviewVerifier(mApp.getInvariantDeviceProfile());
+ // Sort the folder items and make sure all items in the preview are high resolution.
+ for (FolderInfo folder : mBgDataModel.folders) {
+ Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+ verifier.setFolderInfo(folder);
+
+ int numItemsInPreview = 0;
+ for (ShortcutInfo info : folder.contents) {
+ if (info.usingLowResIcon
+ && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+ && verifier.isItemInPreview(info.rank)) {
+ mIconCache.getTitleAndIcon(info, false);
+ numItemsInPreview++;
+ }
+
+ if (numItemsInPreview >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ }
+
+ c.commitRestoredItems();
+ if (!isSdCardReady && !pendingPackages.isEmpty()) {
+ context.registerReceiver(
+ new SdCardAvailableReceiver(mApp, pendingPackages),
+ new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
+ null,
+ new Handler(LauncherModel.getWorkerLooper()));
+ }
+
+ // Remove any empty screens
+ ArrayList<Long> unusedScreens = new ArrayList<>(mBgDataModel.workspaceScreens);
+ for (ItemInfo item: mBgDataModel.itemsIdMap) {
+ long screenId = item.screenId;
+ if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
+ unusedScreens.contains(screenId)) {
+ unusedScreens.remove(screenId);
+ }
+ }
+
+ // If there are any empty screens remove them, and update.
+ if (unusedScreens.size() != 0) {
+ mBgDataModel.workspaceScreens.removeAll(unusedScreens);
+ LauncherModel.updateWorkspaceScreenOrder(context, mBgDataModel.workspaceScreens);
+ }
+ }
+ if (LauncherAppState.PROFILE_STARTUP) {
+ Trace.endSection();
+ }
+ }
+
+ private void updateIconCache() {
+ // Ignore packages which have a promise icon.
+ HashSet<String> packagesToIgnore = new HashSet<>();
+ synchronized (mBgDataModel) {
+ for (ItemInfo info : mBgDataModel.itemsIdMap) {
+ if (info instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) info;
+ if (si.isPromise() && si.getTargetComponent() != null) {
+ packagesToIgnore.add(si.getTargetComponent().getPackageName());
+ }
+ } else if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo lawi = (LauncherAppWidgetInfo) info;
+ if (lawi.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+ packagesToIgnore.add(lawi.providerName.getPackageName());
+ }
+ }
+ }
+ }
+ mIconCache.updateDbIcons(packagesToIgnore);
+ }
+
+ private void loadAllApps() {
+ final long loadTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+
+ final List<UserHandle> profiles = mUserManager.getUserProfiles();
+
+ // Clear the list of apps
+ mBgAllAppsList.clear();
+ for (UserHandle user : profiles) {
+ // Query for the set of apps
+ final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+ final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "getActivityList took "
+ + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
+ Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
+ }
+ // Fail if we don't have any apps
+ // TODO: Fix this. Only fail for the current user.
+ if (apps == null || apps.isEmpty()) {
+ return;
+ }
+ boolean quietMode = mUserManager.isQuietModeEnabled(user);
+ // Create the ApplicationInfos
+ for (int i = 0; i < apps.size(); i++) {
+ LauncherActivityInfo app = apps.get(i);
+ // This builds the icon bitmaps.
+ mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
+ }
+
+ ManagedProfileHeuristic.onAllAppsLoaded(mApp.getContext(), apps, user);
+ }
+
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ // get all active sessions and add them to the all apps list
+ for (PackageInstaller.SessionInfo info :
+ mPackageInstaller.getAllVerifiedSessions()) {
+ mBgAllAppsList.addPromiseApp(mApp.getContext(),
+ PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
+ }
+ }
+
+ mBgAllAppsList.added = new ArrayList<>();
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "All apps loaded in in "
+ + (SystemClock.uptimeMillis() - loadTime) + "ms");
+ }
+ }
+
+ private void loadDeepShortcuts() {
+ mBgDataModel.deepShortcutMap.clear();
+ mBgDataModel.hasShortcutHostPermission = mShortcutManager.hasHostPermission();
+ if (mBgDataModel.hasShortcutHostPermission) {
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ if (mUserManager.isUserUnlocked(user)) {
+ List<ShortcutInfoCompat> shortcuts =
+ mShortcutManager.queryForAllShortcuts(user);
+ mBgDataModel.updateDeepShortcutMap(null, user, shortcuts);
+ }
+ }
+ }
+ }
+
+ public static boolean isValidProvider(AppWidgetProviderInfo provider) {
+ return (provider != null) && (provider.provider != null)
+ && (provider.provider.getPackageName() != null);
+ }
+}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index 4931dca..032ed78 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -34,7 +34,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -55,7 +55,7 @@
public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
mContext = context;
mBgDataModel = dataModel;
- mWorkerExecutor = new LooperExecuter(LauncherModel.getWorkerLooper());
+ mWorkerExecutor = new LooperExecutor(LauncherModel.getWorkerLooper());
mHasVerticalHotseat = hasVerticalHotseat;
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index 5d04325..1e0af68 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -18,21 +18,24 @@
import android.content.ComponentName;
import com.android.launcher3.AllAppsList;
+import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.Callbacks;
+import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import java.util.ArrayList;
import java.util.HashSet;
/**
* Handles changes due to a sessions updates for a currently installing app.
*/
-public class PackageInstallStateChangedTask extends ExtendedModelTask {
+public class PackageInstallStateChangedTask extends BaseModelUpdateTask {
private final PackageInstallInfo mInstallInfo;
@@ -47,6 +50,44 @@
return;
}
+ synchronized (apps) {
+ PromiseAppInfo updated = null;
+ final ArrayList<AppInfo> removed = new ArrayList<>();
+ for (int i=0; i < apps.size(); i++) {
+ final AppInfo appInfo = apps.get(i);
+ final ComponentName tgtComp = appInfo.getTargetComponent();
+ if (tgtComp != null && tgtComp.getPackageName().equals(mInstallInfo.packageName)) {
+ if (appInfo instanceof PromiseAppInfo) {
+ final PromiseAppInfo promiseAppInfo = (PromiseAppInfo) appInfo;
+ if (mInstallInfo.state == PackageInstallerCompat.STATUS_INSTALLING) {
+ promiseAppInfo.level = mInstallInfo.progress;
+ updated = promiseAppInfo;
+ } else if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+ apps.removePromiseApp(appInfo);
+ removed.add(appInfo);
+ }
+ }
+ }
+ }
+ if (updated != null) {
+ final PromiseAppInfo updatedPromiseApp = updated;
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindPromiseAppProgressUpdated(updatedPromiseApp);
+ }
+ });
+ }
+ if (!removed.isEmpty()) {
+ scheduleCallbackTask(new CallbackTask() {
+ @Override
+ public void execute(Callbacks callbacks) {
+ callbacks.bindAppInfosRemoved(removed);
+ }
+ });
+ }
+ }
+
synchronized (dataModel) {
final HashSet<ItemInfo> updates = new HashSet<>();
for (ItemInfo info : dataModel.itemsIdMap) {
@@ -56,7 +97,6 @@
if (si.isPromise() && (cn != null)
&& mInstallInfo.packageName.equals(cn.getPackageName())) {
si.setInstallProgress(mInstallInfo.progress);
-
if (mInstallInfo.state == PackageInstallerCompat.STATUS_FAILED) {
// Mark this info as broken.
si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index f03c9c7..1b2f8d6 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -18,12 +18,11 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
+import android.os.Process;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.Log;
-
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppInfo;
import com.android.launcher3.IconCache;
@@ -31,32 +30,31 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.Callbacks;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.ItemInfoMatcher;
-import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
/**
* Handles updates due to changes in package manager (app installed/updated/removed)
* or when a user availability changes.
*/
-public class PackageUpdatedTask extends ExtendedModelTask {
+public class PackageUpdatedTask extends BaseModelUpdateTask {
private static final boolean DEBUG = false;
private static final String TAG = "PackageUpdatedTask";
@@ -95,12 +93,15 @@
for (int i = 0; i < N; i++) {
if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
+ if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
+ appsList.removePackage(packages[i], Process.myUserHandle());
+ }
appsList.addPackage(context, packages[i], mUser);
- }
- ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
- if (heuristic != null) {
- heuristic.processPackageAdd(mPackages);
+ // Automatically add homescreen icon for work profile apps for below O device.
+ if (!Utilities.isAtLeastO() && !Process.myUserHandle().equals(mUser)) {
+ SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
+ }
}
break;
}
@@ -115,10 +116,6 @@
flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_REMOVE: {
- ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
- if (heuristic != null) {
- heuristic.processPackageRemoved(mPackages);
- }
for (int i = 0; i < N; i++) {
iconCache.removeIconsForPkg(packages[i], mUser);
}
@@ -152,7 +149,7 @@
ArrayList<AppInfo> added = null;
ArrayList<AppInfo> modified = null;
- final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
+ final ArrayList<AppInfo> removedApps = new ArrayList<>();
if (appsList.added.size() > 0) {
added = new ArrayList<>(appsList.added);
@@ -167,7 +164,7 @@
appsList.removed.clear();
}
- final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
+ final ArrayMap<ComponentName, AppInfo> addedOrUpdatedApps = new ArrayMap<>();
if (added != null) {
final ArrayList<AppInfo> addedApps = added;
@@ -222,18 +219,17 @@
if (cn != null && matcher.matches(si, cn)) {
AppInfo appInfo = addedOrUpdatedApps.get(cn);
- if (si.isPromise() && mOp == OP_ADD) {
- if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+ // For system apps, package manager send OP_UPDATE when an
+ // app is enabled.
+ if (si.isPromise() && (mOp == OP_ADD || mOp == OP_UPDATE)) {
+ if (si.hasStatusFlag(ShortcutInfo.FLAG_AUTOINSTALL_ICON)) {
// Auto install icon
- PackageManager pm = context.getPackageManager();
- ResolveInfo matched = pm.resolveActivity(
- new Intent(Intent.ACTION_MAIN)
- .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
- PackageManager.MATCH_DEFAULT_ONLY);
- if (matched == null) {
+ LauncherAppsCompat launcherApps
+ = LauncherAppsCompat.getInstance(context);
+ if (!launcherApps.isActivityEnabledForProfile(cn, mUser)) {
// Try to find the best match activity.
- Intent intent = pm.getLaunchIntentForPackage(
- cn.getPackageName());
+ Intent intent = new PackageManagerHelper(context)
+ .getAppLaunchIntent(cn.getPackageName(), mUser);
if (intent != null) {
cn = intent.getComponent();
appInfo = addedOrUpdatedApps.get(cn);
@@ -374,11 +370,9 @@
} else if (Utilities.isAtLeastO() && mOp == OP_ADD) {
// Load widgets for the new package.
for (int i = 0; i < N; i++) {
- LauncherModel model = app.getModel();
- model.refreshAndBindWidgetsAndShortcuts(
- model.getCallback(), false /* bindFirst */,
- new PackageUserKey(packages[i], mUser) /* packageUser */);
+ dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
}
+ bindUpdatedWidgets(dataModel);
}
}
}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 278669b..3aedae6 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -19,9 +19,9 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.UserHandle;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.util.MultiHashMap;
@@ -44,10 +44,10 @@
private final Context mContext;
private final MultiHashMap<UserHandle, String> mPackages;
- public SdCardAvailableReceiver(LauncherModel model, Context context,
+ public SdCardAvailableReceiver(LauncherAppState app,
MultiHashMap<UserHandle, String> packages) {
- mModel = model;
- mContext = context;
+ mModel = app.getModel();
+ mContext = app.getContext();
mPackages = packages;
}
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index d8a429c..6f32585 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -21,7 +21,6 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.graphics.LauncherIcons;
@@ -35,7 +34,7 @@
/**
* Handles changes due to shortcut manager updates (deep shortcut changes)
*/
-public class ShortcutsChangedTask extends ExtendedModelTask {
+public class ShortcutsChangedTask extends BaseModelUpdateTask {
private final String mPackageName;
private final List<ShortcutInfoCompat> mShortcuts;
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 363f1ee..5682006 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -21,7 +21,6 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.compat.UserManagerCompat;
@@ -39,7 +38,7 @@
/**
* Task to handle changing of lock state of the user
*/
-public class UserLockStateChangedTask extends ExtendedModelTask {
+public class UserLockStateChangedTask extends BaseModelUpdateTask {
private final UserHandle mUser;
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index e5215c7..ed900bf 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -18,7 +18,7 @@
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.MultiHashMap;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.Preconditions;
@@ -38,36 +38,26 @@
private static final boolean DEBUG = false;
/* Map of widgets and shortcuts that are tracked per package. */
- private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList;
+ private final MultiHashMap<PackageItemInfo, WidgetItem> mWidgetsList = new MultiHashMap<>();
- private final IconCache mIconCache;
- private final AppFilter mAppFilter;
+ private AppFilter mAppFilter;
- public WidgetsModel(IconCache iconCache, AppFilter appFilter) {
- mIconCache = iconCache;
- mAppFilter = appFilter;
- mWidgetsList = new MultiHashMap<>();
- }
-
- public MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
- return mWidgetsList;
- }
-
- public boolean isEmpty() {
- return mWidgetsList.isEmpty();
+ public synchronized MultiHashMap<PackageItemInfo, WidgetItem> getWidgetsMap() {
+ return mWidgetsList.clone();
}
/**
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public ArrayList<WidgetItem> update(Context context, @Nullable PackageUserKey packageUser) {
+ public void update(LauncherAppState app, @Nullable PackageUserKey packageUser) {
Preconditions.assertWorkerThread();
+ Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
try {
PackageManager pm = context.getPackageManager();
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
// Widgets
AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
@@ -81,9 +71,9 @@
.getCustomShortcutActivityList(packageUser)) {
widgetsAndShortcuts.add(new WidgetItem(info));
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, context, packageUser);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
} catch (Exception e) {
- if (!ProviderConfig.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+ if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
// time Launcher starts.
// TODO: after figuring out a repro step, introduce a dirty bit to check when
@@ -92,11 +82,12 @@
throw e;
}
}
- return widgetsAndShortcuts;
+
+ app.getWidgetCache().removeObsoletePreviews(widgetsAndShortcuts, packageUser);
}
- private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- Context context, @Nullable PackageUserKey packageUser) {
+ private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
+ LauncherAppState app, @Nullable PackageUserKey packageUser) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
@@ -133,7 +124,7 @@
}
}
- InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
+ InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
UserHandle myUser = Process.myUserHandle();
// add and update.
@@ -152,6 +143,9 @@
}
}
+ if (mAppFilter == null) {
+ mAppFilter = AppFilter.newInstance(app.getContext());
+ }
if (!mAppFilter.shouldShowApp(item.componentName)) {
if (DEBUG) {
Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
@@ -174,8 +168,9 @@
}
// Update each package entry
+ IconCache iconCache = app.getIconCache();
for (PackageItemInfo p : tmpPackageItemInfos.values()) {
- mIconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
+ iconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
}
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/notification/NotificationFooterLayout.java b/src/com/android/launcher3/notification/NotificationFooterLayout.java
index 051c033..b83c9b9 100644
--- a/src/com/android/launcher3/notification/NotificationFooterLayout.java
+++ b/src/com/android/launcher3/notification/NotificationFooterLayout.java
@@ -205,6 +205,7 @@
collapseFooter.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ ((ViewGroup) getParent()).findViewById(R.id.divider).setVisibility(GONE);
((ViewGroup) getParent()).removeView(NotificationFooterLayout.this);
}
});
diff --git a/src/com/android/launcher3/notification/NotificationInfo.java b/src/com/android/launcher3/notification/NotificationInfo.java
index 1a93e11..1b7c87b 100644
--- a/src/com/android/launcher3/notification/NotificationInfo.java
+++ b/src/com/android/launcher3/notification/NotificationInfo.java
@@ -27,6 +27,7 @@
import android.service.notification.StatusBarNotification;
import android.view.View;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.graphics.IconPalette;
@@ -105,7 +106,8 @@
if (autoCancel) {
launcher.getPopupDataProvider().cancelNotification(notificationKey);
}
- PopupContainerWithArrow.getOpen(launcher).close(true);
+ AbstractFloatingView.closeOpenContainer(launcher, AbstractFloatingView
+ .TYPE_POPUP_CONTAINER_WITH_ARROW);
}
public Drawable getIconForBackground(Context context, int background) {
diff --git a/src/com/android/launcher3/notification/NotificationItemView.java b/src/com/android/launcher3/notification/NotificationItemView.java
index 997def2..0cd5a4c 100644
--- a/src/com/android/launcher3/notification/NotificationItemView.java
+++ b/src/com/android/launcher3/notification/NotificationItemView.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
@@ -30,11 +29,12 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
-import com.android.launcher3.anim.PillHeightRevealOutlineProvider;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
import com.android.launcher3.popup.PopupItemView;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.Themes;
import java.util.List;
@@ -48,6 +48,7 @@
private static final Rect sTempRect = new Rect();
+ private TextView mHeaderText;
private TextView mHeaderCount;
private NotificationMainView mMainView;
private NotificationFooterLayout mFooter;
@@ -70,6 +71,7 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mHeaderText = (TextView) findViewById(R.id.notification_text);
mHeaderCount = (TextView) findViewById(R.id.notification_count);
mMainView = (NotificationMainView) findViewById(R.id.main_view);
mFooter = (NotificationFooterLayout) findViewById(R.id.footer);
@@ -86,10 +88,16 @@
return getHeight() - footerHeight;
}
- public Animator animateHeightRemoval(int heightToRemove) {
- final int newHeight = getHeight() - heightToRemove;
- return new PillHeightRevealOutlineProvider(mPillRect,
- getBackgroundRadius(), newHeight).createRevealAnimator(this, true /* isReversed */);
+ public Animator animateHeightRemoval(int heightToRemove, boolean shouldRemoveFromTop) {
+ Rect startRect = new Rect(mPillRect);
+ Rect endRect = new Rect(mPillRect);
+ if (shouldRemoveFromTop) {
+ endRect.top += heightToRemove;
+ } else {
+ endRect.bottom -= heightToRemove;
+ }
+ return new RoundedRectRevealOutlineProvider(getBackgroundRadius(), getBackgroundRadius(),
+ startRect, endRect, mRoundedCorners).createRevealAnimator(this, false);
}
public void updateHeader(int notificationCount, @Nullable IconPalette palette) {
@@ -98,8 +106,9 @@
if (mNotificationHeaderTextColor == Notification.COLOR_DEFAULT) {
mNotificationHeaderTextColor =
IconPalette.resolveContrastColor(getContext(), palette.dominantColor,
- getResources().getColor(R.color.popup_header_background_color));
+ Themes.getAttrColor(getContext(), R.attr.popupColorPrimary));
}
+ mHeaderText.setTextColor(mNotificationHeaderTextColor);
mHeaderCount.setTextColor(mNotificationHeaderTextColor);
}
}
@@ -163,13 +172,6 @@
}
@Override
- public int getArrowColor(boolean isArrowAttachedToBottom) {
- return ContextCompat.getColor(getContext(), isArrowAttachedToBottom
- ? R.color.popup_background_color
- : R.color.popup_header_background_color);
- }
-
- @Override
public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
LauncherLogProto.Target targetParent) {
target.itemType = LauncherLogProto.ItemType.NOTIFICATION;
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 8dca699..8121fd5 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -16,25 +16,25 @@
package com.android.launcher3.notification;
+import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.support.annotation.Nullable;
-import android.support.v4.util.Pair;
import android.text.TextUtils;
-
+import android.util.ArraySet;
+import android.util.Pair;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageUserKey;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -44,6 +44,7 @@
* as well and when this service first connects. An instance of NotificationListener,
* and its methods for getting notifications, can be obtained via {@link #getInstanceIfConnected()}.
*/
+@TargetApi(Build.VERSION_CODES.O)
public class NotificationListener extends NotificationListenerService {
private static final int MSG_NOTIFICATION_POSTED = 1;
@@ -57,9 +58,9 @@
private final Handler mWorkerHandler;
private final Handler mUiHandler;
- private Ranking mTempRanking = new Ranking();
+ private final Ranking mTempRanking = new Ranking();
- private Handler.Callback mWorkerCallback = new Handler.Callback() {
+ private final Handler.Callback mWorkerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
@@ -80,7 +81,7 @@
}
};
- private Handler.Callback mUiCallback = new Handler.Callback() {
+ private final Handler.Callback mUiCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message message) {
switch (message.what) {
@@ -126,8 +127,9 @@
}
sNotificationsChangedListener = listener;
- if (sNotificationListenerInstance != null) {
- sNotificationListenerInstance.onNotificationFullRefresh();
+ NotificationListener notificationListener = getInstanceIfConnected();
+ if (notificationListener != null) {
+ notificationListener.onNotificationFullRefresh();
}
}
@@ -163,9 +165,9 @@
* An object containing data to send to MSG_NOTIFICATION_POSTED targets.
*/
private class NotificationPostedMsg {
- PackageUserKey packageUserKey;
- NotificationKeyData notificationKey;
- boolean shouldBeFilteredOut;
+ final PackageUserKey packageUserKey;
+ final NotificationKeyData notificationKey;
+ final boolean shouldBeFilteredOut;
NotificationPostedMsg(StatusBarNotification sbn) {
packageUserKey = PackageUserKey.fromNotification(sbn);
@@ -189,7 +191,8 @@
StatusBarNotification[] notifications = NotificationListener.this
.getActiveNotifications(NotificationKeyData.extractKeysOnly(keys)
.toArray(new String[keys.size()]));
- return notifications == null ? Collections.EMPTY_LIST : Arrays.asList(notifications);
+ return notifications == null
+ ? Collections.<StatusBarNotification>emptyList() : Arrays.asList(notifications);
}
/**
@@ -201,7 +204,7 @@
private List<StatusBarNotification> filterNotifications(
StatusBarNotification[] notifications) {
if (notifications == null) return null;
- Set<Integer> removedNotifications = new HashSet<>();
+ Set<Integer> removedNotifications = new ArraySet<>();
for (int i = 0; i < notifications.length; i++) {
if (shouldBeFilteredOut(notifications[i])) {
removedNotifications.add(i);
diff --git a/src/com/android/launcher3/notification/NotificationMainView.java b/src/com/android/launcher3/notification/NotificationMainView.java
index 0d6da77..9b8dd64 100644
--- a/src/com/android/launcher3/notification/NotificationMainView.java
+++ b/src/com/android/launcher3/notification/NotificationMainView.java
@@ -87,11 +87,11 @@
CharSequence title = mNotificationInfo.title;
CharSequence text = mNotificationInfo.text;
if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(text)) {
- mTitleView.setText(title);
- mTextView.setText(text);
+ mTitleView.setText(title.toString());
+ mTextView.setText(text.toString());
} else {
mTitleView.setMaxLines(2);
- mTitleView.setText(TextUtils.isEmpty(title) ? text : title);
+ mTitleView.setText(TextUtils.isEmpty(title) ? text.toString() : title.toString());
mTextView.setVisibility(GONE);
}
iconView.setBackground(mNotificationInfo.getIconForBackground(getContext(),
diff --git a/src/com/android/launcher3/notification/SwipeHelper.java b/src/com/android/launcher3/notification/SwipeHelper.java
index 5f03252..ebbe5fc 100644
--- a/src/com/android/launcher3/notification/SwipeHelper.java
+++ b/src/com/android/launcher3/notification/SwipeHelper.java
@@ -24,23 +24,20 @@
import android.content.Context;
import android.graphics.RectF;
import android.os.Handler;
+import android.util.ArrayMap;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-
import com.android.launcher3.R;
-import java.util.HashMap;
-
/**
* This class was copied from com.android.systemui.
*/
public class SwipeHelper {
- static final String TAG = "SwipeHelper";
- private static final boolean DEBUG = false;
+ private static final String TAG = "SwipeHelper";
private static final boolean DEBUG_INVALIDATE = false;
private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
private static final boolean CONSTRAIN_SWIPE = true;
@@ -50,10 +47,10 @@
public static final int X = 0;
public static final int Y = 1;
- private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
- private int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
- private int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
- private int MAX_DISMISS_VELOCITY = 4000; // dp/sec
+ private static final float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
+ private static final int DEFAULT_ESCAPE_ANIMATION_DURATION = 200; // ms
+ private static final int MAX_ESCAPE_ANIMATION_DURATION = 400; // ms
+ private static final int MAX_DISMISS_VELOCITY = 4000; // dp/sec
private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 150; // ms
static final float SWIPE_PROGRESS_FADE_END = 0.5f; // fraction of thumbnail width
@@ -61,12 +58,12 @@
private float mMinSwipeProgress = 0f;
private float mMaxSwipeProgress = 1f;
- private FlingAnimationUtils mFlingAnimationUtils;
+ private final FlingAnimationUtils mFlingAnimationUtils;
private float mPagingTouchSlop;
- private Callback mCallback;
- private Handler mHandler;
- private int mSwipeDirection;
- private VelocityTracker mVelocityTracker;
+ private final Callback mCallback;
+ private final Handler mHandler;
+ private final int mSwipeDirection;
+ private final VelocityTracker mVelocityTracker;
private float mInitialTouchPos;
private float mPerpendicularInitialTouchPos;
@@ -80,14 +77,14 @@
private boolean mLongPressSent;
private LongPressListener mLongPressListener;
private Runnable mWatchLongPress;
- private long mLongPressTimeout;
+ private final long mLongPressTimeout;
final private int[] mTmpPos = new int[2];
- private int mFalsingThreshold;
+ private final int mFalsingThreshold;
private boolean mTouchAboveFalsingThreshold;
private boolean mDisableHwLayers;
- private HashMap<View, Animator> mDismissPendingMap = new HashMap<>();
+ private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>();
public SwipeHelper(int swipeDirection, Callback callback, Context context) {
mCallback = callback;
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
index 0a00e24..5ade497 100644
--- a/src/com/android/launcher3/pageindicators/CaretDrawable.java
+++ b/src/com/android/launcher3/pageindicators/CaretDrawable.java
@@ -22,12 +22,11 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
import com.android.launcher3.R;
import com.android.launcher3.util.Themes;
-import android.graphics.drawable.Drawable;
-
public class CaretDrawable extends Drawable {
public static final float PROGRESS_CARET_POINTING_UP = -1f;
public static final float PROGRESS_CARET_POINTING_DOWN = 1f;
@@ -39,6 +38,7 @@
private Paint mCaretPaint = new Paint();
private Path mPath = new Path();
private final int mCaretSizePx;
+ private final boolean mUseShadow;
public CaretDrawable(Context context) {
final Resources res = context.getResources();
@@ -46,12 +46,12 @@
final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width);
final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread);
- mCaretPaint.setColor(res.getColor(R.color.workspace_icon_text_color));
+ mCaretPaint.setColor(Themes.getAttrColor(context, R.attr.workspaceTextColor));
mCaretPaint.setAntiAlias(true);
mCaretPaint.setStrokeWidth(strokeWidth);
mCaretPaint.setStyle(Paint.Style.STROKE);
- mCaretPaint.setStrokeCap(Paint.Cap.SQUARE);
- mCaretPaint.setStrokeJoin(Paint.Join.MITER);
+ mCaretPaint.setStrokeCap(Paint.Cap.ROUND);
+ mCaretPaint.setStrokeJoin(Paint.Join.ROUND);
mShadowPaint.setColor(res.getColor(R.color.default_shadow_color_no_alpha));
mShadowPaint.setAlpha(Themes.getAlpha(context, android.R.attr.spotShadowAlpha));
@@ -61,6 +61,7 @@
mShadowPaint.setStrokeCap(Paint.Cap.ROUND);
mShadowPaint.setStrokeJoin(Paint.Join.ROUND);
+ mUseShadow = !Themes.getAttrBoolean(context, R.attr.isWorkspaceDarkText);
mCaretSizePx = res.getDimensionPixelSize(R.dimen.all_apps_caret_size);
}
@@ -95,8 +96,9 @@
mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress()));
mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress());
mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress()));
-
- canvas.drawPath(mPath, mShadowPaint);
+ if (mUseShadow) {
+ canvas.drawPath(mPath, mShadowPaint);
+ }
canvas.drawPath(mPath, mCaretPaint);
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
index aedf283..8bcb979 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
@@ -16,9 +16,7 @@
package com.android.launcher3.pageindicators;
import android.content.Context;
-import android.content.res.Resources;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
@@ -52,7 +50,6 @@
Launcher l = Launcher.getLauncher(context);
setOnTouchListener(l.getHapticFeedbackTouchListener());
setOnClickListener(l);
- setOnLongClickListener(l);
setOnFocusChangeListener(l.mFocusHandler);
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
index 3ceba84..6b992fc 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -137,7 +137,6 @@
mAllAppsHandle.setImageDrawable(getCaretDrawable());
mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
mAllAppsHandle.setOnClickListener(mLauncher);
- mAllAppsHandle.setOnLongClickListener(mLauncher);
mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
mLauncher.setAllAppsButton(mAllAppsHandle);
}
@@ -221,7 +220,7 @@
*/
public void updateColor(ExtractedColors extractedColors) {
int originalLineAlpha = mLinePaint.getAlpha();
- int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+ int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX);
if (color != Color.TRANSPARENT) {
color = ColorUtils.setAlphaComponent(color, 255);
if (color == Color.BLACK) {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 4488f66..c6ae0d2 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -26,13 +26,16 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.CornerPathEffect;
+import android.graphics.Outline;
import android.graphics.Paint;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.ShapeDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
+import android.support.annotation.IntDef;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -40,7 +43,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.DecelerateInterpolator;
+import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import com.android.launcher3.AbstractFloatingView;
@@ -51,14 +54,13 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.anim.PropertyResetListener;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.badge.BadgeInfo;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
@@ -71,12 +73,16 @@
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutsItemView;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.Themes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS_IF_NOTIFICATIONS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -88,6 +94,16 @@
public class PopupContainerWithArrow extends AbstractFloatingView implements DragSource,
DragController.DragListener {
+ public static final int ROUNDED_TOP_CORNERS = 1 << 0;
+ public static final int ROUNDED_BOTTOM_CORNERS = 1 << 1;
+
+ @IntDef(flag = true, value = {
+ ROUNDED_TOP_CORNERS,
+ ROUNDED_BOTTOM_CORNERS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RoundedCornerFlags {}
+
protected final Launcher mLauncher;
private final int mStartDragThreshold;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
@@ -106,6 +122,8 @@
protected Animator mOpenCloseAnimator;
private boolean mDeferContainerRemoval;
private AnimatorSet mReduceHeightAnimatorSet;
+ private final Rect mStartRect = new Rect();
+ private final Rect mEndRect = new Rect();
public PopupContainerWithArrow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
@@ -174,7 +192,7 @@
// Add dummy views first, and populate with real info when ready.
PopupPopulator.Item[] itemsToPopulate = PopupPopulator
.getItemsToPopulate(shortcutIds, notificationKeys, systemShortcuts);
- addDummyViews(itemsToPopulate, notificationKeys.size() > 1);
+ addDummyViews(itemsToPopulate, notificationKeys.size());
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -185,7 +203,7 @@
mNotificationItemView = null;
mShortcutsItemView = null;
itemsToPopulate = PopupPopulator.reverseItems(itemsToPopulate);
- addDummyViews(itemsToPopulate, notificationKeys.size() > 1);
+ addDummyViews(itemsToPopulate, notificationKeys.size());
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
@@ -221,6 +239,7 @@
mArrow.setPivotX(arrowWidth / 2);
mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
animateOpen();
mLauncher.getDragController().addDragListener(this);
@@ -234,49 +253,86 @@
systemShortcuts, systemShortcutViews));
}
- private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate,
- boolean notificationFooterHasIcons) {
+ private void addDummyViews(PopupPopulator.Item[] itemTypesToPopulate, int numNotifications) {
final Resources res = getResources();
- final int spacing = res.getDimensionPixelSize(R.dimen.popup_items_spacing);
final LayoutInflater inflater = mLauncher.getLayoutInflater();
+ int shortcutsItemRoundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
int numItems = itemTypesToPopulate.length;
for (int i = 0; i < numItems; i++) {
PopupPopulator.Item itemTypeToPopulate = itemTypesToPopulate[i];
+ PopupPopulator.Item prevItemTypeToPopulate =
+ i > 0 ? itemTypesToPopulate[i - 1] : null;
PopupPopulator.Item nextItemTypeToPopulate =
i < numItems - 1 ? itemTypesToPopulate[i + 1] : null;
final View item = inflater.inflate(itemTypeToPopulate.layoutId, this, false);
+ boolean shouldUnroundTopCorners = prevItemTypeToPopulate != null
+ && itemTypeToPopulate.isShortcut ^ prevItemTypeToPopulate.isShortcut;
+ boolean shouldUnroundBottomCorners = nextItemTypeToPopulate != null
+ && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
+
if (itemTypeToPopulate == PopupPopulator.Item.NOTIFICATION) {
mNotificationItemView = (NotificationItemView) item;
+ boolean notificationFooterHasIcons = numNotifications > 1;
int footerHeight = notificationFooterHasIcons ?
res.getDimensionPixelSize(R.dimen.notification_footer_height) : 0;
item.findViewById(R.id.footer).getLayoutParams().height = footerHeight;
+ if (notificationFooterHasIcons) {
+ mNotificationItemView.findViewById(R.id.divider).setVisibility(VISIBLE);
+ }
+
+ int roundedCorners = ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS;
+ if (shouldUnroundTopCorners) {
+ roundedCorners &= ~ROUNDED_TOP_CORNERS;
+ mNotificationItemView.findViewById(R.id.gutter_top).setVisibility(VISIBLE);
+ }
+ if (shouldUnroundBottomCorners) {
+ roundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
+ mNotificationItemView.findViewById(R.id.gutter_bottom).setVisibility(VISIBLE);
+ }
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorTertiary);
+ mNotificationItemView.setBackgroundWithCorners(backgroundColor, roundedCorners);
+
mNotificationItemView.getMainView().setAccessibilityDelegate(mAccessibilityDelegate);
} else if (itemTypeToPopulate == PopupPopulator.Item.SHORTCUT) {
item.setAccessibilityDelegate(mAccessibilityDelegate);
}
- boolean shouldAddBottomMargin = nextItemTypeToPopulate != null
- && itemTypeToPopulate.isShortcut ^ nextItemTypeToPopulate.isShortcut;
-
if (itemTypeToPopulate.isShortcut) {
if (mShortcutsItemView == null) {
mShortcutsItemView = (ShortcutsItemView) inflater.inflate(
R.layout.shortcuts_item, this, false);
addView(mShortcutsItemView);
+ if (shouldUnroundTopCorners) {
+ shortcutsItemRoundedCorners &= ~ROUNDED_TOP_CORNERS;
+ }
+ }
+ if (itemTypeToPopulate != PopupPopulator.Item.SYSTEM_SHORTCUT_ICON
+ && numNotifications > 0) {
+ int prevHeight = item.getLayoutParams().height;
+ // Condense shortcuts height when there are notifications.
+ item.getLayoutParams().height = res.getDimensionPixelSize(
+ R.dimen.bg_popup_item_condensed_height);
+ if (item instanceof DeepShortcutView) {
+ float iconScale = (float) item.getLayoutParams().height / prevHeight;
+ ((DeepShortcutView) item).getIconView().setScaleX(iconScale);
+ ((DeepShortcutView) item).getIconView().setScaleY(iconScale);
+ }
}
mShortcutsItemView.addShortcutView(item, itemTypeToPopulate);
- if (shouldAddBottomMargin) {
- ((LayoutParams) mShortcutsItemView.getLayoutParams()).bottomMargin = spacing;
+ if (shouldUnroundBottomCorners) {
+ shortcutsItemRoundedCorners &= ~ROUNDED_BOTTOM_CORNERS;
}
} else {
addView(item);
- if (shouldAddBottomMargin) {
- ((LayoutParams) item.getLayoutParams()).bottomMargin = spacing;
- }
}
}
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
+ mShortcutsItemView.setBackgroundWithCorners(backgroundColor, shortcutsItemRoundedCorners);
+ if (numNotifications > 0) {
+ mShortcutsItemView.hideShortcuts(mIsAboveIcon, MAX_SHORTCUTS_IF_NOTIFICATIONS);
+ }
}
protected PopupItemView getItemViewAt(int index) {
@@ -296,45 +352,38 @@
setVisibility(View.VISIBLE);
mIsOpen = true;
- final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
- final int itemCount = getItemCount();
+ final AnimatorSet openAnim = LauncherAnimUtils.createAnimatorSet();
+ final Resources res = getResources();
+ final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
- final long duration = getResources().getInteger(
- R.integer.config_deepShortcutOpenDuration);
- final long arrowScaleDuration = getResources().getInteger(
- R.integer.config_deepShortcutArrowOpenDuration);
- final long arrowScaleDelay = duration - arrowScaleDuration;
- final long stagger = getResources().getInteger(
- R.integer.config_deepShortcutOpenStagger);
- final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
-
- // Animate shortcuts
- DecelerateInterpolator interpolator = new DecelerateInterpolator();
- for (int i = 0; i < itemCount; i++) {
- final PopupItemView popupItemView = getItemViewAt(i);
- popupItemView.setVisibility(INVISIBLE);
- popupItemView.setAlpha(0);
-
- Animator anim = popupItemView.createOpenAnimation(mIsAboveIcon, mIsLeftAligned);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- popupItemView.setVisibility(VISIBLE);
- }
- });
- anim.setDuration(duration);
- int animationIndex = mIsAboveIcon ? itemCount - i - 1 : i;
- anim.setStartDelay(stagger * animationIndex);
- anim.setInterpolator(interpolator);
- shortcutAnims.play(anim);
-
- Animator fadeAnim = ObjectAnimator.ofFloat(popupItemView, View.ALPHA, 1);
- fadeAnim.setInterpolator(fadeInterpolator);
- // We want the shortcut to be fully opaque before the arrow starts animating.
- fadeAnim.setDuration(arrowScaleDelay);
- shortcutAnims.play(fadeAnim);
+ // Rectangular reveal.
+ int itemsTotalHeight = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
}
- shortcutAnims.addListener(new AnimatorListenerAdapter() {
+ Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+ int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+ float radius = getItemViewAt(0).getBackgroundRadius();
+ mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+ mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
+ final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider
+ (radius, radius, mStartRect, mEndRect).createRevealAnimator(this, false);
+ revealAnim.setDuration(revealDuration);
+ revealAnim.setInterpolator(revealInterpolator);
+
+ Animator fadeIn = ObjectAnimator.ofFloat(this, ALPHA, 0, 1);
+ fadeIn.setDuration(revealDuration);
+ fadeIn.setInterpolator(revealInterpolator);
+ openAnim.play(fadeIn);
+
+ // Animate the arrow.
+ mArrow.setScaleX(0);
+ mArrow.setScaleY(0);
+ Animator arrowScale = createArrowScaleAnim(1).setDuration(res.getInteger(
+ R.integer.config_popupArrowOpenDuration));
+
+ openAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mOpenCloseAnimator = null;
@@ -345,15 +394,26 @@
}
});
- // Animate the arrow
- mArrow.setScaleX(0);
- mArrow.setScaleY(0);
- Animator arrowScale = createArrowScaleAnim(1).setDuration(arrowScaleDuration);
- arrowScale.setStartDelay(arrowScaleDelay);
- shortcutAnims.play(arrowScale);
+ mOpenCloseAnimator = openAnim;
+ openAnim.playSequentially(revealAnim, arrowScale);
+ openAnim.start();
+ }
- mOpenCloseAnimator = shortcutAnims;
- shortcutAnims.start();
+ /**
+ * Returns the point at which the center of the arrow merges with the first popup item.
+ */
+ private Point computeAnimStartPoint(int itemsTotalHeight) {
+ int arrowCenterX = getResources().getDimensionPixelSize(mIsLeftAligned ^ mIsRtl ?
+ R.dimen.popup_arrow_horizontal_center_start:
+ R.dimen.popup_arrow_horizontal_center_end);
+ if (!mIsLeftAligned) {
+ arrowCenterX = getMeasuredWidth() - arrowCenterX;
+ }
+ int arrowHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom()
+ - itemsTotalHeight;
+ // The y-coordinate of edge between the arrow and the first popup item.
+ int arrowEdge = getPaddingTop() + (mIsAboveIcon ? itemsTotalHeight : arrowHeight);
+ return new Point(arrowCenterX, arrowEdge);
}
/**
@@ -413,7 +473,9 @@
x += mIsLeftAligned ? xOffset : -xOffset;
// Open above icon if there is room.
- int iconHeight = icon.getIcon().getBounds().height();
+ int iconHeight = icon.getIcon() != null
+ ? icon.getIcon().getBounds().height()
+ : icon.getHeight();
int y = mTempRect.top + icon.getPaddingTop() - height;
mIsAboveIcon = y > dragLayer.getTop() + insets.top;
if (!mIsAboveIcon) {
@@ -505,7 +567,7 @@
// since the latter expects the arrow which hasn't been added yet.
PopupItemView itemAttachedToArrow = (PopupItemView)
(getChildAt(mIsAboveIcon ? getChildCount() - 1 : 0));
- arrowPaint.setColor(itemAttachedToArrow.getArrowColor(mIsAboveIcon));
+ arrowPaint.setColor(Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary));
// The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
int radius = getResources().getDimensionPixelSize(R.dimen.popup_arrow_corner_radius);
arrowPaint.setPathEffect(new CornerPathEffect(radius));
@@ -529,6 +591,7 @@
*/
public DragOptions.PreDragCondition createPreDragCondition() {
return new DragOptions.PreDragCondition() {
+
@Override
public boolean shouldStartDrag(double distanceDragged) {
return distanceDragged > mStartDragThreshold;
@@ -536,15 +599,27 @@
@Override
public void onPreDragStart(DropTarget.DragObject dragObject) {
- mOriginalIcon.setVisibility(INVISIBLE);
+ if (mIsAboveIcon) {
+ // Hide only the icon, keep the text visible.
+ mOriginalIcon.setIconVisible(false);
+ mOriginalIcon.setVisibility(VISIBLE);
+ } else {
+ // Hide both the icon and text.
+ mOriginalIcon.setVisibility(INVISIBLE);
+ }
}
@Override
public void onPreDragEnd(DropTarget.DragObject dragObject, boolean dragStarted) {
- if (!dragStarted) {
- mOriginalIcon.setVisibility(VISIBLE);
+ mOriginalIcon.setIconVisible(true);
+ if (dragStarted) {
+ // Make sure we keep the original icon hidden while it is being dragged.
+ mOriginalIcon.setVisibility(INVISIBLE);
+ } else {
mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mOriginalIcon);
if (!mIsAboveIcon) {
+ // Show the icon but keep the text hidden.
+ mOriginalIcon.setVisibility(VISIBLE);
mOriginalIcon.setTextVisibility(false);
}
}
@@ -590,25 +665,22 @@
ItemInfo originalInfo = (ItemInfo) mOriginalIcon.getTag();
BadgeInfo badgeInfo = updatedBadges.get(PackageUserKey.fromItemInfo(originalInfo));
if (badgeInfo == null || badgeInfo.getNotificationKeys().size() == 0) {
+ // There are no more notifications, so create an animation to remove
+ // the notifications view and expand the shortcuts view (if possible).
AnimatorSet removeNotification = LauncherAnimUtils.createAnimatorSet();
+ int hiddenShortcutsHeight = 0;
+ if (mShortcutsItemView != null) {
+ hiddenShortcutsHeight = mShortcutsItemView.getHiddenShortcutsHeight();
+ int backgroundColor = Themes.getAttrColor(mLauncher, R.attr.popupColorPrimary);
+ // With notifications gone, all corners of shortcuts item should be rounded.
+ mShortcutsItemView.setBackgroundWithCorners(backgroundColor,
+ ROUNDED_TOP_CORNERS | ROUNDED_BOTTOM_CORNERS);
+ removeNotification.play(mShortcutsItemView.showAllShortcuts(mIsAboveIcon));
+ }
final int duration = getResources().getInteger(
R.integer.config_removeNotificationViewDuration);
- final int spacing = getResources().getDimensionPixelSize(R.dimen.popup_items_spacing);
- removeNotification.play(reduceNotificationViewHeight(
- mNotificationItemView.getHeightMinusFooter() + spacing, duration));
- final View removeMarginView = mIsAboveIcon ? getItemViewAt(getItemCount() - 2)
- : mNotificationItemView;
- if (removeMarginView != null) {
- ValueAnimator removeMargin = ValueAnimator.ofFloat(1, 0).setDuration(duration);
- removeMargin.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- ((MarginLayoutParams) removeMarginView.getLayoutParams()).bottomMargin
- = (int) (spacing * (float) valueAnimator.getAnimatedValue());
- }
- });
- removeNotification.play(removeMargin);
- }
+ removeNotification.play(adjustItemHeights(mNotificationItemView.getHeightMinusFooter(),
+ hiddenShortcutsHeight, duration));
Animator fade = ObjectAnimator.ofFloat(mNotificationItemView, ALPHA, 0)
.setDuration(duration);
fade.addListener(new AnimatorListenerAdapter() {
@@ -618,13 +690,12 @@
mNotificationItemView = null;
if (getItemCount() == 0) {
close(false);
- return;
}
}
});
removeNotification.play(fade);
final long arrowScaleDuration = getResources().getInteger(
- R.integer.config_deepShortcutArrowOpenDuration);
+ R.integer.config_popupArrowOpenDuration);
Animator hideArrow = createArrowScaleAnim(0).setDuration(arrowScaleDuration);
hideArrow.setStartDelay(0);
Animator showArrow = createArrowScaleAnim(1).setDuration(arrowScaleDuration);
@@ -649,28 +720,50 @@
mArrow, new PropertyListBuilder().scale(scale).build());
}
+ public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
+ return adjustItemHeights(heightToRemove, 0, duration);
+ }
+
/**
* Animates the height of the notification item and the translationY of other items accordingly.
*/
- public Animator reduceNotificationViewHeight(int heightToRemove, int duration) {
+ public Animator adjustItemHeights(int notificationHeightToRemove, int shortcutHeightToAdd,
+ int duration) {
if (mReduceHeightAnimatorSet != null) {
mReduceHeightAnimatorSet.cancel();
}
- final int translateYBy = mIsAboveIcon ? heightToRemove : -heightToRemove;
+ final int translateYBy = mIsAboveIcon ? notificationHeightToRemove - shortcutHeightToAdd
+ : -notificationHeightToRemove;
mReduceHeightAnimatorSet = LauncherAnimUtils.createAnimatorSet();
- mReduceHeightAnimatorSet.play(mNotificationItemView.animateHeightRemoval(heightToRemove));
+ boolean removingNotification =
+ notificationHeightToRemove == mNotificationItemView.getHeightMinusFooter();
+ boolean shouldRemoveNotificationHeightFromTop = mIsAboveIcon && removingNotification;
+ mReduceHeightAnimatorSet.play(mNotificationItemView.animateHeightRemoval(
+ notificationHeightToRemove, shouldRemoveNotificationHeightFromTop));
PropertyResetListener<View, Float> resetTranslationYListener
= new PropertyResetListener<>(TRANSLATION_Y, 0f);
+ boolean itemIsAfterShortcuts = false;
for (int i = 0; i < getItemCount(); i++) {
final PopupItemView itemView = getItemViewAt(i);
- if (!mIsAboveIcon && itemView == mNotificationItemView) {
- // The notification view is already in the right place when container is below icon.
+ if (itemIsAfterShortcuts) {
+ // Every item after the shortcuts item needs to adjust for the new height.
+ itemView.setTranslationY(itemView.getTranslationY() - shortcutHeightToAdd);
+ }
+ if (itemView == mNotificationItemView && (!mIsAboveIcon || removingNotification)) {
+ // The notification view is already in the right place.
continue;
}
ValueAnimator translateItem = ObjectAnimator.ofFloat(itemView, TRANSLATION_Y,
itemView.getTranslationY() + translateYBy).setDuration(duration);
translateItem.addListener(resetTranslationYListener);
mReduceHeightAnimatorSet.play(translateItem);
+ if (itemView == mShortcutsItemView) {
+ itemIsAfterShortcuts = true;
+ }
+ }
+ if (mIsAboveIcon) {
+ // We also need to adjust the arrow position to account for the new shortcuts height.
+ mArrow.setTranslationY(mArrow.getTranslationY() - shortcutHeightToAdd);
}
mReduceHeightAnimatorSet.addListener(new AnimatorListenerAdapter() {
@Override
@@ -680,6 +773,7 @@
// container itself did not. This means the items would jump back to their
// original translation unless we update the container's translationY here.
setTranslationY(getTranslationY() + translateYBy);
+ mArrow.setTranslationY(0);
}
mReduceHeightAnimatorSet = null;
}
@@ -754,55 +848,49 @@
if (!mIsOpen) {
return;
}
+ mEndRect.setEmpty();
if (mOpenCloseAnimator != null) {
+ Outline outline = new Outline();
+ getOutlineProvider().getOutline(this, outline);
+ outline.getRect(mEndRect);
mOpenCloseAnimator.cancel();
}
mIsOpen = false;
- final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
- final int itemCount = getItemCount();
- int numOpenShortcuts = 0;
- for (int i = 0; i < itemCount; i++) {
- if (getItemViewAt(i).isOpenOrOpening()) {
- numOpenShortcuts++;
- }
+ final AnimatorSet closeAnim = LauncherAnimUtils.createAnimatorSet();
+ final Resources res = getResources();
+ final long revealDuration = (long) res.getInteger(R.integer.config_popupOpenCloseDuration);
+ final TimeInterpolator revealInterpolator = new AccelerateDecelerateInterpolator();
+
+ // Rectangular reveal (reversed).
+ int itemsTotalHeight = 0;
+ for (int i = 0; i < getItemCount(); i++) {
+ itemsTotalHeight += getItemViewAt(i).getMeasuredHeight();
}
- final long duration = getResources().getInteger(
- R.integer.config_deepShortcutCloseDuration);
- final long arrowScaleDuration = getResources().getInteger(
- R.integer.config_deepShortcutArrowOpenDuration);
- final long stagger = getResources().getInteger(
- R.integer.config_deepShortcutCloseStagger);
- final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
-
- int firstOpenItemIndex = mIsAboveIcon ? itemCount - numOpenShortcuts : 0;
- for (int i = firstOpenItemIndex; i < firstOpenItemIndex + numOpenShortcuts; i++) {
- final PopupItemView view = getItemViewAt(i);
- Animator anim;
- anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
- int animationIndex = mIsAboveIcon ? i - firstOpenItemIndex
- : numOpenShortcuts - i - 1;
- anim.setStartDelay(stagger * animationIndex);
-
- Animator fadeAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 0);
- // Don't start fading until the arrow is gone.
- fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration);
- fadeAnim.setDuration(duration - arrowScaleDuration);
- fadeAnim.setInterpolator(fadeInterpolator);
- shortcutAnims.play(fadeAnim);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- view.setVisibility(INVISIBLE);
- }
- });
- shortcutAnims.play(anim);
+ Point startPoint = computeAnimStartPoint(itemsTotalHeight);
+ int top = mIsAboveIcon ? getPaddingTop() : startPoint.y;
+ float radius = getItemViewAt(0).getBackgroundRadius();
+ mStartRect.set(startPoint.x, startPoint.y, startPoint.x, startPoint.y);
+ if (mEndRect.isEmpty()) {
+ mEndRect.set(0, top, getMeasuredWidth(), top + itemsTotalHeight);
}
- Animator arrowAnim = createArrowScaleAnim(0).setDuration(arrowScaleDuration);
- arrowAnim.setStartDelay(0);
- shortcutAnims.play(arrowAnim);
+ final ValueAnimator revealAnim = new RoundedRectRevealOutlineProvider(
+ radius, radius, mStartRect, mEndRect).createRevealAnimator(this, true);
+ revealAnim.setDuration(revealDuration);
+ revealAnim.setInterpolator(revealInterpolator);
+ closeAnim.play(revealAnim);
- shortcutAnims.addListener(new AnimatorListenerAdapter() {
+ Animator fadeOut = ObjectAnimator.ofFloat(this, ALPHA, 0);
+ fadeOut.setDuration(revealDuration);
+ fadeOut.setInterpolator(revealInterpolator);
+ closeAnim.play(fadeOut);
+
+ // Animate original icon's text back in.
+ Animator fadeText = mOriginalIcon.createTextAlphaAnimator(true /* fadeIn */);
+ fadeText.setDuration(revealDuration);
+ closeAnim.play(fadeText);
+
+ closeAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mOpenCloseAnimator = null;
@@ -813,8 +901,8 @@
}
}
});
- mOpenCloseAnimator = shortcutAnims;
- shortcutAnims.start();
+ mOpenCloseAnimator = closeAnim;
+ closeAnim.start();
mOriginalIcon.forceHideBadge(false);
}
@@ -828,9 +916,7 @@
}
mIsOpen = false;
mDeferContainerRemoval = false;
- boolean isInHotseat = ((ItemInfo) mOriginalIcon.getTag()).container
- == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
- mOriginalIcon.setTextVisibility(!isInHotseat);
+ mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
mOriginalIcon.forceHideBadge(false);
mLauncher.getDragController().removeDragListener(this);
mLauncher.getDragLayer().removeView(this);
diff --git a/src/com/android/launcher3/popup/PopupItemView.java b/src/com/android/launcher3/popup/PopupItemView.java
index 384f554..8ec051b 100644
--- a/src/com/android/launcher3/popup/PopupItemView.java
+++ b/src/com/android/launcher3/popup/PopupItemView.java
@@ -16,38 +16,34 @@
package com.android.launcher3.popup;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
-import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.RoundRectShape;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
-import com.android.launcher3.LogAccelerateInterpolator;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.PillRevealOutlineProvider;
+import com.android.launcher3.popup.PopupContainerWithArrow.RoundedCornerFlags;
+
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_BOTTOM_CORNERS;
+import static com.android.launcher3.popup.PopupContainerWithArrow.ROUNDED_TOP_CORNERS;
/**
- * An abstract {@link FrameLayout} that supports animating an item's content
- * (e.g. icon and text) separate from the item's background.
+ * An abstract {@link FrameLayout} that contains content for {@link PopupContainerWithArrow}.
*/
-public abstract class PopupItemView extends FrameLayout
- implements ValueAnimator.AnimatorUpdateListener {
-
- protected static final Point sTempPoint = new Point();
+public abstract class PopupItemView extends FrameLayout {
protected final Rect mPillRect;
- private float mOpenAnimationProgress;
+ protected @RoundedCornerFlags int mRoundedCorners;
protected final boolean mIsRtl;
protected View mIconView;
@@ -93,164 +89,57 @@
@Override
protected void dispatchDraw(Canvas canvas) {
+ if (mRoundedCorners == 0) {
+ super.dispatchDraw(canvas);
+ return;
+ }
+
int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
super.dispatchDraw(canvas);
+ // Clip children to this item's rounded corners.
int cornerWidth = mRoundedCornerBitmap.getWidth();
int cornerHeight = mRoundedCornerBitmap.getHeight();
- // Clip top left corner.
- mMatrix.reset();
- canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
- // Clip top right corner.
- mMatrix.setRotate(90, cornerWidth / 2, cornerHeight / 2);
- mMatrix.postTranslate(canvas.getWidth() - cornerWidth, 0);
- canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
- // Clip bottom right corner.
- mMatrix.setRotate(180, cornerWidth / 2, cornerHeight / 2);
- mMatrix.postTranslate(canvas.getWidth() - cornerWidth, canvas.getHeight() - cornerHeight);
- canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
- // Clip bottom left corner.
- mMatrix.setRotate(270, cornerWidth / 2, cornerHeight / 2);
- mMatrix.postTranslate(0, canvas.getHeight() - cornerHeight);
- canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+ int cornerCenterX = Math.round(cornerWidth / 2f);
+ int cornerCenterY = Math.round(cornerHeight / 2f);
+ if ((mRoundedCorners & ROUNDED_TOP_CORNERS) != 0) {
+ // Clip top left corner.
+ mMatrix.reset();
+ canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+ // Clip top right corner.
+ mMatrix.setRotate(90, cornerCenterX, cornerCenterY);
+ mMatrix.postTranslate(canvas.getWidth() - cornerWidth, 0);
+ canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+ }
+ if ((mRoundedCorners & ROUNDED_BOTTOM_CORNERS) != 0) {
+ // Clip bottom right corner.
+ mMatrix.setRotate(180, cornerCenterX, cornerCenterY);
+ mMatrix.postTranslate(canvas.getWidth() - cornerWidth, canvas.getHeight() - cornerHeight);
+ canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+ // Clip bottom left corner.
+ mMatrix.setRotate(270, cornerCenterX, cornerCenterY);
+ mMatrix.postTranslate(0, canvas.getHeight() - cornerHeight);
+ canvas.drawBitmap(mRoundedCornerBitmap, mMatrix, mBackgroundClipPaint);
+ }
canvas.restoreToCount(saveCount);
}
/**
- * Creates an animator to play when the shortcut container is being opened.
+ * Creates a round rect drawable (with the specified corners unrounded)
+ * and sets it as this View's background.
*/
- public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
- Point center = getIconCenter();
- int arrowCenter = getResources().getDimensionPixelSize(pivotLeft ^ mIsRtl ?
- R.dimen.popup_arrow_horizontal_center_start:
- R.dimen.popup_arrow_horizontal_center_end);
- ValueAnimator openAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
- mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft, arrowCenter)
- .createRevealAnimator(this, false);
- mOpenAnimationProgress = 0f;
- openAnimator.addUpdateListener(this);
- return openAnimator;
- }
-
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mOpenAnimationProgress = valueAnimator.getAnimatedFraction();
- }
-
- public boolean isOpenOrOpening() {
- return mOpenAnimationProgress > 0;
- }
-
- /**
- * Creates an animator to play when the shortcut container is being closed.
- */
- public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
- long duration) {
- Point center = getIconCenter();
- int arrowCenter = getResources().getDimensionPixelSize(pivotLeft ^ mIsRtl ?
- R.dimen.popup_arrow_horizontal_center_start :
- R.dimen.popup_arrow_horizontal_center_end);
- ValueAnimator closeAnimator = new ZoomRevealOutlineProvider(center.x, center.y,
- mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft, arrowCenter)
- .createRevealAnimator(this, true);
- // Scale down the duration and interpolator according to the progress
- // that the open animation was at when the close started.
- closeAnimator.setDuration((long) (duration * mOpenAnimationProgress));
- closeAnimator.setInterpolator(new CloseInterpolator(mOpenAnimationProgress));
- closeAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mOpenAnimationProgress = 0;
- }
- });
- return closeAnimator;
- }
-
- /**
- * Returns the position of the center of the icon relative to the container.
- */
- public Point getIconCenter() {
- sTempPoint.y = getMeasuredHeight() / 2;
- sTempPoint.x = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height) / 2;
- if (Utilities.isRtl(getResources())) {
- sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
- }
- return sTempPoint;
+ public void setBackgroundWithCorners(int color, @RoundedCornerFlags int roundedCorners) {
+ mRoundedCorners = roundedCorners;
+ float rTop = (roundedCorners & ROUNDED_TOP_CORNERS) == 0 ? 0 : getBackgroundRadius();
+ float rBot = (roundedCorners & ROUNDED_BOTTOM_CORNERS) == 0 ? 0 : getBackgroundRadius();
+ float[] radii = new float[] {rTop, rTop, rTop, rTop, rBot, rBot, rBot, rBot};
+ ShapeDrawable roundRectBackground = new ShapeDrawable(new RoundRectShape(radii, null, null));
+ roundRectBackground.getPaint().setColor(color);
+ setBackground(roundRectBackground);
}
protected float getBackgroundRadius() {
return getResources().getDimensionPixelSize(R.dimen.bg_round_rect_radius);
}
-
- public abstract int getArrowColor(boolean isArrowAttachedToBottom);
-
- /**
- * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
- */
- private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
-
- private final View mTranslateView;
- private final View mZoomView;
-
- private final float mFullHeight;
- private final float mTranslateYMultiplier;
-
- private final boolean mPivotLeft;
- private final float mTranslateX;
- private final float mArrowCenter;
-
- public ZoomRevealOutlineProvider(int x, int y, Rect pillRect, PopupItemView translateView,
- View zoomView, boolean isContainerAboveIcon, boolean pivotLeft, float arrowCenter) {
- super(x, y, pillRect, translateView.getBackgroundRadius());
- mTranslateView = translateView;
- mZoomView = zoomView;
- mFullHeight = pillRect.height();
-
- mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f;
-
- mPivotLeft = pivotLeft;
- mTranslateX = pivotLeft ? arrowCenter : pillRect.right - arrowCenter;
- mArrowCenter = arrowCenter;
- }
-
- @Override
- public void setProgress(float progress) {
- super.setProgress(progress);
-
- if (mZoomView != null) {
- mZoomView.setScaleX(progress);
- mZoomView.setScaleY(progress);
- }
-
- float height = mOutline.height();
- mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
-
- float offsetX = Math.min(mOutline.width(), mArrowCenter);
- float pivotX = mPivotLeft ? (mOutline.left + offsetX) : (mOutline.right - offsetX);
- mTranslateView.setTranslationX(mTranslateX - pivotX);
- }
- }
-
- /**
- * An interpolator that reverses the current open animation progress.
- */
- private static class CloseInterpolator extends LogAccelerateInterpolator {
- private float mStartProgress;
- private float mRemainingProgress;
-
- /**
- * @param openAnimationProgress The progress that the open interpolator ended at.
- */
- public CloseInterpolator(float openAnimationProgress) {
- super(100, 0);
- mStartProgress = 1f - openAnimationProgress;
- mRemainingProgress = openAnimationProgress;
- }
-
- @Override
- public float getInterpolation(float v) {
- return mStartProgress + super.getInterpolation(v) * mRemainingProgress;
- }
- }
}
diff --git a/src/com/android/launcher3/popup/PopupPopulator.java b/src/com/android/launcher3/popup/PopupPopulator.java
index c62d877..0dc1ca0 100644
--- a/src/com/android/launcher3/popup/PopupPopulator.java
+++ b/src/com/android/launcher3/popup/PopupPopulator.java
@@ -52,9 +52,9 @@
*/
public class PopupPopulator {
- public static final int MAX_ITEMS = 4;
+ public static final int MAX_SHORTCUTS = 4;
@VisibleForTesting static final int NUM_DYNAMIC = 2;
- private static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;
+ public static final int MAX_SHORTCUTS_IF_NOTIFICATIONS = 2;
public enum Item {
SHORTCUT(R.layout.deep_shortcut, true),
@@ -77,10 +77,7 @@
boolean hasNotifications = notificationKeys.size() > 0;
int numNotificationItems = hasNotifications ? 1 : 0;
int numShortcuts = shortcutIds.size();
- if (hasNotifications && numShortcuts > MAX_SHORTCUTS_IF_NOTIFICATIONS) {
- numShortcuts = MAX_SHORTCUTS_IF_NOTIFICATIONS;
- }
- int numItems = Math.min(MAX_ITEMS, numShortcuts + numNotificationItems)
+ int numItems = Math.min(MAX_SHORTCUTS, numShortcuts) + numNotificationItems
+ systemShortcuts.size();
Item[] items = new Item[numItems];
for (int i = 0; i < numItems; i++) {
@@ -126,12 +123,12 @@
};
/**
- * Filters the shortcuts so that only MAX_ITEMS or fewer shortcuts are retained.
+ * Filters the shortcuts so that only MAX_SHORTCUTS or fewer shortcuts are retained.
* We want the filter to include both static and dynamic shortcuts, so we always
* include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
*
* @param shortcutIdToRemoveFirst An id that should be filtered out first, if any.
- * @return a subset of shortcuts, in sorted order, with size <= MAX_ITEMS.
+ * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
*/
public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
List<ShortcutInfoCompat> shortcuts, @Nullable String shortcutIdToRemoveFirst) {
@@ -147,27 +144,27 @@
}
Collections.sort(shortcuts, SHORTCUT_RANK_COMPARATOR);
- if (shortcuts.size() <= MAX_ITEMS) {
+ if (shortcuts.size() <= MAX_SHORTCUTS) {
return shortcuts;
}
// The list of shortcuts is now sorted with static shortcuts followed by dynamic
- // shortcuts. We want to preserve this order, but only keep MAX_ITEMS.
- List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_ITEMS);
+ // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
+ List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
int numDynamic = 0;
int size = shortcuts.size();
for (int i = 0; i < size; i++) {
ShortcutInfoCompat shortcut = shortcuts.get(i);
int filteredSize = filteredShortcuts.size();
- if (filteredSize < MAX_ITEMS) {
- // Always add the first MAX_ITEMS to the filtered list.
+ if (filteredSize < MAX_SHORTCUTS) {
+ // Always add the first MAX_SHORTCUTS to the filtered list.
filteredShortcuts.add(shortcut);
if (shortcut.isDynamic()) {
numDynamic++;
}
continue;
}
- // At this point, we have MAX_ITEMS already, but they may all be static.
+ // At this point, we have MAX_SHORTCUTS already, but they may all be static.
// If there are dynamic shortcuts, remove static shortcuts to add them.
if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
numDynamic++;
@@ -305,14 +302,12 @@
if (view instanceof DeepShortcutView) {
// Expanded system shortcut, with both icon and text shown on white background.
final DeepShortcutView shortcutView = (DeepShortcutView) view;
- shortcutView.getIconView().setBackground(info.getIcon(context,
- android.R.attr.textColorTertiary));
+ shortcutView.getIconView().setBackground(info.getIcon(context));
shortcutView.getBubbleText().setText(info.getLabel(context));
} else if (view instanceof ImageView) {
// Only the system shortcut icon shows on a gray background header.
final ImageView shortcutIcon = (ImageView) view;
- shortcutIcon.setImageDrawable(info.getIcon(context,
- android.R.attr.textColorHint));
+ shortcutIcon.setImageDrawable(info.getIcon(context));
shortcutIcon.setContentDescription(info.getLabel(context));
}
view.setTag(info);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index f158f71..6254d2d 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -13,18 +13,20 @@
import com.android.launcher3.R;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.PackageUserKey;
-import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.WidgetsBottomSheet;
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.
*
* Example system shortcuts, defined as inner classes, include Widgets and AppInfo.
*/
-public abstract class SystemShortcut {
+public abstract class SystemShortcut extends ItemInfo {
private final int mIconResId;
private final int mLabelResId;
@@ -33,10 +35,8 @@
mLabelResId = labelResId;
}
- public Drawable getIcon(Context context, int colorAttr) {
- Drawable icon = context.getResources().getDrawable(mIconResId, context.getTheme()).mutate();
- icon.setTint(Themes.getAttrColor(context, colorAttr));
- return icon;
+ public Drawable getIcon(Context context) {
+ return context.getResources().getDrawable(mIconResId, context.getTheme());
}
public String getLabel(Context context) {
@@ -68,6 +68,8 @@
(WidgetsBottomSheet) launcher.getLayoutInflater().inflate(
R.layout.widgets_bottom_sheet, launcher.getDragLayer(), false);
widgetsBottomSheet.populateAndShow(itemInfo);
+ launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.WIDGETS_BUTTON, view);
}
};
}
@@ -87,6 +89,8 @@
Rect sourceBounds = launcher.getViewBounds(view);
Bundle opts = launcher.getActivityLaunchOptions(view);
InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null, sourceBounds, opts);
+ launcher.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
+ ControlType.APPINFO_TARGET, view);
}
};
}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
index b0482f8..b83d3c0 100644
--- a/src/com/android/launcher3/provider/ImportDataTask.java
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -16,6 +16,8 @@
package com.android.launcher3.provider;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.Context;
@@ -29,14 +31,14 @@
import android.net.Uri;
import android.os.Process;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.LongSparseArray;
import android.util.SparseBooleanArray;
-
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.DefaultLayoutParser;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
-import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
@@ -46,14 +48,11 @@
import com.android.launcher3.Workspace;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.util.LongArrayMap;
-
import java.net.URISyntaxException;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.HashSet;
/**
@@ -112,7 +111,7 @@
screenOps.add(ContentProviderOperation.newInsert(
LauncherSettings.WorkspaceScreens.CONTENT_URI).withValues(v).build());
}
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY, screenOps);
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, screenOps);
importWorkspaceItems(allScreens.get(0), screenIdMap);
GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
@@ -133,7 +132,7 @@
String profileId = Long.toString(UserManagerCompat.getInstance(mContext)
.getSerialNumberForUser(Process.myUserHandle()));
- boolean createEmptyRowOnFirstScreen = false;
+ boolean createEmptyRowOnFirstScreen;
if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
// get items on the first row of the first screen
@@ -289,7 +288,7 @@
}
if (insertOperations.size() >= BATCH_INSERT_SIZE) {
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
insertOperations);
insertOperations.clear();
}
@@ -300,7 +299,7 @@
throw new Exception("Insufficient data");
}
if (!insertOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
insertOperations);
insertOperations.clear();
}
@@ -319,15 +318,15 @@
mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
if (!insertOperations.isEmpty()) {
- mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY,
insertOperations);
}
}
}
- private static final String getPackage(Intent intent) {
+ private static String getPackage(Intent intent) {
return intent.getComponent() != null ? intent.getComponent().getPackageName()
- : intent.getPackage();
+ : intent.getPackage();
}
/**
@@ -377,11 +376,7 @@
return false;
}
- private static SharedPreferences getDevicePrefs(Context c) {
- return c.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
- }
-
- private static final int getMyHotseatLayoutId(Context context) {
+ private static int getMyHotseatLayoutId(Context context) {
return LauncherAppState.getIDP(context).numHotseatIcons <= 5
? R.xml.dw_phone_hotseat
: R.xml.dw_tablet_hotseat;
@@ -396,9 +391,9 @@
}
@Override
- protected HashMap<String, TagParser> getLayoutElementsMap() {
+ protected ArrayMap<String, TagParser> getLayoutElementsMap() {
// Only allow shortcut parsers
- HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ ArrayMap<String, TagParser> parsers = new ArrayMap<>();
parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
parsers.put(TAG_RESOLVE, new ResolveParser());
@@ -410,7 +405,7 @@
* {@link LayoutParserCallback} which adds items in empty hotseat spots.
*/
private static class HotseatParserCallback implements LayoutParserCallback {
- private final HashSet<String> mExisitingApps;
+ private final HashSet<String> mExistingApps;
private final LongArrayMap<Object> mExistingItems;
private final ArrayList<ContentProviderOperation> mOutOps;
private final int mRequiredSize;
@@ -419,7 +414,7 @@
HotseatParserCallback(
HashSet<String> existingApps, LongArrayMap<Object> existingItems,
ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
- mExisitingApps = existingApps;
+ mExistingApps = existingApps;
mExistingItems = existingItems;
mOutOps = outOps;
mRequiredSize = requiredSize;
@@ -444,11 +439,11 @@
return 0;
}
String pkg = getPackage(intent);
- if (pkg == null || mExisitingApps.contains(pkg)) {
+ if (pkg == null || mExistingApps.contains(pkg)) {
// The item does not target an app or is already in hotseat.
return 0;
}
- mExisitingApps.add(pkg);
+ mExistingApps.add(pkg);
// find next vacant spot.
long screen = 0;
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index 1758350..74373d3 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -19,15 +19,16 @@
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
-import com.android.launcher3.logging.FileLog;
import java.util.ArrayList;
+import java.util.Collection;
/**
* A set of utility methods for Launcher DB used for DB updates and migration.
@@ -44,14 +45,14 @@
* items are simply deleted.
*/
public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
// Get the existing screens
ArrayList<Long> screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME,
null, null, null, null, null, WorkspaceScreens.SCREEN_RANK));
if (screenIds.isEmpty()) {
// No update needed
+ t.commit();
return true;
}
if (screenIds.get(0) != 0) {
@@ -68,23 +69,20 @@
}
// Check if the first row is empty
- try (Cursor c = db.query(Favorites.TABLE_NAME, null,
- "container = -100 and screen = 0 and cellY = 0", null, null, null, null)) {
- if (c.getCount() == 0) {
- // First row is empty, no need to migrate.
- return true;
- }
+ if (DatabaseUtils.queryNumEntries(db, Favorites.TABLE_NAME,
+ "container = -100 and screen = 0 and cellY = 0") == 0) {
+ // First row is empty, no need to migrate.
+ t.commit();
+ return true;
}
new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db)
.migrateScreen0();
- db.setTransactionSuccessful();
+ t.commit();
return true;
} catch (Exception e) {
Log.e(TAG, "Failed to update workspace size", e);
return false;
- } finally {
- db.endTransaction();
}
}
@@ -104,19 +102,40 @@
* Parses the cursor containing workspace screens table and returns the list of screen IDs
*/
public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) {
- ArrayList<Long> screenIds = new ArrayList<Long>();
try {
- final int idIndex = sc.getColumnIndexOrThrow(WorkspaceScreens._ID);
- while (sc.moveToNext()) {
- try {
- screenIds.add(sc.getLong(idIndex));
- } catch (Exception e) {
- FileLog.d(TAG, "Invalid screen id", e);
- }
- }
+ return iterateCursor(sc,
+ sc.getColumnIndexOrThrow(WorkspaceScreens._ID),
+ new ArrayList<Long>());
} finally {
sc.close();
}
- return screenIds;
+ }
+
+ public static <T extends Collection<Long>> T iterateCursor(Cursor c, int columnIndex, T out) {
+ while (c.moveToNext()) {
+ out.add(c.getLong(columnIndex));
+ }
+ return out;
+ }
+
+ /**
+ * Utility class to simplify managing sqlite transactions
+ */
+ public static class SQLiteTransaction implements AutoCloseable {
+ private final SQLiteDatabase mDb;
+
+ public SQLiteTransaction(SQLiteDatabase db) {
+ mDb = db;
+ db.beginTransaction();
+ }
+
+ public void commit() {
+ mDb.setTransactionSuccessful();
+ }
+
+ @Override
+ public void close() {
+ mDb.endTransaction();
+ }
}
}
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
index 4addbfa..51890d1 100644
--- a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -21,7 +21,6 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Point;
-import android.util.Log;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -31,7 +30,6 @@
import com.android.launcher3.util.LongArrayMap;
import java.util.ArrayList;
-import java.util.HashMap;
/**
* An extension of {@link GridSizeMigrationTask} which migrates only one screen and
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index dc85aba..00e2644 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -27,6 +27,7 @@
import com.android.launcher3.ShortcutInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.util.LogConfig;
import java.io.InvalidObjectException;
@@ -47,16 +48,13 @@
public static boolean performRestore(DatabaseHelper helper) {
SQLiteDatabase db = helper.getWritableDatabase();
- db.beginTransaction();
- try {
+ try (SQLiteTransaction t = new SQLiteTransaction(db)) {
new RestoreDbTask().sanitizeDB(helper, db);
- db.setTransactionSuccessful();
+ t.commit();
return true;
} catch (Exception e) {
FileLog.e(TAG, "Failed to verify db", e);
return false;
- } finally {
- db.endTransaction();
}
}
diff --git a/src/com/android/launcher3/qsb/QsbBlockerView.java b/src/com/android/launcher3/qsb/QsbBlockerView.java
deleted file mode 100644
index 5379336..0000000
--- a/src/com/android/launcher3/qsb/QsbBlockerView.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.qsb;
-
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.util.AttributeSet;
-import android.view.View;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.Workspace;
-import com.android.launcher3.Workspace.OnStateChangeListener;
-import com.android.launcher3.Workspace.State;
-
-/**
- * A simple view used to show the region blocked by QSB during drag and drop.
- */
-public class QsbBlockerView extends View implements OnStateChangeListener {
-
- private static final int VISIBLE_ALPHA = 100;
-
- private final Paint mBgPaint;
-
- public QsbBlockerView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBgPaint.setColor(Color.WHITE);
- mBgPaint.setAlpha(0);
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
-
- Workspace w = Launcher.getLauncher(getContext()).getWorkspace();
- w.setOnStateChangeListener(this);
- prepareStateChange(w.getState(), null);
- }
-
- @Override
- public void prepareStateChange(State toState, AnimatorSet targetAnim) {
- int finalAlpha = getAlphaForState(toState);
- if (targetAnim == null) {
- mBgPaint.setAlpha(finalAlpha);
- invalidate();
- } else {
- ObjectAnimator anim = ObjectAnimator.ofArgb(mBgPaint, "alpha", finalAlpha);
- anim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- invalidate();
- }
- });
- targetAnim.play(anim);
- }
- }
-
- private static int getAlphaForState(State state) {
- switch (state) {
- case SPRING_LOADED:
- case OVERVIEW:
- case OVERVIEW_HIDDEN:
- return VISIBLE_ALPHA;
- }
- return 0;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- canvas.drawPaint(mBgPaint);
- }
-}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
index df7f695..5ce78dc 100644
--- a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -65,8 +65,10 @@
}
public static boolean supportsShortcuts(ItemInfo info) {
+ boolean isItemPromise = info instanceof com.android.launcher3.ShortcutInfo
+ && ((com.android.launcher3.ShortcutInfo) info).isPromise();
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
- && !info.isDisabled();
+ && !info.isDisabled() && !isItemPromise;
}
public boolean wasLastCallSuccess() {
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
index d4db96d..5742d1d 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutCache.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutCache.java
@@ -19,9 +19,8 @@
import android.annotation.TargetApi;
import android.os.Build;
import android.os.UserHandle;
+import android.util.ArrayMap;
import android.util.LruCache;
-
-import java.util.HashMap;
import java.util.List;
/**
@@ -31,18 +30,15 @@
*/
@TargetApi(Build.VERSION_CODES.N)
public class ShortcutCache {
- private static final String TAG = "ShortcutCache";
- private static final boolean LOGD = false;
-
private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
- private LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
+ private final LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
// We always keep pinned shortcuts in the cache.
- private HashMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
+ private final ArrayMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
public ShortcutCache() {
mCachedShortcuts = new LruCache<>(CACHE_SIZE);
- mPinnedShortcuts = new HashMap<>();
+ mPinnedShortcuts = new ArrayMap<>();
}
/**
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
index ab8de6b..e9d2b50 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -23,10 +23,10 @@
import android.graphics.drawable.Drawable;
import android.view.View;
-import com.android.launcher3.graphics.HolographicOutlineHelper;
import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.graphics.HolographicOutlineHelper;
/**
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
index 37047bb..9c91c87 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -18,15 +18,11 @@
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.ItemInfo;
-import com.android.launcher3.compat.UserManagerCompat;
-
/**
* Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
*
diff --git a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
index 5b3b02e..f6fffe0 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutsItemView.java
@@ -18,10 +18,13 @@
import android.animation.Animator;
import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Point;
-import android.support.v4.content.ContextCompat;
+import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
@@ -33,6 +36,7 @@
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.anim.PropertyListBuilder;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.logging.UserEventDispatcher.LogContainerProvider;
@@ -53,7 +57,10 @@
public class ShortcutsItemView extends PopupItemView implements View.OnLongClickListener,
View.OnTouchListener, LogContainerProvider {
+ private static final String TAG = "ShortcutsItem";
+
private Launcher mLauncher;
+ private LinearLayout mContent;
private LinearLayout mShortcutsLayout;
private LinearLayout mSystemShortcutIcons;
private final Point mIconShift = new Point();
@@ -61,6 +68,8 @@
private final List<DeepShortcutView> mDeepShortcutViews = new ArrayList<>();
private final List<View> mSystemShortcutViews = new ArrayList<>();
+ private int mHiddenShortcutsHeight;
+
public ShortcutsItemView(Context context) {
this(context, null, 0);
}
@@ -78,7 +87,8 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- mShortcutsLayout = findViewById(R.id.deep_shortcuts);
+ mContent = findViewById(R.id.content);
+ mShortcutsLayout = findViewById(R.id.shortcuts);
}
@Override
@@ -95,8 +105,8 @@
@Override
public boolean onLongClick(View v) {
- // Return early if this is not initiated from a touch or not the correct view
- if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
+ // Return early if not the correct view
+ if (!(v.getParent() instanceof DeepShortcutView)) return false;
// Return early if global dragging is not enabled
if (!mLauncher.isDraggingEnabled()) return false;
// Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
@@ -134,8 +144,19 @@
// System shortcut icons are added to a header that is separate from the full shortcuts.
if (mSystemShortcutIcons == null) {
mSystemShortcutIcons = (LinearLayout) mLauncher.getLayoutInflater().inflate(
- R.layout.system_shortcut_icons, mShortcutsLayout, false);
- mShortcutsLayout.addView(mSystemShortcutIcons, 0);
+ R.layout.system_shortcut_icons, mContent, false);
+
+ View divider = LayoutInflater.from(getContext()).inflate(
+ R.layout.horizontal_divider, this, false);
+
+ boolean iconsAreBelowShortcuts = mShortcutsLayout.getChildCount() > 0;
+ if (iconsAreBelowShortcuts) {
+ mContent.addView(divider);
+ mContent.addView(mSystemShortcutIcons);
+ } else {
+ mContent.addView(divider, 0);
+ mContent.addView(mSystemShortcutIcons, 0);
+ }
}
mSystemShortcutIcons.addView(shortcutView, index);
} else {
@@ -166,6 +187,117 @@
}
/**
+ * Hides shortcuts until only {@param maxShortcuts} are showing. Also sets
+ * {@link #mHiddenShortcutsHeight} to be the amount of extra space that shortcuts will
+ * require when {@link #showAllShortcuts(boolean)} is called.
+ */
+ public void hideShortcuts(boolean hideFromTop, int maxShortcuts) {
+ // When shortcuts are shown, they get more space allocated to them.
+ final int oldHeight = mShortcutsLayout.getChildAt(0).getLayoutParams().height;
+ final int newHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
+ mHiddenShortcutsHeight = (newHeight - oldHeight) * mShortcutsLayout.getChildCount();
+
+ int numToHide = mShortcutsLayout.getChildCount() - maxShortcuts;
+ if (numToHide <= 0) {
+ return;
+ }
+ final int numShortcuts = mShortcutsLayout.getChildCount();
+ final int dir = hideFromTop ? 1 : -1;
+ for (int i = hideFromTop ? 0 : numShortcuts - 1; 0 <= i && i < numShortcuts; i += dir) {
+ View child = mShortcutsLayout.getChildAt(i);
+ if (child instanceof DeepShortcutView) {
+ mHiddenShortcutsHeight += child.getLayoutParams().height;
+ child.setVisibility(GONE);
+ int prev = i + dir;
+ if (!hideFromTop && 0 <= prev && prev < numShortcuts) {
+ // When hiding views from the bottom, make sure to hide the last divider.
+ mShortcutsLayout.getChildAt(prev).findViewById(R.id.divider).setVisibility(GONE);
+ }
+ numToHide--;
+ if (numToHide == 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ public int getHiddenShortcutsHeight() {
+ return mHiddenShortcutsHeight;
+ }
+
+ /**
+ * Sets all shortcuts in {@link #mShortcutsLayout} to VISIBLE, then creates an
+ * animation to reveal the newly shown shortcuts.
+ *
+ * @see #hideShortcuts(boolean, int)
+ */
+ public Animator showAllShortcuts(boolean showFromTop) {
+ // First set all the shortcuts to VISIBLE.
+ final int numShortcuts = mShortcutsLayout.getChildCount();
+ if (numShortcuts == 0) {
+ Log.w(TAG, "Tried to show all shortcuts but there were no shortcuts to show");
+ return null;
+ }
+ final int oldHeight = mShortcutsLayout.getChildAt(0).getLayoutParams().height;
+ final int newHeight = getResources().getDimensionPixelSize(R.dimen.bg_popup_item_height);
+ for (int i = 0; i < numShortcuts; i++) {
+ DeepShortcutView view = (DeepShortcutView) mShortcutsLayout.getChildAt(i);
+ view.getLayoutParams().height = newHeight;
+ view.requestLayout();
+ view.setVisibility(VISIBLE);
+ if (i < numShortcuts - 1) {
+ view.findViewById(R.id.divider).setVisibility(VISIBLE);
+ }
+ }
+
+ // Now reveal the newly shown shortcuts.
+ AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+
+ if (showFromTop) {
+ // The new shortcuts pushed the original shortcuts down, but we want to animate them
+ // to that position. So we revert the translation and animate to the new.
+ animation.play(translateYFrom(mShortcutsLayout, -mHiddenShortcutsHeight));
+ } else if (mSystemShortcutIcons != null) {
+ // When adding the shortcuts from the bottom, things are a little trickier, since
+ // that means they push the icons header down. To account for this, we do the same
+ // translation trick as above, but on the header. Since this means leaving behind
+ // a blank area where the header was, we also need to clip the background.
+ animation.play(translateYFrom(mSystemShortcutIcons, -mHiddenShortcutsHeight));
+ // mPillRect is the bounds of this view before the new shortcuts were shown.
+ Rect backgroundStartRect = new Rect(mPillRect);
+ Rect backgroundEndRect = new Rect(mPillRect);
+ backgroundEndRect.bottom += mHiddenShortcutsHeight;
+ animation.play(new RoundedRectRevealOutlineProvider(getBackgroundRadius(),
+ getBackgroundRadius(), backgroundStartRect, backgroundEndRect, mRoundedCorners)
+ .createRevealAnimator(this, false));
+ }
+ for (int i = 0; i < numShortcuts; i++) {
+ // Animate each shortcut to its new height.
+ DeepShortcutView shortcut = (DeepShortcutView) mShortcutsLayout.getChildAt(i);
+ int heightDiff = newHeight - oldHeight;
+ int heightAdjustmentIndex = showFromTop ? numShortcuts - i - 1 : i;
+ int fromDir = showFromTop ? 1 : -1;
+ animation.play(translateYFrom(shortcut, heightDiff * heightAdjustmentIndex * fromDir));
+ // Make sure the text and icon stay centered in the shortcut.
+ animation.play(translateYFrom(shortcut.getBubbleText(), heightDiff / 2 * fromDir));
+ animation.play(translateYFrom(shortcut.getIconView(), heightDiff / 2 * fromDir));
+ // Scale icons back up to full size.
+ animation.play(LauncherAnimUtils.ofPropertyValuesHolder(shortcut.getIconView(),
+ new PropertyListBuilder().scale(1f).build()));
+ }
+ return animation;
+ }
+
+ /**
+ * Animates the translationY of the view from the given offset to the view's current translation
+ * @return an Animator, which should be started by the caller.
+ */
+ private Animator translateYFrom(View v, int diff) {
+ float finalY = v.getTranslationY();
+ return ObjectAnimator.ofFloat(v, TRANSLATION_Y, finalY + diff, finalY);
+ }
+
+ /**
* Adds a {@link SystemShortcut.Widgets} item if there are widgets for the given ItemInfo.
*/
public void enableWidgetsIfExist(final BubbleTextView originalIcon) {
@@ -210,51 +342,6 @@
}
@Override
- public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
- AnimatorSet openAnimation = LauncherAnimUtils.createAnimatorSet();
- openAnimation.play(super.createOpenAnimation(isContainerAboveIcon, pivotLeft));
- for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
- if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
- continue;
- }
- DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
- View deepShortcutIcon = shortcutView.getIconView();
- deepShortcutIcon.setScaleX(0);
- deepShortcutIcon.setScaleY(0);
- openAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
- deepShortcutIcon, new PropertyListBuilder().scale(1).build()));
- }
- return openAnimation;
- }
-
- @Override
- public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
- long duration) {
- AnimatorSet closeAnimation = LauncherAnimUtils.createAnimatorSet();
- closeAnimation.play(super.createCloseAnimation(isContainerAboveIcon, pivotLeft, duration));
- for (int i = 0; i < mShortcutsLayout.getChildCount(); i++) {
- if (!(mShortcutsLayout.getChildAt(i) instanceof DeepShortcutView)) {
- continue;
- }
- DeepShortcutView shortcutView = ((DeepShortcutView) mShortcutsLayout.getChildAt(i));
- View deepShortcutIcon = shortcutView.getIconView();
- deepShortcutIcon.setScaleX(1);
- deepShortcutIcon.setScaleY(1);
- closeAnimation.play(LauncherAnimUtils.ofPropertyValuesHolder(
- deepShortcutIcon, new PropertyListBuilder().scale(0).build()));
- }
- return closeAnimation;
- }
-
- @Override
- public int getArrowColor(boolean isArrowAttachedToBottom) {
- return ContextCompat.getColor(getContext(),
- isArrowAttachedToBottom || mSystemShortcutIcons == null
- ? R.color.popup_background_color
- : R.color.popup_header_background_color);
- }
-
- @Override
public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
LauncherLogProto.Target targetParent) {
target.itemType = LauncherLogProto.ItemType.DEEPSHORTCUT;
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index aedca8d..8d43518 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -2,7 +2,6 @@
import android.content.Intent;
import android.graphics.Color;
-import android.graphics.Rect;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
@@ -11,8 +10,6 @@
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.allapps.AllAppsSearchBarController;
-import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.util.ComponentKey;
import java.io.FileDescriptor;
@@ -200,11 +197,6 @@
}
@Override
- public AllAppsSearchBarController getAllAppsSearchBarController() {
- return null;
- }
-
- @Override
public List<ComponentKey> getPredictedApps() {
// To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
return new ArrayList<>();
@@ -216,11 +208,6 @@
}
@Override
- public void setLauncherSearchCallback(Object callbacks) {
- // Do nothing
- }
-
- @Override
public void onAttachedToWindow() {
}
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
deleted file mode 100644
index 314b4c0..0000000
--- a/src/com/android/launcher3/util/CachedPackageTracker.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.pm.LauncherActivityInfo;
-import android.os.UserHandle;
-
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
-import com.android.launcher3.compat.UserManagerCompat;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Utility class to track list of installed packages. It persists the list so that apps
- * installed/uninstalled while Launcher was dead can also be handled properly.
- */
-public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat {
-
- protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
-
- protected final SharedPreferences mPrefs;
- protected final UserManagerCompat mUserManager;
- protected final LauncherAppsCompat mLauncherApps;
-
- public CachedPackageTracker(Context context, String preferenceFileName) {
- mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);
- mUserManager = UserManagerCompat.getInstance(context);
- mLauncherApps = LauncherAppsCompat.getInstance(context);
- }
-
- /**
- * Checks the list of user apps, and generates package event accordingly.
- * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
- */
- public void processUserApps(List<LauncherActivityInfo> apps, UserHandle user) {
- String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
- HashSet<String> oldPackageSet = new HashSet<>();
- final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);
-
- HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet);
- HashSet<String> newPackageSet = new HashSet<>();
- ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();
-
- for (LauncherActivityInfo info : apps) {
- String packageName = info.getComponentName().getPackageName();
- newPackageSet.add(packageName);
- packagesRemoved.remove(packageName);
-
- if (!oldPackageSet.contains(packageName)) {
- oldPackageSet.add(packageName);
- packagesAdded.add(new LauncherActivityInstallInfo(
- info, info.getFirstInstallTime()));
- }
- }
-
- if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) {
- mPrefs.edit().putStringSet(prefKey, newPackageSet).apply();
-
- if (!packagesAdded.isEmpty()) {
- Collections.sort(packagesAdded);
- onLauncherAppsAdded(packagesAdded, user, userAppsExisted);
- }
-
- if (!packagesRemoved.isEmpty()) {
- for (String pkg : packagesRemoved) {
- onLauncherPackageRemoved(pkg, user);
- }
- }
- }
- }
-
- /**
- * Reads the list of user apps which have already been processed.
- * @return false if the list didn't exist, true otherwise
- */
- private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) {
- Set<String> userApps = mPrefs.getStringSet(prefKey, null);
- if (userApps == null) {
- return false;
- } else {
- outExistingApps.addAll(userApps);
- return true;
- }
- }
-
- @Override
- public void onPackageRemoved(String packageName, UserHandle user) {
- String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
- HashSet<String> packageSet = new HashSet<>();
- if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) {
- mPrefs.edit().putStringSet(prefKey, packageSet).apply();
- }
-
- onLauncherPackageRemoved(packageName, user);
- }
-
- @Override
- public void onPackageAdded(String packageName, UserHandle user) {
- String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
- HashSet<String> packageSet = new HashSet<>();
- final boolean userAppsExisted = getUserApps(packageSet, prefKey);
- if (!packageSet.contains(packageName)) {
- List<LauncherActivityInfo> activities =
- mLauncherApps.getActivityList(packageName, user);
- if (!activities.isEmpty()) {
- LauncherActivityInfo activityInfo = activities.get(0);
-
- packageSet.add(packageName);
- mPrefs.edit().putStringSet(prefKey, packageSet).apply();
- onLauncherAppsAdded(Arrays.asList(
- new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())),
- user, userAppsExisted);
- }
- }
- }
-
- @Override
- public void onPackageChanged(String packageName, UserHandle user) { }
-
- @Override
- public void onPackagesAvailable(
- String[] packageNames, UserHandle user, boolean replacing) { }
-
- @Override
- public void onPackagesUnavailable(
- String[] packageNames, UserHandle user, boolean replacing) { }
-
- @Override
- public void onPackagesSuspended(String[] packageNames, UserHandle user) { }
-
- @Override
- public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { }
-
- /**
- * Called when new launcher apps are added.
- * @param apps list of newly added activities. Only one entry per package is sent.
- * @param user the user for this event. All activities in {@param apps} will belong to
- * the same user.
- * @param userAppsExisted false if the list was processed for the first time, like in case
- * when Launcher was newly installed or a new user was added.
- */
- protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps,
- UserHandle user, boolean userAppsExisted);
-
- /**
- * Called when apps are removed from the system.
- */
- protected abstract void onLauncherPackageRemoved(String packageName, UserHandle user);
-
- public static class LauncherActivityInstallInfo
- implements Comparable<LauncherActivityInstallInfo> {
- public final LauncherActivityInfo info;
- public final long installTime;
-
- public LauncherActivityInstallInfo(LauncherActivityInfo info, long installTime) {
- this.info = info;
- this.installTime = installTime;
- }
-
- @Override
- public int compareTo(LauncherActivityInstallInfo another) {
- return Utilities.longCompare(installTime, another.installTime);
- }
- }
-}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index afc45fe..b80e94d 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -23,7 +23,6 @@
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.config.FeatureFlags;
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
new file mode 100644
index 0000000..77c21fe
--- /dev/null
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Supports various IO utility functions
+ */
+public class IOUtils {
+
+ private static final int BUF_SIZE = 0x1000; // 4K
+
+ public static byte[] toByteArray(File file) throws IOException {
+ try (InputStream in = new FileInputStream(file)) {
+ return toByteArray(in);
+ }
+ }
+
+ public static byte[] toByteArray(InputStream in) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ copy(in, out);
+ return out.toByteArray();
+ }
+
+ public static long copy(InputStream from, OutputStream to) throws IOException {
+ byte[] buf = new byte[BUF_SIZE];
+ long total = 0;
+ int r;
+ while ((r = from.read(buf)) != -1) {
+ to.write(buf, 0, r);
+ total += r;
+ }
+ return total;
+ }
+}
diff --git a/src/com/android/launcher3/util/LauncherEdgeEffect.java b/src/com/android/launcher3/util/LauncherEdgeEffect.java
deleted file mode 100644
index 3e3b255..0000000
--- a/src/com/android/launcher3/util/LauncherEdgeEffect.java
+++ /dev/null
@@ -1,365 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.view.animation.AnimationUtils;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-/**
- * This class differs from the framework {@link android.widget.EdgeEffect}:
- * 1) It does not use PorterDuffXfermode
- * 2) The width to radius factor is smaller (0.5 instead of 0.75)
- */
-public class LauncherEdgeEffect {
-
- // Time it will take the effect to fully recede in ms
- private static final int RECEDE_TIME = 600;
-
- // Time it will take before a pulled glow begins receding in ms
- private static final int PULL_TIME = 167;
-
- // Time it will take in ms for a pulled glow to decay to partial strength before release
- private static final int PULL_DECAY_TIME = 2000;
-
- private static final float MAX_ALPHA = 0.5f;
-
- private static final float MAX_GLOW_SCALE = 2.f;
-
- private static final float PULL_GLOW_BEGIN = 0.f;
-
- // Minimum velocity that will be absorbed
- private static final int MIN_VELOCITY = 100;
- // Maximum velocity, clamps at this value
- private static final int MAX_VELOCITY = 10000;
-
- private static final float EPSILON = 0.001f;
-
- private static final double ANGLE = Math.PI / 6;
- private static final float SIN = (float) Math.sin(ANGLE);
- private static final float COS = (float) Math.cos(ANGLE);
-
- private float mGlowAlpha;
- private float mGlowScaleY;
-
- private float mGlowAlphaStart;
- private float mGlowAlphaFinish;
- private float mGlowScaleYStart;
- private float mGlowScaleYFinish;
-
- private long mStartTime;
- private float mDuration;
-
- private final Interpolator mInterpolator;
-
- private static final int STATE_IDLE = 0;
- private static final int STATE_PULL = 1;
- private static final int STATE_ABSORB = 2;
- private static final int STATE_RECEDE = 3;
- private static final int STATE_PULL_DECAY = 4;
-
- private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
-
- private static final int VELOCITY_GLOW_FACTOR = 6;
-
- private int mState = STATE_IDLE;
-
- private float mPullDistance;
-
- private final Rect mBounds = new Rect();
- private final Paint mPaint = new Paint();
- private float mRadius;
- private float mBaseGlowScale;
- private float mDisplacement = 0.5f;
- private float mTargetDisplacement = 0.5f;
-
- /**
- * Construct a new EdgeEffect with a theme appropriate for the provided context.
- */
- public LauncherEdgeEffect() {
- mPaint.setAntiAlias(true);
- mPaint.setStyle(Paint.Style.FILL);
- mInterpolator = new DecelerateInterpolator();
- }
-
- /**
- * Set the size of this edge effect in pixels.
- *
- * @param width Effect width in pixels
- * @param height Effect height in pixels
- */
- public void setSize(int width, int height) {
- final float r = width * 0.5f / SIN;
- final float y = COS * r;
- final float h = r - y;
- final float or = height * 0.75f / SIN;
- final float oy = COS * or;
- final float oh = or - oy;
-
- mRadius = r;
- mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
-
- mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
- }
-
- /**
- * Reports if this EdgeEffect's animation is finished. If this method returns false
- * after a call to {@link #draw(Canvas)} the host widget should schedule another
- * drawing pass to continue the animation.
- *
- * @return true if animation is finished, false if drawing should continue on the next frame.
- */
- public boolean isFinished() {
- return mState == STATE_IDLE;
- }
-
- /**
- * Immediately finish the current animation.
- * After this call {@link #isFinished()} will return true.
- */
- public void finish() {
- mState = STATE_IDLE;
- }
-
- /**
- * A view should call this when content is pulled away from an edge by the user.
- * This will update the state of the current visual effect and its associated animation.
- * The host view should always {@link android.view.View#invalidate()} after this
- * and draw the results accordingly.
- *
- * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
- * of the pull point is known.</p>
- *
- * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
- * 1.f (full length of the view) or negative values to express change
- * back toward the edge reached to initiate the effect.
- */
- public void onPull(float deltaDistance) {
- onPull(deltaDistance, 0.5f);
- }
-
- /**
- * A view should call this when content is pulled away from an edge by the user.
- * This will update the state of the current visual effect and its associated animation.
- * The host view should always {@link android.view.View#invalidate()} after this
- * and draw the results accordingly.
- *
- * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
- * 1.f (full length of the view) or negative values to express change
- * back toward the edge reached to initiate the effect.
- * @param displacement The displacement from the starting side of the effect of the point
- * initiating the pull. In the case of touch this is the finger position.
- * Values may be from 0-1.
- */
- public void onPull(float deltaDistance, float displacement) {
- final long now = AnimationUtils.currentAnimationTimeMillis();
- mTargetDisplacement = displacement;
- if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
- return;
- }
- if (mState != STATE_PULL) {
- mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
- }
- mState = STATE_PULL;
-
- mStartTime = now;
- mDuration = PULL_TIME;
-
- mPullDistance += deltaDistance;
-
- final float absdd = Math.abs(deltaDistance);
- mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
- mGlowAlpha + (absdd * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
-
- if (mPullDistance == 0) {
- mGlowScaleY = mGlowScaleYStart = 0;
- } else {
- final float scale = (float) (Math.max(0, 1 - 1 /
- Math.sqrt(Math.abs(mPullDistance) * mBounds.height()) - 0.3d) / 0.7d);
-
- mGlowScaleY = mGlowScaleYStart = scale;
- }
-
- mGlowAlphaFinish = mGlowAlpha;
- mGlowScaleYFinish = mGlowScaleY;
- }
-
- /**
- * Call when the object is released after being pulled.
- * This will begin the "decay" phase of the effect. After calling this method
- * the host view should {@link android.view.View#invalidate()} and thereby
- * draw the results accordingly.
- */
- public void onRelease() {
- mPullDistance = 0;
-
- if (mState != STATE_PULL && mState != STATE_PULL_DECAY) {
- return;
- }
-
- mState = STATE_RECEDE;
- mGlowAlphaStart = mGlowAlpha;
- mGlowScaleYStart = mGlowScaleY;
-
- mGlowAlphaFinish = 0.f;
- mGlowScaleYFinish = 0.f;
-
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mDuration = RECEDE_TIME;
- }
-
- /**
- * Call when the effect absorbs an impact at the given velocity.
- * Used when a fling reaches the scroll boundary.
- *
- * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
- * the method <code>getCurrVelocity</code> will provide a reasonable approximation
- * to use here.</p>
- *
- * @param velocity Velocity at impact in pixels per second.
- */
- public void onAbsorb(int velocity) {
- mState = STATE_ABSORB;
- velocity = Math.min(Math.max(MIN_VELOCITY, Math.abs(velocity)), MAX_VELOCITY);
-
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mDuration = 0.15f + (velocity * 0.02f);
-
- // The glow depends more on the velocity, and therefore starts out
- // nearly invisible.
- mGlowAlphaStart = 0.3f;
- mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
-
-
- // Growth for the size of the glow should be quadratic to properly
- // respond
- // to a user's scrolling speed. The faster the scrolling speed, the more
- // intense the effect should be for both the size and the saturation.
- mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
- // Alpha should change for the glow as well as size.
- mGlowAlphaFinish = Math.max(
- mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
- mTargetDisplacement = 0.5f;
- }
-
- /**
- * Set the color of this edge effect in argb.
- *
- * @param color Color in argb
- */
- public void setColor(int color) {
- mPaint.setColor(color);
- }
-
- /**
- * Return the color of this edge effect in argb.
- * @return The color of this edge effect in argb
- */
- public int getColor() {
- return mPaint.getColor();
- }
-
- /**
- * Draw into the provided canvas. Assumes that the canvas has been rotated
- * accordingly and the size has been set. The effect will be drawn the full
- * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
- * 1.f of height.
- *
- * @param canvas Canvas to draw into
- * @return true if drawing should continue beyond this frame to continue the
- * animation
- */
- public boolean draw(Canvas canvas) {
- update();
-
- final float centerX = mBounds.centerX();
- final float centerY = mBounds.height() - mRadius;
-
- canvas.scale(1.f, Math.min(mGlowScaleY, 1.f) * mBaseGlowScale, centerX, 0);
-
- final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
- float translateX = mBounds.width() * displacement / 2;
- mPaint.setAlpha((int) (0xff * mGlowAlpha));
- canvas.drawCircle(centerX + translateX, centerY, mRadius, mPaint);
-
- boolean oneLastFrame = false;
- if (mState == STATE_RECEDE && mGlowScaleY == 0) {
- mState = STATE_IDLE;
- oneLastFrame = true;
- }
-
- return mState != STATE_IDLE || oneLastFrame;
- }
-
- /**
- * Return the maximum height that the edge effect will be drawn at given the original
- * {@link #setSize(int, int) input size}.
- * @return The maximum height of the edge effect
- */
- public int getMaxHeight() {
- return (int) (mBounds.height() * MAX_GLOW_SCALE + 0.5f);
- }
-
- private void update() {
- final long time = AnimationUtils.currentAnimationTimeMillis();
- final float t = Math.min((time - mStartTime) / mDuration, 1.f);
-
- final float interp = mInterpolator.getInterpolation(t);
-
- mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
- mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
- mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
-
- if (t >= 1.f - EPSILON) {
- switch (mState) {
- case STATE_ABSORB:
- mState = STATE_RECEDE;
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mDuration = RECEDE_TIME;
-
- mGlowAlphaStart = mGlowAlpha;
- mGlowScaleYStart = mGlowScaleY;
-
- // After absorb, the glow should fade to nothing.
- mGlowAlphaFinish = 0.f;
- mGlowScaleYFinish = 0.f;
- break;
- case STATE_PULL:
- mState = STATE_PULL_DECAY;
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mDuration = PULL_DECAY_TIME;
-
- mGlowAlphaStart = mGlowAlpha;
- mGlowScaleYStart = mGlowScaleY;
-
- // After pull, the glow should fade to nothing.
- mGlowAlphaFinish = 0.f;
- mGlowScaleYFinish = 0.f;
- break;
- case STATE_PULL_DECAY:
- mState = STATE_RECEDE;
- break;
- case STATE_RECEDE:
- mState = STATE_IDLE;
- break;
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/util/LooperExecuter.java b/src/com/android/launcher3/util/LooperExecutor.java
similarity index 94%
rename from src/com/android/launcher3/util/LooperExecuter.java
rename to src/com/android/launcher3/util/LooperExecutor.java
index 4db999b..5b7c20b 100644
--- a/src/com/android/launcher3/util/LooperExecuter.java
+++ b/src/com/android/launcher3/util/LooperExecutor.java
@@ -25,11 +25,11 @@
/**
* Extension of {@link AbstractExecutorService} which executed on a provided looper.
*/
-public class LooperExecuter extends AbstractExecutorService {
+public class LooperExecutor extends AbstractExecutorService {
private final Handler mHandler;
- public LooperExecuter(Looper looper) {
+ public LooperExecutor(Looper looper) {
mHandler = new Handler(looper);
}
diff --git a/src/com/android/launcher3/util/LooperIdleLock.java b/src/com/android/launcher3/util/LooperIdleLock.java
new file mode 100644
index 0000000..35cac14
--- /dev/null
+++ b/src/com/android/launcher3/util/LooperIdleLock.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.util;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility class to block execution until the UI looper is idle.
+ */
+public class LooperIdleLock implements MessageQueue.IdleHandler, Runnable {
+
+ private final Object mLock;
+
+ private boolean mIsLocked;
+
+ public LooperIdleLock(Object lock, Looper looper) {
+ mLock = lock;
+ mIsLocked = true;
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ looper.getQueue().addIdleHandler(this);
+ } else {
+ // Looper.myQueue() only gives the current queue. Move the execution to the UI thread
+ // so that the IdleHandler is attached to the correct message queue.
+ new LooperExecutor(looper).execute(this);
+ }
+ }
+
+ @Override
+ public void run() {
+ Looper.myQueue().addIdleHandler(this);
+ }
+
+ @Override
+ public boolean queueIdle() {
+ synchronized (mLock) {
+ mIsLocked = false;
+ mLock.notify();
+ }
+ return false;
+ }
+
+ public boolean awaitLocked(long ms) {
+ if (mIsLocked) {
+ try {
+ // Just in case mFlushingWorkerThread changes but we aren't woken up,
+ // wait no longer than 1sec at a time
+ mLock.wait(ms);
+ } catch (InterruptedException ex) {
+ // Ignore
+ }
+ }
+ return mIsLocked;
+ }
+}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index ce603c4..091dd84 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -19,23 +19,23 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.LauncherActivityInfo;
+import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
-import android.support.v4.os.BuildCompat;
-import com.android.launcher3.AppInfo;
import com.android.launcher3.FolderInfo;
-import com.android.launcher3.IconCache;
+import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherFiles;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
import com.android.launcher3.SessionCommitReceiver;
import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.model.ModelWriter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -47,11 +47,6 @@
*/
public class ManagedProfileHeuristic {
- /**
- * Maintain a set of packages installed per user.
- */
- private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
-
private static final String USER_FOLDER_ID_PREFIX = "user_folder_";
/**
@@ -59,165 +54,154 @@
*/
private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
- public static ManagedProfileHeuristic get(Context context, UserHandle user) {
- if (!Process.myUserHandle().equals(user)) {
- return new ManagedProfileHeuristic(context, user);
- }
- return null;
- }
-
- private final Context mContext;
- private final LauncherModel mModel;
- private final UserHandle mUser;
- private final IconCache mIconCache;
- private final boolean mAddIconsToHomescreen;
-
- private ManagedProfileHeuristic(Context context, UserHandle user) {
- mContext = context;
- mUser = user;
- mModel = LauncherAppState.getInstance(context).getModel();
- mIconCache = LauncherAppState.getInstance(context).getIconCache();
- mAddIconsToHomescreen =
- !BuildCompat.isAtLeastO() || SessionCommitReceiver.isEnabled(context);
- }
-
- public void processPackageRemoved(String[] packages) {
- Preconditions.assertWorkerThread();
- ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
- for (String pkg : packages) {
- handler.onPackageRemoved(pkg, mUser);
- }
- }
-
- public void processPackageAdd(String[] packages) {
- Preconditions.assertWorkerThread();
- ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
- for (String pkg : packages) {
- handler.onPackageAdded(pkg, mUser);
- }
- }
-
- public void processUserApps(List<LauncherActivityInfo> apps) {
- Preconditions.assertWorkerThread();
- new ManagedProfilePackageHandler().processUserApps(apps, mUser);
- }
-
- private class ManagedProfilePackageHandler extends CachedPackageTracker {
-
- private ManagedProfilePackageHandler() {
- super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY);
+ public static void onAllAppsLoaded(final Context context,
+ List<LauncherActivityInfo> apps, UserHandle user) {
+ if (Process.myUserHandle().equals(user)) {
+ return;
}
- protected void onLauncherAppsAdded(
- List<LauncherActivityInstallInfo> apps, UserHandle user, boolean userAppsExisted) {
- ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>();
- ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>();
+ UserFolderInfo ufi = new UserFolderInfo(context, user, null);
+ // We only handle folder creation once. Later icon additions are handled using package
+ // or session events.
+ if (ufi.folderAlreadyCreated) {
+ return;
+ }
- int count = apps.size();
- long folderCreationTime =
- mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
+ if (Utilities.isAtLeastO() && !SessionCommitReceiver.isEnabled(context)) {
+ // Just mark the folder id preference to avoid new folder creation later.
+ ufi.prefs.edit().putLong(ufi.folderIdKey, ItemInfo.NO_ID).apply();
+ return;
+ }
- boolean quietModeEnabled = UserManagerCompat.getInstance(mContext)
- .isQuietModeEnabled(user);
- for (int i = 0; i < count; i++) {
- LauncherActivityInstallInfo info = apps.get(i);
- AppInfo appInfo = new AppInfo(info.info, user, quietModeEnabled);
- mIconCache.getTitleAndIcon(appInfo, info.info, false /* useLowResIcon */);
- ShortcutInfo si = appInfo.makeShortcut();
- ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
- }
-
- finalizeWorkFolder(user, workFolderApps, homescreenApps);
-
- // Do not add shortcuts on the homescreen for the first time. This prevents the launcher
- // getting filled with the managed user apps, when it start with a fresh DB (or after
- // a very long time).
- if (userAppsExisted && !homescreenApps.isEmpty() && mAddIconsToHomescreen) {
- mModel.addAndBindAddedWorkspaceItems(new ArrayList<ItemInfo>(homescreenApps));
+ InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_BULK_ADD);
+ for (LauncherActivityInfo app : apps) {
+ // Queue all items which should go in the work folder.
+ if (app.getFirstInstallTime() < ufi.addIconToFolderTime) {
+ InstallShortcutReceiver.queueActivityInfo(app, context);
}
}
+ // Post the queue update on next frame, so that the loader gets finished.
+ new Handler(LauncherModel.getWorkerLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ InstallShortcutReceiver.disableAndFlushInstallQueue(
+ InstallShortcutReceiver.FLAG_BULK_ADD, context);
+ }
+ });
+ }
- @Override
- protected void onLauncherPackageRemoved(String packageName, UserHandle user) {
+
+ /**
+ * Utility class to help workspace icon addition.
+ */
+ public static class UserFolderInfo {
+
+ final ArrayList<ShortcutInfo> pendingShortcuts = new ArrayList<>();
+
+ final UserHandle user;
+
+ final long userSerial;
+ // Time until which icons will be added to folder instead.
+ final long addIconToFolderTime;
+
+ final String folderIdKey;
+ final SharedPreferences prefs;
+
+ final boolean folderAlreadyCreated;
+ final FolderInfo folderInfo;
+
+ boolean folderPendingAddition;
+
+ public UserFolderInfo(Context context, UserHandle user, BgDataModel dataModel) {
+ this.user = user;
+
+ UserManagerCompat um = UserManagerCompat.getInstance(context);
+ userSerial = um.getSerialNumberForUser(user);
+ addIconToFolderTime = um.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
+
+ folderIdKey = USER_FOLDER_ID_PREFIX + userSerial;
+ prefs = prefs(context);
+
+ folderAlreadyCreated = prefs.contains(folderIdKey);
+ if (dataModel != null) {
+ if (folderAlreadyCreated) {
+ long folderId = prefs.getLong(folderIdKey, ItemInfo.NO_ID);
+ folderInfo = dataModel.folders.get(folderId);
+ } else {
+ folderInfo = new FolderInfo();
+ folderInfo.title = context.getText(R.string.work_folder_name);
+ folderInfo.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+ folderPendingAddition = true;
+ }
+ } else {
+ folderInfo = null;
+ }
}
/**
- * Adds and binds shortcuts marked to be added to the work folder.
+ * Returns the ItemInfo which should be added to the workspace. In case the the provided
+ * {@link ShortcutInfo} or a wrapped {@link FolderInfo} or null.
*/
- private void finalizeWorkFolder(
- UserHandle user, final ArrayList<ShortcutInfo> workFolderApps,
- ArrayList<ShortcutInfo> homescreenApps) {
- if (workFolderApps.isEmpty()) {
+ public ItemInfo convertToWorkspaceItem(
+ ShortcutInfo shortcut, LauncherActivityInfo activityInfo) {
+ if (activityInfo.getFirstInstallTime() >= addIconToFolderTime) {
+ return shortcut;
+ }
+
+ if (folderAlreadyCreated) {
+ if (folderInfo == null) {
+ // Work folder was deleted by user, add icon to home screen.
+ return shortcut;
+ } else {
+ // Add item to work folder instead. Nothing needs to be added
+ // on the homescreen.
+ pendingShortcuts.add(shortcut);
+ return null;
+ }
+ }
+
+ pendingShortcuts.add(shortcut);
+ folderInfo.add(shortcut, false);
+ if (folderPendingAddition) {
+ folderPendingAddition = false;
+ return folderInfo;
+ } else {
+ // WorkFolder already requested to be added. Nothing new needs to be added.
+ return null;
+ }
+ }
+
+ public void applyPendingState(ModelWriter writer) {
+ if (folderInfo == null) {
return;
}
- // Try to get a work folder.
- String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user);
- if (!mAddIconsToHomescreen) {
- if (!mPrefs.contains(folderIdKey)) {
- // Just mark the folder id preference to avoid new folder creation later.
- mPrefs.edit().putLong(folderIdKey, -1).apply();
- }
- return;
+
+ int startingRank = 0;
+ if (folderAlreadyCreated) {
+ startingRank = folderInfo.contents.size();
}
- if (mPrefs.contains(folderIdKey)) {
- long folderId = mPrefs.getLong(folderIdKey, 0);
- final FolderInfo workFolder = mModel.findFolderById(folderId);
- if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
- // Could not get a work folder. Add all the icons to homescreen.
- homescreenApps.addAll(0, workFolderApps);
- return;
- }
- saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps);
+ for (ShortcutInfo info : pendingShortcuts) {
+ info.rank = startingRank++;
+ writer.addItemToDatabase(info, folderInfo.id, 0, 0, 0);
+ }
+ if (folderAlreadyCreated) {
// FolderInfo could already be bound. We need to add shortcuts on the UI thread.
new MainThreadExecutor().execute(new Runnable() {
@Override
public void run() {
- workFolder.prepareAutoUpdate();
- for (ShortcutInfo info : workFolderApps) {
- workFolder.add(info, false);
+ folderInfo.prepareAutoUpdate();
+ for (ShortcutInfo info : pendingShortcuts) {
+ folderInfo.add(info, false);
}
}
});
} else {
- // Create a new folder.
- final FolderInfo workFolder = new FolderInfo();
- workFolder.title = mContext.getText(R.string.work_folder_name);
- workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
-
- // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
- for (ShortcutInfo info : workFolderApps) {
- workFolder.add(info, false);
- }
-
- // Add the item to home screen and DB. This also generates an item id synchronously.
- ArrayList<ItemInfo> itemList = new ArrayList<>(1);
- itemList.add(workFolder);
- mModel.addAndBindAddedWorkspaceItems(itemList);
- mPrefs.edit().putLong(folderIdKey, workFolder.id).apply();
-
- saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
+ prefs.edit().putLong(folderIdKey, folderInfo.id).apply();
}
}
-
- @Override
- public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
- UserHandle user) {
- // Do nothing
- }
- }
-
- /**
- * Add work folder shortcuts to the DB.
- */
- private void saveWorkFolderShortcuts(
- long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
- for (ItemInfo info : workFolderApps) {
- info.rank = startingRank++;
- mModel.getWriter(false).addItemToDatabase(info, workFolderId, 0, 0, 0);
- }
}
/**
@@ -225,14 +209,12 @@
*/
public static void processAllUsers(List<UserHandle> users, Context context) {
UserManagerCompat userManager = UserManagerCompat.getInstance(context);
- HashSet<String> validKeys = new HashSet<String>();
+ HashSet<String> validKeys = new HashSet<>();
for (UserHandle user : users) {
- addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys);
+ validKeys.add(USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user));
}
- SharedPreferences prefs = context.getSharedPreferences(
- LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
- Context.MODE_PRIVATE);
+ SharedPreferences prefs = prefs(context);
SharedPreferences.Editor editor = prefs.edit();
for (String key : prefs.getAll().keySet()) {
if (!validKeys.contains(key)) {
@@ -242,11 +224,6 @@
editor.apply();
}
- private static void addAllUserKeys(long userSerial, HashSet<String> keysOut) {
- keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial);
- keysOut.add(USER_FOLDER_ID_PREFIX + userSerial);
- }
-
/**
* For each user, if a work folder has not been created, mark it such that the folder will
* never get created.
@@ -260,11 +237,8 @@
if (myUser.equals(user)) {
continue;
}
-
if (prefs == null) {
- prefs = context.getSharedPreferences(
- LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
- Context.MODE_PRIVATE);
+ prefs = prefs(context);
}
String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user);
if (!prefs.contains(folderIdKey)) {
@@ -272,4 +246,9 @@
}
}
}
+
+ public static SharedPreferences prefs(Context context) {
+ return context.getSharedPreferences(
+ LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE);
+ }
}
diff --git a/src/com/android/launcher3/util/MultiStateAlphaController.java b/src/com/android/launcher3/util/MultiStateAlphaController.java
deleted file mode 100644
index 956fc9e..0000000
--- a/src/com/android/launcher3/util/MultiStateAlphaController.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-
-import java.util.Arrays;
-
-/**
- * A utility class which divides the alpha for a view across multiple states.
- */
-public class MultiStateAlphaController {
-
- private final View mTargetView;
- private final float[] mAlphas;
- private final AccessibilityManager mAm;
- private int mZeroAlphaListenerCount = 0;
-
- public MultiStateAlphaController(View view, int stateCount) {
- mTargetView = view;
- mAlphas = new float[stateCount];
- Arrays.fill(mAlphas, 1);
-
- mAm = (AccessibilityManager) view.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- }
-
- public void setAlphaAtIndex(float alpha, int index) {
- mAlphas[index] = alpha;
- updateAlpha();
- }
-
- private void updateAlpha() {
- // Only update the alpha if no zero-alpha animation is running.
- if (mZeroAlphaListenerCount > 0) {
- return;
- }
- float finalAlpha = 1;
- for (float a : mAlphas) {
- finalAlpha = finalAlpha * a;
- }
- mTargetView.setAlpha(finalAlpha);
- mTargetView.setVisibility(finalAlpha > 0 ? View.VISIBLE
- : (mAm.isEnabled() ? View.GONE : View.INVISIBLE));
- }
-
- /**
- * Returns an animator which changes the alpha at the index {@param index}
- * to {@param finalAlpha}. Alphas at other index are not affected.
- */
- public Animator animateAlphaAtIndex(float finalAlpha, final int index) {
- final ValueAnimator anim;
-
- if (Float.compare(finalAlpha, mAlphas[index]) == 0) {
- // Return a dummy animator to avoid null checks.
- anim = ValueAnimator.ofFloat(0, 0);
- } else {
- ValueAnimator animator = ValueAnimator.ofFloat(mAlphas[index], finalAlpha);
- animator.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator valueAnimator) {
- float value = (Float) valueAnimator.getAnimatedValue();
- setAlphaAtIndex(value, index);
- }
- });
- anim = animator;
- }
-
- if (Float.compare(finalAlpha, 0f) == 0) {
- // In case when any channel is animating to 0, and the current alpha is also 0, do not
- // update alpha of the target view while the animation is running.
- // We special case '0' because if any channel is set to 0, values of other
- // channels do not matter.
- anim.addListener(new ZeroAlphaAnimatorListener());
- }
- return anim;
- }
-
- private class ZeroAlphaAnimatorListener extends AnimatorListenerAdapter {
-
- private boolean mStartedAtZero = false;
-
- @Override
- public void onAnimationStart(Animator animation) {
- mStartedAtZero = Float.compare(mTargetView.getAlpha(), 0f) == 0;
- if (mStartedAtZero) {
- mZeroAlphaListenerCount++;
- mTargetView.setAlpha(0);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mStartedAtZero) {
- mZeroAlphaListenerCount--;
- updateAlpha();
- }
- }
- }
-}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index e12b2d4..13034dd 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -30,9 +30,11 @@
import android.text.TextUtils;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherAppsCompat;
+import java.net.URISyntaxException;
import java.util.List;
/**
@@ -149,4 +151,20 @@
.appendQueryParameter("id", packageName)
.build());
}
+
+ /**
+ * Creates a new market search intent.
+ */
+ public static Intent getMarketSearchIntent(Context context, String query) {
+ try {
+ Intent intent = Intent.parseUri(context.getString(R.string.market_search_intent), 0);
+ if (!TextUtils.isEmpty(query)) {
+ intent.setData(
+ intent.getData().buildUpon().appendQueryParameter("q", query).build());
+ }
+ return intent;
+ } catch (URISyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/src/com/android/launcher3/util/PillRevealOutlineProvider.java b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
deleted file mode 100644
index a57d69f..0000000
--- a/src/com/android/launcher3/util/PillRevealOutlineProvider.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.graphics.Rect;
-import android.view.ViewOutlineProvider;
-
-/**
- * A {@link ViewOutlineProvider} that animates a reveal in a "pill" shape.
- * A pill is simply a round rect, but we assume the width is greater than
- * the height and that the radius is equal to half the height.
- */
-public class PillRevealOutlineProvider extends RevealOutlineAnimation {
-
- private int mCenterX;
- private int mCenterY;
- private float mFinalRadius;
- protected Rect mPillRect;
-
- /**
- * @param x reveal center x
- * @param y reveal center y
- * @param pillRect round rect that represents the final pill shape
- */
- public PillRevealOutlineProvider(int x, int y, Rect pillRect) {
- this(x, y, pillRect, pillRect.height() / 2f);
- }
-
- public PillRevealOutlineProvider(int x, int y, Rect pillRect, float radius) {
- mCenterX = x;
- mCenterY = y;
- mPillRect = pillRect;
- mOutlineRadius = mFinalRadius = radius;
- }
-
- @Override
- public boolean shouldRemoveElevationDuringAnimation() {
- return false;
- }
-
- @Override
- public void setProgress(float progress) {
- // Assumes width is greater than height.
- int centerToEdge = Math.max(mCenterX, mPillRect.width() - mCenterX);
- int currentSize = (int) (progress * centerToEdge);
-
- // Bound the outline to the final pill shape defined by mPillRect.
- mOutline.left = Math.max(mPillRect.left, mCenterX - currentSize);
- mOutline.top = Math.max(mPillRect.top, mCenterY - currentSize);
- mOutline.right = Math.min(mPillRect.right, mCenterX + currentSize);
- mOutline.bottom = Math.min(mPillRect.bottom, mCenterY + currentSize);
- mOutlineRadius = Math.min(mFinalRadius, mOutline.height() / 2);
- }
-}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index 89353e1..7ab0d31 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -19,7 +19,7 @@
import android.os.Looper;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
/**
* A set of utility methods for thread verification.
@@ -27,25 +27,25 @@
public class Preconditions {
public static void assertNotNull(Object o) {
- if (ProviderConfig.IS_DOGFOOD_BUILD && o == null) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
throw new IllegalStateException();
}
}
public static void assertWorkerThread() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
throw new IllegalStateException();
}
}
public static void assertUIThread() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
public static void assertNonUiThread() {
- if (ProviderConfig.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+ if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
throw new IllegalStateException();
}
}
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index ef10f97..9084bfb 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -10,7 +10,7 @@
import android.util.Log;
import com.android.launcher3.Utilities;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.config.FeatureFlags;
/**
* An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
@@ -19,7 +19,7 @@
public abstract class SQLiteCacheHelper {
private static final String TAG = "SQLiteCacheHelper";
- private static final boolean NO_ICON_CACHE = ProviderConfig.IS_DOGFOOD_BUILD &&
+ private static final boolean NO_ICON_CACHE = FeatureFlags.IS_DOGFOOD_BUILD &&
Utilities.isPropertyEnabled(LogConfig.MEMORY_ONLY_ICON_CACHE);
private final String mTableName;
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
new file mode 100644
index 0000000..6b5b095
--- /dev/null
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.view.View;
+import android.view.Window;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility class to manage various window flags to control system UI.
+ */
+public class SystemUiController {
+
+ // Various UI states in increasing order of priority
+ public static final int UI_STATE_BASE_WINDOW = 0;
+ public static final int UI_STATE_ALL_APPS = 1;
+ public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
+
+ public static final int FLAG_LIGHT_NAV = 1 << 0;
+ public static final int FLAG_DARK_NAV = 1 << 1;
+ public static final int FLAG_LIGHT_STATUS = 1 << 2;
+ public static final int FLAG_DARK_STATUS = 1 << 3;
+
+ private final Window mWindow;
+ private final int[] mStates = new int[3];
+
+ public SystemUiController(Window window) {
+ mWindow = window;
+ }
+
+ public void updateUiState(int uiState, boolean isLight) {
+ updateUiState(uiState, isLight
+ ? (FLAG_LIGHT_NAV | FLAG_LIGHT_STATUS) : (FLAG_DARK_NAV | FLAG_DARK_STATUS));
+ }
+
+ public void updateUiState(int uiState, int flags) {
+ if (mStates[uiState] == flags) {
+ return;
+ }
+ mStates[uiState] = flags;
+
+ int oldFlags = mWindow.getDecorView().getSystemUiVisibility();
+ // Apply the state flags in priority order
+ int newFlags = oldFlags;
+ for (int stateFlag : mStates) {
+ if (Utilities.isAtLeastO()) {
+ if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
+ newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
+ } else if ((stateFlag & FLAG_DARK_NAV) != 0) {
+ newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ }
+ }
+
+ if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
+ newFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ } else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
+ newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+ }
+ }
+ if (newFlags != oldFlags) {
+ mWindow.getDecorView().setSystemUiVisibility(newFlags);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java
index 665c371..a7cc42b 100644
--- a/src/com/android/launcher3/util/TestingUtils.java
+++ b/src/com/android/launcher3/util/TestingUtils.java
@@ -3,7 +3,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
@@ -11,7 +10,6 @@
import com.android.launcher3.CustomAppWidget;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index d863339..89597b9 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -20,7 +20,7 @@
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.ColorMatrix;
-import android.view.ContextThemeWrapper;
+import android.graphics.drawable.Drawable;
/**
* Various utility methods associated with theming.
@@ -31,10 +31,6 @@
return getAttrColor(context, android.R.attr.colorAccent);
}
- public static int getColorPrimary(Context context, int theme) {
- return getAttrColor(new ContextThemeWrapper(context, theme), android.R.attr.colorPrimary);
- }
-
public static int getAttrColor(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
int colorAccent = ta.getColor(0, 0);
@@ -42,6 +38,20 @@
return colorAccent;
}
+ public static boolean getAttrBoolean(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ boolean value = ta.getBoolean(0, false);
+ ta.recycle();
+ return value;
+ }
+
+ public static Drawable getAttrDrawable(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
+ Drawable value = ta.getDrawable(0);
+ ta.recycle();
+ return value;
+ }
+
/**
* Returns the alpha corresponding to the theme attribute {@param attr}, in the range [0, 255].
*/
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
index 9bd2882..4cb6ca8 100644
--- a/src/com/android/launcher3/util/ViewOnDrawExecutor.java
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -16,12 +16,10 @@
package com.android.launcher3.util;
-import android.util.Log;
import android.view.View;
import android.view.View.OnAttachStateChangeListener;
import android.view.ViewTreeObserver.OnDrawListener;
-import com.android.launcher3.DeferredHandler;
import com.android.launcher3.Launcher;
import java.util.ArrayList;
@@ -34,7 +32,7 @@
OnAttachStateChangeListener {
private final ArrayList<Runnable> mTasks = new ArrayList<>();
- private final DeferredHandler mHandler;
+ private final Executor mExecutor;
private Launcher mLauncher;
private View mAttachedView;
@@ -43,8 +41,8 @@
private boolean mLoadAnimationCompleted;
private boolean mFirstDrawCompleted;
- public ViewOnDrawExecutor(DeferredHandler handler) {
- mHandler = handler;
+ public ViewOnDrawExecutor(Executor executor) {
+ mExecutor = executor;
}
public void attachTo(Launcher launcher) {
@@ -92,7 +90,7 @@
// Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
for (final Runnable r : mTasks) {
- mHandler.post(r);
+ mExecutor.execute(r);
}
markCompleted();
}
diff --git a/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
new file mode 100644
index 0000000..c0b5fe1
--- /dev/null
+++ b/src/com/android/launcher3/views/DoubleShadowBubbleTextView.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Region;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+
+/**
+ * Extension of {@link BubbleTextView} which draws two shadows on the text (ambient and key shadows}
+ */
+public class DoubleShadowBubbleTextView extends BubbleTextView {
+
+ private final ShadowInfo mShadowInfo;
+
+ public DoubleShadowBubbleTextView(Context context) {
+ this(context, null);
+ }
+
+ public DoubleShadowBubbleTextView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DoubleShadowBubbleTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mShadowInfo = new ShadowInfo(context, attrs, defStyle);
+ setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0, mShadowInfo.ambientShadowColor);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ // If text is transparent, don't draw any shadow
+ int alpha = Color.alpha(getCurrentTextColor());
+ if (alpha == 0) {
+ getPaint().clearShadowLayer();
+ super.onDraw(canvas);
+ return;
+ }
+
+ // We enhance the shadow by drawing the shadow twice
+ getPaint().setShadowLayer(mShadowInfo.ambientShadowBlur, 0, 0,
+ ColorUtils.setAlphaComponent(mShadowInfo.ambientShadowColor, alpha));
+
+ drawWithoutBadge(canvas);
+ canvas.save(Canvas.CLIP_SAVE_FLAG);
+ canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
+ getScrollX() + getWidth(),
+ getScrollY() + getHeight(), Region.Op.INTERSECT);
+
+ getPaint().setShadowLayer(mShadowInfo.keyShadowBlur, 0.0f, mShadowInfo.keyShadowOffset,
+ ColorUtils.setAlphaComponent(mShadowInfo.keyShadowColor, alpha));
+ drawWithoutBadge(canvas);
+ canvas.restore();
+
+ drawBadgeIfNecessary(canvas);
+ }
+
+ public static class ShadowInfo {
+ public final float ambientShadowBlur;
+ public final int ambientShadowColor;
+
+ public final float keyShadowBlur;
+ public final float keyShadowOffset;
+ public final int keyShadowColor;
+
+ public ShadowInfo(Context c, AttributeSet attrs, int defStyle) {
+
+ TypedArray a = c.obtainStyledAttributes(
+ attrs, R.styleable.ShadowInfo, defStyle, 0);
+
+ ambientShadowBlur = a.getDimension(R.styleable.ShadowInfo_ambientShadowBlur, 0);
+ ambientShadowColor = a.getColor(R.styleable.ShadowInfo_ambientShadowColor, 0);
+
+ keyShadowBlur = a.getDimension(R.styleable.ShadowInfo_keyShadowBlur, 0);
+ keyShadowOffset = a.getDimension(R.styleable.ShadowInfo_keyShadowOffset, 0);
+ keyShadowColor = a.getColor(R.styleable.ShadowInfo_keyShadowColor, 0);
+ a.recycle();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/views/RecyclerViewFastScroller.java b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
new file mode 100644
index 0000000..7b5bcdb
--- /dev/null
+++ b/src/com/android/launcher3/views/RecyclerViewFastScroller.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.TextView;
+
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.FastScrollThumbDrawable;
+import com.android.launcher3.util.Themes;
+
+/**
+ * The track and scrollbar that shows when you scroll the list.
+ */
+public class RecyclerViewFastScroller extends View {
+
+ private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
+
+ private static final Property<RecyclerViewFastScroller, Integer> TRACK_WIDTH =
+ new Property<RecyclerViewFastScroller, Integer>(Integer.class, "width") {
+
+ @Override
+ public Integer get(RecyclerViewFastScroller scrollBar) {
+ return scrollBar.mWidth;
+ }
+
+ @Override
+ public void set(RecyclerViewFastScroller scrollBar, Integer value) {
+ scrollBar.setTrackWidth(value);
+ }
+ };
+
+ private final static int MAX_TRACK_ALPHA = 30;
+ private final static int SCROLL_BAR_VIS_DURATION = 150;
+ private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
+
+ private final int mMinWidth;
+ private final int mMaxWidth;
+ private final int mThumbPadding;
+
+ /** Keeps the last known scrolling delta/velocity along y-axis. */
+ private int mDy = 0;
+ private final float mDeltaThreshold;
+
+ private final ViewConfiguration mConfig;
+
+ // Current width of the track
+ private int mWidth;
+ private ObjectAnimator mWidthAnimator;
+
+ private final Paint mThumbPaint;
+ protected final int mThumbHeight;
+
+ private final Paint mTrackPaint;
+
+ private float mLastTouchY;
+ private boolean mIsDragging;
+ private boolean mIsThumbDetached;
+ private final boolean mCanThumbDetach;
+ private boolean mIgnoreDragGesture;
+
+ // This is the offset from the top of the scrollbar when the user first starts touching. To
+ // prevent jumping, this offset is applied as the user scrolls.
+ protected int mTouchOffsetY;
+ protected int mThumbOffsetY;
+
+ // Fast scroller popup
+ private TextView mPopupView;
+ private boolean mPopupVisible;
+ private String mPopupSectionName;
+
+ protected BaseRecyclerView mRv;
+
+ private int mDownX;
+ private int mDownY;
+ private int mLastY;
+
+ public RecyclerViewFastScroller(Context context) {
+ this(context, null);
+ }
+
+ public RecyclerViewFastScroller(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public RecyclerViewFastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mTrackPaint = new Paint();
+ mTrackPaint.setColor(Themes.getAttrColor(context, android.R.attr.textColorPrimary));
+ mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
+
+ mThumbPaint = new Paint();
+ mThumbPaint.setAntiAlias(true);
+ mThumbPaint.setColor(Themes.getColorAccent(context));
+ mThumbPaint.setStyle(Paint.Style.FILL);
+
+ Resources res = getResources();
+ mWidth = mMinWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_min_width);
+ mMaxWidth = res.getDimensionPixelSize(R.dimen.fastscroll_track_max_width);
+
+ mThumbPadding = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_padding);
+ mThumbHeight = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
+
+ mConfig = ViewConfiguration.get(context);
+ mDeltaThreshold = res.getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
+
+ TypedArray ta =
+ context.obtainStyledAttributes(attrs, R.styleable.RecyclerViewFastScroller, defStyleAttr, 0);
+ mCanThumbDetach = ta.getBoolean(R.styleable.RecyclerViewFastScroller_canThumbDetach, false);
+ ta.recycle();
+ }
+
+ public void setRecyclerView(BaseRecyclerView rv, TextView popupView) {
+ mRv = rv;
+ mRv.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ mDy = dy;
+
+ // TODO(winsonc): If we want to animate the section heads while scrolling, we can
+ // initiate that here if the recycler view scroll state is not
+ // RecyclerView.SCROLL_STATE_IDLE.
+
+ mRv.onUpdateScrollbar(dy);
+ }
+ });
+
+ mPopupView = popupView;
+ mPopupView.setBackground(
+ new FastScrollThumbDrawable(mThumbPaint, Utilities.isRtl(getResources())));
+ }
+
+ public void reattachThumbToScroll() {
+ mIsThumbDetached = false;
+ }
+
+ public void setThumbOffsetY(int y) {
+ if (mThumbOffsetY == y) {
+ return;
+ }
+ mThumbOffsetY = y;
+ invalidate();
+ }
+
+ public int getThumbOffsetY() {
+ return mThumbOffsetY;
+ }
+
+ private void setTrackWidth(int width) {
+ if (mWidth == width) {
+ return;
+ }
+ mWidth = width;
+ invalidate();
+ }
+
+ public int getThumbHeight() {
+ return mThumbHeight;
+ }
+
+ public boolean isDraggingThumb() {
+ return mIsDragging;
+ }
+
+ public boolean isThumbDetached() {
+ return mIsThumbDetached;
+ }
+
+ /**
+ * Handles the touch event and determines whether to show the fast scroller (or updates it if
+ * it is already showing).
+ */
+ public boolean handleTouchEvent(MotionEvent ev) {
+ int x = (int) ev.getX();
+ int y = (int) ev.getY();
+ switch (ev.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // Keep track of the down positions
+ mDownX = x;
+ mDownY = mLastY = y;
+
+ if ((Math.abs(mDy) < mDeltaThreshold &&
+ mRv.getScrollState() != RecyclerView.SCROLL_STATE_IDLE)) {
+ // now the touch events are being passed to the {@link WidgetCell} until the
+ // touch sequence goes over the touch slop.
+ mRv.stopScroll();
+ }
+ if (isNearThumb(x, y)) {
+ mTouchOffsetY = mDownY - mThumbOffsetY;
+ } else if (FeatureFlags.LAUNCHER3_DIRECT_SCROLL
+ && mRv.supportsFastScrolling()
+ && isNearScrollBar(mDownX)) {
+ calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+ updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ mLastY = y;
+
+ // Check if we should start scrolling, but ignore this fastscroll gesture if we have
+ // exceeded some fixed movement
+ mIgnoreDragGesture |= Math.abs(y - mDownY) > mConfig.getScaledPagingTouchSlop();
+ if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
+ isNearThumb(mDownX, mLastY) &&
+ Math.abs(y - mDownY) > mConfig.getScaledTouchSlop()) {
+ calcTouchOffsetAndPrepToFastScroll(mDownY, mLastY);
+ }
+ if (mIsDragging) {
+ updateFastScrollSectionNameAndThumbOffset(mLastY, y);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ mRv.onFastScrollCompleted();
+ mTouchOffsetY = 0;
+ mLastTouchY = 0;
+ mIgnoreDragGesture = false;
+ if (mIsDragging) {
+ mIsDragging = false;
+ animatePopupVisibility(false);
+ showActiveScrollbar(false);
+ }
+ break;
+ }
+ return mIsDragging;
+ }
+
+ private void calcTouchOffsetAndPrepToFastScroll(int downY, int lastY) {
+ mRv.getParent().requestDisallowInterceptTouchEvent(true);
+ mIsDragging = true;
+ if (mCanThumbDetach) {
+ mIsThumbDetached = true;
+ }
+ mTouchOffsetY += (lastY - downY);
+ animatePopupVisibility(true);
+ showActiveScrollbar(true);
+ }
+
+ private void updateFastScrollSectionNameAndThumbOffset(int lastY, int y) {
+ // Update the fastscroller section name at this touch position
+ int bottom = mRv.getScrollbarTrackHeight() - mThumbHeight;
+ float boundedY = (float) Math.max(0, Math.min(bottom, y - mTouchOffsetY));
+ String sectionName = mRv.scrollToPositionAtProgress(boundedY / bottom);
+ if (!sectionName.equals(mPopupSectionName)) {
+ mPopupSectionName = sectionName;
+ mPopupView.setText(sectionName);
+ }
+ animatePopupVisibility(!sectionName.isEmpty());
+ updatePopupY(lastY);
+ mLastTouchY = boundedY;
+ setThumbOffsetY((int) mLastTouchY);
+ }
+
+ public void onDraw(Canvas canvas) {
+ if (mThumbOffsetY < 0) {
+ return;
+ }
+ int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.translate(getWidth() / 2, mRv.getPaddingTop());
+ // Draw the track
+ float halfW = mWidth / 2;
+ canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
+ mWidth, mWidth, mTrackPaint);
+
+ canvas.translate(0, mThumbOffsetY);
+ halfW += mThumbPadding;
+ float r = mWidth + mThumbPadding + mThumbPadding;
+ canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
+ canvas.restoreToCount(saveCount);
+ }
+
+
+ /**
+ * Animates the width of the scrollbar.
+ */
+ private void showActiveScrollbar(boolean isScrolling) {
+ if (mWidthAnimator != null) {
+ mWidthAnimator.cancel();
+ }
+
+ mWidthAnimator = ObjectAnimator.ofInt(this, TRACK_WIDTH,
+ isScrolling ? mMaxWidth : mMinWidth);
+ mWidthAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
+ mWidthAnimator.start();
+ }
+
+ /**
+ * Returns whether the specified point is inside the thumb bounds.
+ */
+ private boolean isNearThumb(int x, int y) {
+ int offset = y - mRv.getPaddingTop() - mThumbOffsetY;
+
+ return x >= 0 && x < getWidth() && offset >= 0 && offset <= mThumbHeight;
+ }
+
+ /**
+ * Returns true if AllAppsTransitionController can handle vertical motion
+ * beginning at this point.
+ */
+ public boolean shouldBlockIntercept(int x, int y) {
+ return isNearThumb(x, y);
+ }
+
+ /**
+ * Returns whether the specified x position is near the scroll bar.
+ */
+ public boolean isNearScrollBar(int x) {
+ return x >= (getWidth() - mMaxWidth) / 2 && x <= (getWidth() + mMaxWidth) / 2;
+ }
+
+ private void animatePopupVisibility(boolean visible) {
+ if (mPopupVisible != visible) {
+ mPopupVisible = visible;
+ mPopupView.animate().cancel();
+ mPopupView.animate().alpha(visible ? 1f : 0f).setDuration(visible ? 200 : 150).start();
+ }
+ }
+
+ private void updatePopupY(int lastTouchY) {
+ int height = mPopupView.getHeight();
+ float top = lastTouchY - (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * height)
+ + mRv.getPaddingTop();
+ top = Utilities.boundToRange(top,
+ mMaxWidth, mRv.getScrollbarTrackHeight() - mMaxWidth - height);
+ mPopupView.setTranslationY(top);
+ }
+}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 6628971..19be28d 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -116,7 +116,7 @@
} else {
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) mAddInfo;
Drawable icon = createShortcutInfo.activityInfo.getFullResIcon(app.getIconCache());
- preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, Build.VERSION_CODES.O);
+ preview = LauncherIcons.createScaledBitmapWithoutShadow(icon, launcher, 0);
mAddInfo.spanX = mAddInfo.spanY = 1;
scale = ((float) launcher.getDeviceProfile().iconSizePx) / preview.getWidth();
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index 5fe00c2..a754375 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -21,14 +21,13 @@
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Rect;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -45,9 +44,12 @@
import com.android.launcher3.anim.PropertyListBuilder;
import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.graphics.GradientView;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.PackageUserKey;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.util.TouchController;
import java.util.List;
@@ -69,23 +71,25 @@
private Interpolator mFastOutSlowInInterpolator;
private VerticalPullDetector.ScrollInterpolator mScrollInterpolator;
private Rect mInsets;
- private boolean mWasNavBarLight;
private VerticalPullDetector mVerticalPullDetector;
+ private GradientView mGradientBackground;
public WidgetsBottomSheet(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WidgetsBottomSheet(Context context, AttributeSet attrs, int defStyleAttr) {
- super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme), attrs, defStyleAttr);
+ super(context, attrs, defStyleAttr);
setWillNotDraw(false);
mLauncher = Launcher.getLauncher(context);
mOpenCloseAnimator = LauncherAnimUtils.ofPropertyValuesHolder(this);
- mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
+ mFastOutSlowInInterpolator =
+ AnimationUtils.loadInterpolator(context, android.R.interpolator.fast_out_slow_in);
mScrollInterpolator = new VerticalPullDetector.ScrollInterpolator();
mInsets = new Rect();
mVerticalPullDetector = new VerticalPullDetector(context);
mVerticalPullDetector.setListener(this);
+ mGradientBackground = (GradientView) mLauncher.findViewById(R.id.gradient_bg);
}
@Override
@@ -103,8 +107,6 @@
onWidgetsBound();
- mWasNavBarLight = (mLauncher.getWindow().getDecorView().getSystemUiVisibility()
- & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;
mLauncher.getDragLayer().addView(this);
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
setTranslationY(mTranslationYClosed);
@@ -180,7 +182,10 @@
return;
}
mIsOpen = true;
- setLightNavBar(true);
+ boolean isSheetDark = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
+ isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
if (animate) {
mOpenCloseAnimator.setValues(new PropertyListBuilder()
.translationY(mTranslationYOpen).build());
@@ -211,7 +216,8 @@
mIsOpen = false;
mVerticalPullDetector.finishedScrolling();
((ViewGroup) getParent()).removeView(WidgetsBottomSheet.this);
- setLightNavBar(mWasNavBarLight);
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
}
});
mOpenCloseAnimator.setInterpolator(mVerticalPullDetector.isIdleState()
@@ -219,15 +225,12 @@
mOpenCloseAnimator.start();
} else {
setTranslationY(mTranslationYClosed);
- setLightNavBar(mWasNavBarLight);
+ mLauncher.getSystemUiController().updateUiState(
+ SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET, 0);
mIsOpen = false;
}
}
- private void setLightNavBar(boolean lightNavBar) {
- mLauncher.activateLightSystemBars(lightNavBar, false /* statusBar */, true /* navBar */);
- }
-
@Override
protected boolean isOfType(@FloatingViewType int type) {
return (type & TYPE_WIDGETS_BOTTOM_SHEET) != 0;
@@ -270,6 +273,13 @@
}
@Override
+ public void setTranslationY(float translationY) {
+ super.setTranslationY(translationY);
+ if (mGradientBackground == null) return;
+ mGradientBackground.setProgress((mTranslationYClosed - translationY) / mTranslationYRange);
+ }
+
+ @Override
public void onDragEnd(float velocity, boolean fling) {
if ((fling && velocity > 0) || getTranslationY() > (mTranslationYRange) / 2) {
mScrollInterpolator.setVelocityAtZero(velocity);
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 4e296bf..14a9d17 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -139,8 +139,6 @@
if (LOGD) {
Log.d(TAG, String.format("onLongClick [v=%s]", v));
}
- // Return early if this is not initiated from a touch
- if (!v.isInTouchMode()) return false;
// When we are in transition, disregard long clicks
if (mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index e0a80c6..9730a82 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -21,9 +21,8 @@
import android.support.v7.widget.LinearLayoutManager;
import android.util.AttributeSet;
import android.view.View;
+
import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.model.PackageItemInfo;
-import com.android.launcher3.model.WidgetsModel;
/**
* The widgets recycler view.
@@ -60,10 +59,6 @@
setLayoutManager(new LinearLayoutManager(getContext()));
}
- public int getFastScrollerTrackColor(int defaultTrackColor) {
- return Color.WHITE;
- }
-
@Override
public void setAdapter(Adapter adapter) {
super.setAdapter(adapter);
diff --git a/src_config/com/android/launcher3/config/ProviderConfig.java b/src_config/com/android/launcher3/BuildConfig.java
similarity index 67%
rename from src_config/com/android/launcher3/config/ProviderConfig.java
rename to src_config/com/android/launcher3/BuildConfig.java
index 491fa65..4df75a1 100644
--- a/src_config/com/android/launcher3/config/ProviderConfig.java
+++ b/src_config/com/android/launcher3/BuildConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.launcher3.config;
+package com.android.launcher3;
-public class ProviderConfig {
-
- public static final String AUTHORITY = "com.android.launcher3.settings".intern();
-
- public static final boolean IS_DOGFOOD_BUILD = true;
+/**
+ * Config file used by Make. This file is automatically generated when using gradle.
+ */
+public class BuildConfig {
+ public static final String APPLICATION_ID = "com.android.launcher3";
}
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_flags/com/android/launcher3/config/FeatureFlags.java
similarity index 73%
rename from src_config/com/android/launcher3/config/FeatureFlags.java
rename to src_flags/com/android/launcher3/config/FeatureFlags.java
index 4e337a2..42a110c 100644
--- a/src_config/com/android/launcher3/config/FeatureFlags.java
+++ b/src_flags/com/android/launcher3/config/FeatureFlags.java
@@ -20,6 +20,9 @@
* Defines a set of flags used to control various launcher behaviors
*/
public final class FeatureFlags {
+
+ public static final boolean IS_DOGFOOD_BUILD = true;
+
private FeatureFlags() {}
// Custom flags go below this
@@ -28,12 +31,19 @@
public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
- public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = false;
+ public static boolean LAUNCHER3_NEW_FOLDER_ANIMATION = true;
// When enabled allows to use any point on the fast scrollbar to start dragging.
public static boolean LAUNCHER3_DIRECT_SCROLL = true;
// When enabled while all-apps open, the soft input will be set to adjust resize .
- public static boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = false;
-
+ public static boolean LAUNCHER3_UPDATE_SOFT_INPUT_MODE = true;
+ // When enabled the promise icon is visible in all apps while installation an app.
+ public static boolean LAUNCHER3_PROMISE_APPS_IN_ALL_APPS = false;
+ // When enabled uses the AllAppsRadialGradientAndScrimDrawable for all apps
+ public static boolean LAUNCHER3_GRADIENT_ALL_APPS = true;
+ // When enabled allows use of physics based motions in the Launcher.
+ public static boolean LAUNCHER3_PHYSICS = true;
+ // When enabled allows use of spring motions on the icons.
+ public static boolean LAUNCHER3_SPRING_ICONS = true;
// Feature flag to enable moving the QSB on the 0th screen of the workspace.
public static final boolean QSB_ON_FIRST_SCREEN = true;
@@ -45,10 +55,12 @@
public static final boolean LIGHT_STATUS_BAR = false;
// When enabled icons are badged with the number of notifications associated with that app.
public static final boolean BADGE_ICONS = true;
- // When enabled, icons not supporting {@link AdaptiveIconDrawable} will be wrapped in this class.
+ // 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 enabled, the qsb will be moved to the hotseat.
+ public static final boolean QSB_IN_HOTSEAT = true;
}
diff --git a/tests/res/raw/db_schema_v10.json b/tests/res/raw/db_schema_v10.json
new file mode 100644
index 0000000..a5e290e
--- /dev/null
+++ b/tests/res/raw/db_schema_v10.json
@@ -0,0 +1,4 @@
+{
+ "version" : 10,
+ "downgrade_to_9" : []
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
similarity index 98%
rename from tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
rename to tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
index 18570de..20b23b0 100644
--- a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
+++ b/tests/src/com/android/launcher3/allapps/search/DefaultAppSearchAlgorithmTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3.allapps.search;
import android.content.ComponentName;
import android.test.InstrumentationTestCase;
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index d0ba907..4c80902 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -10,9 +10,9 @@
import android.util.Pair;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.ShortcutInfo;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Provider;
@@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.verify;
@@ -50,7 +51,11 @@
}
private AddWorkspaceItemsTask newTask(ItemInfo... items) {
- return new AddWorkspaceItemsTask(Provider.of(Arrays.asList(items))) {
+ List<Pair<ItemInfo, Object>> list = new ArrayList<>();
+ for (ItemInfo item : items) {
+ list.add(Pair.create(item, null));
+ }
+ return new AddWorkspaceItemsTask(Provider.of(list)) {
@Override
protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }
@@ -178,6 +183,6 @@
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
ops.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
}
- getMockContentResolver().applyBatch(ProviderConfig.AUTHORITY, ops);
+ getMockContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
}
}
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index b9944db..3d03507 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -16,15 +16,14 @@
import com.android.launcher3.AllAppsList;
import com.android.launcher3.AppFilter;
import com.android.launcher3.AppInfo;
-import com.android.launcher3.DeferredHandler;
import com.android.launcher3.IconCache;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherModel.BaseModelUpdateTask;
+import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherModel.Callbacks;
-import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Provider;
import com.android.launcher3.util.TestLauncherProvider;
@@ -36,6 +35,7 @@
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
+import java.util.concurrent.Executor;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.atLeast;
@@ -64,7 +64,7 @@
public Callbacks callbacks;
public BaseModelUpdateTaskTestCase() {
- super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+ super(TestLauncherProvider.class, LauncherProvider.AUTHORITY);
}
@Override
@@ -75,8 +75,10 @@
appState = mock(LauncherAppState.class);
model = mock(LauncherModel.class);
modelWriter = mock(ModelWriter.class);
+
when(appState.getModel()).thenReturn(model);
when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
+ when(model.getCallback()).thenReturn(callbacks);
myUser = Process.myUserHandle();
@@ -94,22 +96,15 @@
/**
* Synchronously executes the task and returns all the UI callbacks posted.
*/
- public List<Runnable> executeTaskForTest(BaseModelUpdateTask task) throws Exception {
- LauncherModel mockModel = mock(LauncherModel.class);
- when(mockModel.getCallback()).thenReturn(callbacks);
+ public List<Runnable> executeTaskForTest(ModelUpdateTask task) throws Exception {
+ when(model.isModelLoaded()).thenReturn(true);
- Field f = BaseModelUpdateTask.class.getDeclaredField("mModel");
- f.setAccessible(true);
- f.set(task, mockModel);
+ Executor mockExecutor = mock(Executor.class);
- DeferredHandler mockHandler = mock(DeferredHandler.class);
- f = BaseModelUpdateTask.class.getDeclaredField("mUiHandler");
- f.setAccessible(true);
- f.set(task, mockHandler);
-
- task.execute(appState, bgDataModel, allAppsList);
+ task.init(appState, model, bgDataModel, allAppsList, mockExecutor);
+ task.run();
ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
- verify(mockHandler, atLeast(0)).post(captor.capture());
+ verify(mockExecutor, atLeast(0)).execute(captor.capture());
return captor.getAllValues();
}
diff --git a/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
new file mode 100644
index 0000000..1d9148b
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/DbDowngradeHelperTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.model;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherProvider.DatabaseHelper;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for {@link DbDowngradeHelper}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DbDowngradeHelperTest {
+
+ private static final String SCHEMA_FILE = "test_schema.json";
+ private static final String DB_FILE = "test.db";
+
+ private Context mContext;
+ private File mSchemaFile;
+ private File mDbFile;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mSchemaFile = mContext.getFileStreamPath(SCHEMA_FILE);
+ mDbFile = mContext.getDatabasePath(DB_FILE);
+ }
+
+ @Test
+ public void testUpdateSchemaFile() throws Exception {
+ Context myContext = InstrumentationRegistry.getContext();
+ int testResId = myContext.getResources().getIdentifier(
+ "db_schema_v10", "raw", myContext.getPackageName());
+ mSchemaFile.delete();
+ assertFalse(mSchemaFile.exists());
+
+ DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, myContext, testResId);
+ assertTrue(mSchemaFile.exists());
+ assertEquals(10, DbDowngradeHelper.parse(mSchemaFile).version);
+
+ // Schema is updated on version upgrade
+ assertTrue(mSchemaFile.setLastModified(0));
+ DbDowngradeHelper.updateSchemaFile(mSchemaFile, 11, myContext, testResId);
+ assertNotSame(0, mSchemaFile.lastModified());
+
+ // Schema is not updated when version is same
+ assertTrue(mSchemaFile.setLastModified(0));
+ DbDowngradeHelper.updateSchemaFile(mSchemaFile, 10, myContext, testResId);
+ assertEquals(0, mSchemaFile.lastModified());
+
+ // Schema is not updated on version downgrade
+ DbDowngradeHelper.updateSchemaFile(mSchemaFile, 3, myContext, testResId);
+ assertEquals(0, mSchemaFile.lastModified());
+ }
+
+ @Test
+ public void testDowngrade_success_v24() throws Exception {
+ setupTestDb();
+
+ TestOpenHelper helper = new TestOpenHelper(24);
+ assertEquals(24, helper.getReadableDatabase().getVersion());
+ helper.close();
+ }
+
+ @Test
+ public void testDowngrade_success_v22() throws Exception {
+ setupTestDb();
+
+ SQLiteOpenHelper helper = new TestOpenHelper(22);
+ assertEquals(22, helper.getWritableDatabase().getVersion());
+
+ // Check column does not exist
+ try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
+ null, null, null, null, null, null)) {
+ assertEquals(-1, c.getColumnIndex(Favorites.OPTIONS));
+
+ // Check data is present
+ assertEquals(10, c.getCount());
+ }
+ helper.close();
+
+ helper = new DatabaseHelper(mContext, null, DB_FILE) {
+ @Override
+ public void onOpen(SQLiteDatabase db) { }
+ };
+ assertEquals(LauncherProvider.SCHEMA_VERSION, helper.getWritableDatabase().getVersion());
+
+ try (Cursor c = helper.getWritableDatabase().query(Favorites.TABLE_NAME,
+ null, null, null, null, null, null)) {
+ // Check column exists
+ assertNotSame(-1, c.getColumnIndex(Favorites.OPTIONS));
+
+ // Check data is present
+ assertEquals(10, c.getCount());
+ }
+ helper.close();
+ }
+
+ @Test(expected = DowngradeFailException.class)
+ public void testDowngrade_fail_v20() throws Exception {
+ setupTestDb();
+
+ TestOpenHelper helper = new TestOpenHelper(20);
+ helper.getReadableDatabase().getVersion();
+ }
+
+ private void setupTestDb() throws Exception {
+ mSchemaFile.delete();
+ mDbFile.delete();
+
+ DbDowngradeHelper.updateSchemaFile(mSchemaFile, LauncherProvider.SCHEMA_VERSION, mContext,
+ R.raw.downgrade_schema);
+
+ DatabaseHelper dbHelper = new DatabaseHelper(mContext, null, DB_FILE) {
+ @Override
+ public void onOpen(SQLiteDatabase db) { }
+ };
+ // Insert dummy data
+ for (int i = 0; i < 10; i++) {
+ ContentValues values = new ContentValues();
+ values.put(Favorites._ID, i);
+ values.put(Favorites.TITLE, "title " + i);
+ dbHelper.getWritableDatabase().insert(Favorites.TABLE_NAME, null, values);
+ }
+ dbHelper.close();
+ }
+
+ private class TestOpenHelper extends SQLiteOpenHelper {
+
+ public TestOpenHelper(int version) {
+ super(mContext, DB_FILE, null, version);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase sqLiteDatabase) {
+ throw new RuntimeException("DB should already be created");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ throw new RuntimeException("Only downgrade supported");
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ try {
+ DbDowngradeHelper.parse(mSchemaFile).onDowngrade(db, oldVersion, newVersion);
+ } catch (Exception e) {
+ throw new DowngradeFailException(e);
+ }
+ }
+ }
+
+ private static class DowngradeFailException extends RuntimeException {
+ public DowngradeFailException(Exception e) {
+ super(e);
+ }
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index fc7fe48..fd62d36 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -1,7 +1,6 @@
package com.android.launcher3.model;
import android.content.ContentValues;
-import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Point;
@@ -10,9 +9,9 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.config.ProviderConfig;
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
import com.android.launcher3.util.TestLauncherProvider;
@@ -40,7 +39,7 @@
private InvariantDeviceProfile mIdp;
public GridSizeMigrationTaskTest() {
- super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+ super(TestLauncherProvider.class, LauncherProvider.AUTHORITY);
}
@Override
diff --git a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
index d655562..ed893c4 100644
--- a/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
+++ b/tests/src/com/android/launcher3/model/PackageInstallStateChangedTaskTest.java
@@ -21,9 +21,8 @@
}
private PackageInstallStateChangedTask newTask(String pkg, int progress) {
- PackageInstallInfo installInfo = new PackageInstallInfo(pkg);
- installInfo.progress = progress;
- installInfo.state = PackageInstallerCompat.STATUS_INSTALLING;
+ int state = PackageInstallerCompat.STATUS_INSTALLING;
+ PackageInstallInfo installInfo = new PackageInstallInfo(pkg, state, progress);
return new PackageInstallStateChangedTask(installInfo);
}
diff --git a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
index 2ad9b35..9a89b1b 100644
--- a/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
+++ b/tests/src/com/android/launcher3/popup/PopupPopulatorTest.java
@@ -28,7 +28,7 @@
import java.util.Collections;
import java.util.List;
-import static com.android.launcher3.popup.PopupPopulator.MAX_ITEMS;
+import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
import static com.android.launcher3.popup.PopupPopulator.NUM_DYNAMIC;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -44,18 +44,18 @@
public void testSortAndFilterShortcuts() {
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_ITEMS, 0);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_ITEMS);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
- MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+ MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
- MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_ITEMS - 1, 1);
- filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_ITEMS - 1);
+ MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_SHORTCUTS - 1, 1);
+ filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_SHORTCUTS - 1);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 3),
- MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+ MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
- MAX_ITEMS - NUM_DYNAMIC, NUM_DYNAMIC);
+ MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
}
@Test
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index df2b662..97f7b50 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -39,13 +39,12 @@
import com.android.launcher3.compat.PackageInstallerCompat;
import com.android.launcher3.ui.LauncherInstrumentationTestCase;
import com.android.launcher3.util.ContentWriter;
-import com.android.launcher3.util.LooperExecuter;
+import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
import java.util.Set;
import java.util.concurrent.Callable;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -340,7 +339,7 @@
* Blocks the current thread until all the jobs in the main worker thread are complete.
*/
private void waitUntilLoaderIdle() throws Exception {
- new LooperExecuter(LauncherModel.getWorkerLooper())
+ new LooperExecutor(LauncherModel.getWorkerLooper())
.submit(new Runnable() {
@Override
public void run() { }
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
index 6ca2121..1d6c18b 100644
--- a/tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -1,6 +1,7 @@
package com.android.launcher3.util;
import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.android.launcher3.LauncherProvider;
@@ -43,5 +44,8 @@
@Override
protected void onEmptyDbCreated() { }
+
+ @Override
+ protected void handleOneTimeDataUpgrade(SQLiteDatabase db) { }
}
}
\ No newline at end of file